Does SnakeYaml work within an OSGi framework? I've modified the MANIFEST & such so that it deploys correctly, but but trying to load a document into a JavaBean object structure is failing with "Class not found" exceptions.
Thanks.
Sometimes it is as simple as adding manifest headers to make a jar play nice in the OSGi sandbox. Sometimes jars/libraries do "naughty" things in the context of OSGi. A golden rule is to avoid using "Class.forName()" due to the way OSGi uses classloaders, otherwise perfectly valid in a single class loader environment. I pulled down the source to SnakeYaml and they're bean based loader makes use of Class.forName.
The good news is that there appears to be a constructor, CustomClassLoaderConstructor, that let's you use your own classloader and you use this when you make the core Yaml parser object. The key is getting the right class loader. You'll want to use the classloader of the bundle in which you're using Yaml BUT you'll need to make sure than ANY CLASS that will be created is imported into that bundle. The import will make sure all of the objects needed are in the classloader tree that OSGi creates.
See this question for created a classloader based on a bundle.
For anyone that stumbles across this, newer versions of snakeyaml are already a osgi bundle. No need to fiddle at file MANIFEST.MF.
You must just use a CustomClassLoaderConstructor like this:
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.CustomClassLoaderConstructor;
CustomClassLoaderConstructor constructor = new CustomClassLoaderConstructor(this.getClass().getClassLoader());
Config config = new Yaml(constructor).loadAs(in, Config.class);
Code tested with org.yaml.snakeyaml;bundle-version="1.25.0"
Related
I'm using Play 2.2.2, and I have an external jar that tries to load an XML resource from the same jar's root. It does so using System.class.getClassLoader().getResource("/Blabla.xml").
This fails, since apparently Play has a weird hierarchy of classloaders: ReloadableClassLoader and several parents. This hierarchy doesn't include the system class loader, nor the extensions and bootstrap classloaders.
How is this possible?
I found out that using ClassLoader.getSystemClassLoader() returns a classloader hierarchy that resembles the standard classloading hierarchy. But I would prefer not to touch the external jar mentioned above, and make System.class.getClassLoader().getResource("/Blabla.xml") work.
How can I make System.class.getClassLoader().getResource("/Blabla.xml") return the standard system classloader?
Play's classloader set-up is rather complex due to the compile-reload mechanism in development mode. Rather than trying to use the normal System.class.getClassLoader() you may want to use built-in API methods to get to a resource or to the classloader.
I am working on an Eclipse 3.7 RCP-based application with multiple modules. Module A is a bunch of libraries including mybatis-3.2.2.jar. Module B depends on module A (Require-Bundle in the manifest.mf) and has code that uses MyBatis to access data in a database. I have exported packages with mapper classes and XML in module B and imported them in module A. I am building SqlSessionFactory in the code, and it works fine if I add all Mapper classes by name, e.g.
configuration.addMapper(MyMapper.class);
however when I try to add all Mappers in the package:
configuration.addMappers(MyMapper.class.getPackage().getName());
MyBatis does not see them.
I tried changing the default classloader but this did not help.
Resources.setDefaultClassLoader(this.getClass().getClassLoader());
I suspect the problem has to do with visibility of classes in an OSGI environment. If that's the case, are there any ways to fix it in the application?
Have you tried
Resources.setDefaultClassLoader(Activator.class.getClassLoader()). I think that will use the OSGi class loader for the bundle. Hopefully that will help.
configuration.addMappers uses its own ResolverUtil that uses the Thread context class loader. (At least in mybatis3).
Best bet would be to write your own scanning code and use addMapper directly. There are my references and examples below:
http://grepcode.com/file/repo1.maven.org/maven2/org.mybatis/mybatis/3.1.1/org/apache/ibatis/session/Configuration.java?av=f#518
http://grepcode.com/file/repo1.maven.org/maven2/org.mybatis/mybatis/3.1.1/org/apache/ibatis/io/ResolverUtil.java#148
EDIT: Here are some for mybatis 3.2.2
http://grepcode.com/file/repo1.maven.org/maven2/org.mybatis/mybatis/3.2.2/org/apache/ibatis/io/ResolverUtil.java#147
http://grepcode.com/file/repo1.maven.org/maven2/org.mybatis/mybatis/3.2.2/org/apache/ibatis/binding/MapperRegistry.java#86
Same thing applies, though.
I ran into a similar problem with Spring Data JPA in a Felix OSGi environment. In that case I was able to override a factory class and add this into the offending methods:
ClassLoader pre = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(context.getClassLoader());
// add mappers here or call super method
} finally {
Thread.currentThread().setContextClassLoader(pre);
}
In this case "context" was a Spring context, but you should be able to get Module B's classloader from a BundleWiring.
Bundle bundle; //get this by symbolic name if you don't have a reference
BundleWiring bundleWiring = bundle.adapt(BundleWiring.class);
bundleWiring.getClassLoader();
Hopefully the addMappers method doesn't need to access Module A's classloader in the same call. If that's the case, there's not a ton you'll be able to do unless there's a way to extend and inject a different Configuration or MapperRegistry class.
I have a "plugin like" architecture and I want to create one instance of each class that implements a dedicated interface and put these in a cache. (To have a singleton-ish effect).
The plugins will be provided as jars and put into the app engine war file before the app is uploaded.
I have tried to use the ClassPathScanningCandidateComponentProvider as I'm using spring anyway, but this didn't work. The provider complained that it was not able to find the HttpServletResponse class file while scanning the classpath. I can't get around this, when I add the servlet jar, then I'll get of course problems, because the same jar is also provided by the GAE. If I don't, I'm getting the error above...
So I tried to add a static initialization code, but of course this doesn't work, because the class is initialized when it's instantiated for the first time. (Well I knew that but it was worth a try)
The last chance I currently see is that I create a properties file with all plugin classes when the package is created, but this requires writing of a maven plugin etc. and I'd like to avoid that.
Is there something that I am missing?
OK, I think I'll try to write my own classloader. This way, I could even put the plugins in the datastore and I can detect all the necessary classes etc.
:-)
I'd like to implement a dynamic plugin feature in a Java application. Ideally:
The application would define an interface Plugin with a method like getCapabilities().
A plugin would be a JAR pluginX.jar containing a class PluginXImpl implementing Plugin (and maybe some others).
The user would put pluginX.jar in a special directory or set a configuration parameter pointing to it. The user should not necessarily have to include pluginX.jar in their classpath.
The application would find PluginXImpl (maybe via the JAR manifest, maybe by reflection) and add it to a registry.
The client could get an instance of PluginXImpl, e.g., by invoking a method like getPluginWithCapabilities("X"). The user should not necessarily have to know the name of the plugin.
I've got a sense I should be able to do this with peaberry, but I can't make any sense of the documentation. I've invested some time in learning Guice, so my preferred answer would not be "use Spring Dynamic Modules."
Can anybody give me a simple idea of how to go about doing this using Guice/peaberry, OSGi, or just plain Java?
This is actually quite easy using plain Java means:
Since you don't want the user to configure the classpath before starting the application, I would first create a URLClassLoader with an array of URLs to the files in your plugin directory. Use File.listFiles to find all plugin jars and then File.toURI().toURL() to get a URL to each file. You should pass the system classloader (ClassLoader.getSystemClassLoader()) as a parent to your URLClassLoader.
If the plugin jars contain a configuration file in META-INF/services as described in the API documentation for java.util.ServiceLoader, you can now use ServiceLoader.load(Plugin.class, myUrlClassLoader) to obatin a service loader for your Plugin interface and call iterator() on it to get instances of all configured Plugin implementations.
You still have to provide your own wrapper around this to filter plugin capabilites, but that shouldn't be too much trouble, I suppose.
OSGI would be fine if you want to replace the plugins during runtime i.g. for bugfixes in a 24/7 environment. I played a while with OSGI but it took too much time, because it wasn't a requirement, and you need a plan b if you remove a bundle.
My humble solution then was, providing a properties files with the class names of plugin descriptor classes and let the server call them to register (including quering their capabilities).
This is obvious suboptimal but I can't wait to read the accepted answer.
Any chance you can leverage the Service Provider Interface?
The best way to implement plug-ins with Guice is with Multibindings. The linked page goes into detail on how to use multibindings to host plugins.
Apologize if you know this, but check out the forName method of Class. It is used at least in JDBC to dynamically load the DBMS-specific driver classes runtime by class name.
Then I guess it would not be difficult to enumerate all class/jar files in a directory, load each of them, and define an interface for a static method getCapabilities() (or any name you choose) that returns their capabilities/description in whatever terms and format that makes sense for your system.
Inside my host application I tried implement a simple pushService, which
shall be used to transfer an instance of a class named Vehicle to the OSGi
world, by providing a set and get method. To be able to use the service I
exported both the service interface and the Vehicle class to a jar file and
imported that file within the bundle, which should use the service.
Everytime I tried to use the Vehicle class within my host application,
which instanciates the felix framework, and the bundle, I got a linkage
error. After reading the following blog entry
(http://frankkieviet.blogspot.com/2009/03/javalanglinkageerror-loader-constraint.html)
I understood why this error occurs. But I have no clue how to solve my problem.
Is it possible to share a class between the host application and an OSGi
instance? Maybe I have to use reflection instead of import the jar file? I had a look at that library (http://code.google.com/p/transloader/) and I'm don't really sure whether this lib is able to solve my problem or not ...
BR,
Markus
At one time I was using Felix to do EXACTLY what you're asking in a custom client-server application. I've since switched to Equinox (they correctly implement framework fragments which I needed for swing LAF as osgi bundles). I THINK the following will work in Felix, I KNOW it works in Equinox.
UPDATE: I started down a very similar path with my host application. I realized early that I needed to move as much code as possible into real OSGi bundles to truly take advantage of the platform. My host application sets up client/server comms and synchronizes bundles; that's it. The few classes I used to share have been moved into bundle and I haven't look back. If you design/application can support having the majority of code in bundles I would definitely go that route. Even if some redesign is required, it's worth it.
Before initializing the OSGi runtime, set this property "org.osgi.framework.system.packages" to include you packages (no wildcards) separated by semi-colons ";". You may additionally need to include the base osgi packages, "org.osgi.framework" and the base services "org.osgi.packageadmin", "org.osgi.startlevel", "org.osgi.url".
I just dug through my version control and found a snippet when I was still using Felix (the setup is almost the same for Equinox)
Map<String, String> configMap = new HashMap<String, String();
configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES,
"your.package;other.package;org.osgi.framework");
// setup other properties
Bundle systemBundle = new Felix(configMap, null);
systemBundle.start();
// Now you can use classes from "your.package" with explicity
// declaring them as imports in bundles