How to use ServiceRegistry - java

I run into a problem, I wrote a Class(EasybImpl) which implement EasybPlugin, but when I iterate providers, I could not get the EasybImpl support that i wrote.
I thought the EasybImpl is using the System classloader and the console println the classloader as sun.misc.Launcher$AppClassLoader. what's wrong with it.
Iterator providers = ServiceRegistry.lookupProviders(EasybPlugin.class,
ClassLoader.getSystemClassLoader());

Did you declare your class as a service provider?
To declare a service provider, a services subdirectory is placed within the META-INF directory that is present in every JAR file. This directory contains a file for each service provider interface that has one or more implementation classes present in the JAR file. For example, if the JAR file contained a class named com.mycompany.mypkg.MyServiceImpl which implements the javax.someapi.SomeService interface, the JAR file would contain a file named:
META-INF/services/javax.someapi.SomeService
containing the line:
com.mycompany.mypkg.MyService
http://docs.oracle.com/javase/6/docs/api/javax/imageio/spi/ServiceRegistry.html

Four steps are required:
you build a META-INF/services subdirectory into your JAR file.
in the 'services' directory, create a file for each service you are implementing. (in your case, this would be file META-INFA/services/my.package.EasybPlugin).
in that file, declare the name of your implementation class(es).
you can then use the java.util.ServiceLoader API to discover & load registered services.
An example of the service provider file:
# providers of EasyBPlugin SPI
# (comment lines begin with pound)
my.package.StandardEasybPlugin
You can then find and load service implementations like this:
ServiceLoader<EasybPlugin> loader = ServiceLoader.load(EasybPlugin.class);
for (EasybPlugin plugin : loader) {
// ...
}
Note 1: the ServiceProvider API you were looking at is really targetted at Image IO, eg reading image file formats. ServiceLoader is for general use & should be preferred unless your purpose is not reading images.
There's a more detailed tutorial here: http://literatejava.com/extensibility/java-serviceloader-extensible-applications/
References:
https://docs.oracle.com/javase/tutorial/ext/basics/spi.html#the-serviceloader-class
https://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html
https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html targetted at sound, but clear simple examples of the service provider files.

Related

adding resource files to karaf classpath

I am working on a project using karaf 4.0.5, and osgi. We have client side code to invoke a REST API, which requires to load 3 "*.properties" files. I have been given a client jar that I'm using to invoke the server side classes and methods (containing code that I cannot update). The required property files are present in the provided client jar, but their code still doesn't locate and load them.
On debugging my pax exam I found the below possible reasons for it not loading resource files from the jar.
the code to load the files seems to attempt to load resources only from the Bundle Classloader, and
it calls the "getResource()" method instead of the "getResourceAsStream()" method.
Alternatively, I tried adding the resources to a directory on my file system, and appending the classpath with the directory's location, as in:
"-cp .;C:/Users/abcUser/Desktop/resourceFolder/;"
(windows 7, classpath entry added as a VM argument while executing pax exam using junit 4+ from eclipse) -> this doesn't work either and its still unable to locate these files.
What other options do I have so the Bundle Classloader locates these files?
Note: We already have a bunch of other *.cfg files whose contents are loaded into beans using blueprint and are registered in the containers, but that's not what I need to do here. During runtime, these files should be available to the BundleClassloader, and should be retrieved by the "getResource()" method.
Update: Following the below portion of the accepted answer, the properties files were successfully loaded by the application.
Another thing to check is whether the client code is actually using the bundle classloader when trying to load these resources. In some cases the code tries to be clever by using the Thread Context Classloader, which would need to be set appropriately before calling the client code.
The code from the client jar was exactly as guessed: the resource loading was happening using the Thread.currentThread().getContextClassLoader(). I was able to set the ContextLoader to the CustomAbstractProcessor's classloader and it now loads the properties files from that bundle's classpath!
ClassLoader previousCL = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(CustomAbstractProcessor.class.getClassLoader());
try {
//REST call to server using classes and methods from provided client jar.
}
finally {
Thread.currentThread().setContextClassLoader(previousCL);
}
I have been given a client jar that I'm using to invoke the server side classes and methods (containing code that I cannot update). The required property files are present in the provided client jar, but their code still doesn't locate and load them.
If this client jar is running as an OSGi bundle then it should be able to find resources using its own class loader (the bundle class loader) if (and only if) the resources are on the bundle's classpath.
The default classpath for an OSGi bundle is . i.e. the root of the bundle. This can be overridden using the Bundle-ClassPath manifest header and used to one or more locations in the bundle.
One possibility is that the client bundle has a different classpath set and the properties files are not on it.
Another possibility is that the properties files are on the classpath, but that the locations don't match what's expected, e.g. the code is looking for foo.properties and the file is at `/props/foo.properties'
it calls the getResource() method instead of the getResourceAsStream() method.
getResourceAsStream() is just a null-safe version of getResource().openStream().
What other options do I have so the Bundle Classloader locates these files?
Another thing to check is whether the client code is actually using the bundle classloader when trying to load these resources. In some cases the code tries to be clever by using the Thread Context Classloader, which would need to be set appropriately before calling the client code.

Getting the current working resource directory in java maven project

I am currently working on a JUnit test that checks functionality responsible for loading/saving a process configuration from/to some file. Given that a particular configuration file is present in resources, the functionality loads parameters from the file. Otherwise the functionality attempts to create new configuration file and persist a default configuration coded in the class. Right now I am using .class.getResource() method to check if configuration file exists, and to retrieve the necessary information. This approach is proven to be working fine from both maven's "test-class" and "class" directories. However, I am having problems while attempting to save default configuration when the file does not exist, namely the .class.getResource() method returns null, as the resource does not yet exist. This stops me from building the target resource directory (context-dependent) where the file should be saved.
Is there a way to code my functionality to evaluate whether particular object is being executed as a test or in production? More precisely, how can I build a relative path to my resource files to point to either production resources (../classes/...) or test resources (../test-classes/..) depending on the execution mode in which the project currently is?
My question is somewhat similar to the following How should I discover test-resource files in a Maven-managed Java project? but I think it is different enough to warrant new thread.
If I understand you right, essentially your issue is that you have a Maven project, which reads a particular file (normally, and during unit tests), that determines the application's behaviour. If that file doesn't exist, your application creates it.
The problem with ClassLoader.getSystemResource(...), is that it's not actually scanning a single directory. Instead it's looking at Java's classpath to determine the location of that particular resource. If there's multiple directories on the classpath, it'll have a number of areas that the file could potentially be located in.
In a sense then, .getSystemResource(...) is one way. You're able to look-up the location of a file, but not get the appropriate location to place it.
*So what about when you need to put the file in the correct location?*
You have two options essentially:
Hard-code the location of the file: Noone likes doing that.
The locations that are scanned on the classpath are passed into the classloader. You could use, for example, the first one and create the file there.
The second option isn't actually a bad one; have a look at this sample code.
final Enumeration<URL> urls = ClassLoader.getSystemClassLoader().getResources("");
if(! urls.hasMoreElements()) {
LOG.error("No entries exist on the class path!");
System.exit(1);
}
final File configFile = new File(urls.nextElement().getFile(), "config.xml");
configFile.createNewFile();
LOG.info("Create a new configuration file: " + configFile.getPath());
System.exit(0);
This resolved the configuration file to be within my target folder: ..\target\classes\config.xml
Up to you what you do; happy to provide more tips & advice if you feel more is required.
It sounds like you want to do the following:
When your code runs, it tries to load the configuration file. When the configuration file is not found you want to create the configuration file. The twist is that
if you are executing the code in "production mode" (I presume using something like the exec-maven-plugin or jetty-maven-plugin depending on the nature of your code) you want the configuration file to be placed in ${project.build.outputDirectory}
if you are executing the code in "test mode" (e.g. via surefire or failsafe) you want the configuration file to be placed in ${project.build.testOutputDirectory}
What I would do is use the ServiceLoader pattern.
You create a ConfigFileStore interface that is responsible for storing your configuration.
The ConfigFileStoreFactory enumerates all the services implementing that interface (using the ServiceLoader API by getting all the /META-INF/services/com.yourpackage.ConfigFileStore resources and extracting the class names from those. If there are no implementations registered then it will instantiate a default implementation that stores the file in the path based on getClass() (i.e. working backwards to get to the ${project.build.outputDirectory} note that it should handle the case where the classes get bundled up into a JAR, and I would presume in such a case the config file might get stored adjacent to the JAR)
Note: The default implementation will not be registered in /META-INF/services
Then in src/test/java you extend the default implementation and register that extended implementation in src/test/resources/META-INF/services/com.yourpackage.ConfigFileStore
Now when running code that has the test code on the classpath, the test version will be found, that will pick up the getClass() for a class from ${project.build.testOutputDirectory} because it is from the test classpath's /META-INF/services.
When running code that does not have the test code on the classpath, the default implementation will pick up the getClass() for a class from ${project.build.outputDirectory}
Should do what you want.

How to extend my jar later with another .jar or .java

I imagine my application like this:
I have an application for encrypting files. And I want to extend ciphers in my application(jar) with another cipher later.
So, I will have a folder with ciphers (jar or java files) and my application will read the files from this folder. Then, in GUI, there will be a list with files(jar or java), which have a method encrypt and decrypt (I would test it with reflection). And user will choose one.
Could someone give me an advice? How make it, that it could be extended? Or how my application(.jar) could work with another .jar, .java file(read them and run them)?
Another way to do this would be the java.util.ServiceLoader mechanism. (Read the documentation.)
Your additional jar would contain a file META-INF/services/my.package.Cipher listing all implementations of your Cipher interface (or abstract class), and then you can say
ClassLoader cipherLoader =
new URLClassLoader(new URL[]{new URL("jar:file:myAdditional.jar")},
Cipher.class.getClassLoader());
ServiceLoader<Cipher> serviceLoader = ServiceLoader.load(Cipher.class, cipherLoader);
for(Cipher c : serviceLoader) {
c.encrypt(...);
}
Using ServiceLoader mandates that your implementations have public no-argument constructors - if they don't have, use instead a ServiceLoader for some factory interface.
The URL should be either a jar: URL to the jar file in which the classes are, or a non-jar URL which points to the root directory where the .class files are in (in their package structure).
I would check out the Java Extension Mechanism.
This document describes the mechanism
provided by the Java™ platform for
handling optional packages. An
optional package is a group of
packages housed in one or more JAR
files that implement an API that
extends the Java platform. Optional
package classes extend the platform in
the sense that the virtual machine can
find and load them without their being
on the class path, much as if they
were classes in the platform's core
API.

How to make the java ServiceLoader work in a NetBeans 6.9 module application

I have troubles using the java ServiceLoader in a NetBeans module application. Here is what I'm trying to do (and it works in a normal java application in Eclipse):
I have an interface.jar, which declares the interface. And I have implementations.jar, which has several implementations of this interface, all specified in the spi/META-INF/services/my.package.name.MyInteface file (this file is in the implemenations.jar).
I also have a class ImplementationHandler (in yet another handler.jar), which has the following method to load all implementations:
private static List<MyInterface<?>> loadAllImplementations() {
List<MyInterface<?>> foundImplementations = Lists.newArrayList();
try {
for (MyInterface implementation : ServiceLoader.load(MyInterface.class)) {
foundImplementations.add(implementation);
}
} catch (ServiceConfigurationError error) {
system.out.println("Exception happened");
}
return foundImplementations;
}
This code returns all implementations in Eclipse normal application (the foundImplementations.size() > 0).
However under NetBeans, it can't find anything (foundImplementations.size() == 0).
More details:
I have the source of a NetBeans module application (open source, not written by me), which I need to extend by using some of MyInterface implementations. The interface.jar, implementations.jar and the handler.jar are created in Eclipse and they are part of another application.
In the NetBeans, I opened the module which needs to use the new impplementations and I added all my 3 jars as external libraries (NetBeans copied them into its ext folder, which I don't want but I can't do anything about - I want them in another myext folder, but that's another story). Then I rebuilt everything and tried to use one of my implementations, but it was not found... The code that gets an implementation is in the ImplementationHandler class and looks like:
public static final <T> MyInteface<T> getByName(String name) {
for (MyInteface implementation : loadAllImplementations()) {
if (implementation.getName().equals(name)) {
return implementation;
}
}
throw new IllegalArgumentException("Unable to find MyInterface class for: " + name);
}
I got my exception "Unable to find MyInteface class for: myImplementationName"...
My knowledge about NetBeans is very limited and I was wondering is there something more that I need to do in order to get this working?
In order to work, you have to make Netbeans create this services sub folder inside META-INF. It's very easy to do, but the information is easily accessible.
To add something to META-INF, you need to create a folder of this name in your src/ (the source directory [spi?]) folder. In this case you also need the services folder and in it, create a text file with the same fully qualified name as your service interface. In the end you should have this structure: src/META-INF/services/my.package.MyInterface.
Finally, this [my.package.MyInterface] file's content should list all the implementation classes (one per line).
With this setup, Netbeans will create the appropriate jar when building your app.
Take a look at this ServiceLoader example. It's a complete example, although it does not explain the Netbeans integration I just described.
The ServiceLoader.load(Class) uses the current thread's context class loader to load all the implementations. It may be that in your case your implementation classes in your jar file (implementation.jar) are not in that class loader's classpath.
You may have to try different approaches for this :
You may either need to have all the jars in the netbeans module's classpath or,
You may need to create a class loader (probably a URLClassLoader having those jars in its classpath) and use the ServiceLoader.load(Class, ClassLoader) and pass a that classloader.
There is another option you could try but I am not sure about this: The jar file spec allows you to specify Class-Path manifest attribute, to which you can add 'implementation.jar' entry. More details here
Most likely, the handler.jar and implementations.jar are not loaded by the same class loader. Also you may want to take a look as to why your files are getting to ext folder.
Hope this helps.
Edit:
Also try calling the ServiceLoader.load(Class, null) which uses the System class loader (the one that started the application). Probably that classloader may be able to find classes in jars located in the ext directory.
I gave up, and replaced ClassLoader with JSPF. This works out of the box and I don't need to know about the internals of a third party program, written as NetBeans module and how this affects the classpath given to the class loaders.

Class.getResourceAsStream() issue

I have a JAR-archive with java classes. One of them uses some resource that is embedded into the same JAR. In order to load that resource I use
MyClass.class.getResourceAsStream(myResourceName);
One thing that bothers me though is whether it is guaranteed that required resource will be loaded from within the same JAR. The documentation for "getResourceAsStream()" method (and corresponding ClassLoader's method) is not really clear to me.
What would happen if there's a resource with the same name located somewhere in JVM classpath before my JAR? Will that resource be loaded instead of the one embedded in my JAR? Is there any other way to substitute resource embedded in JAR?
Yes. The first matching resource found on the class path is returned, just like an executable search path. This is why resources are often "namespaced" by putting them in directories that mirror the package structure of the library or application.
This behavior may be slightly different in the presence of custom classloaders (say in OSGi), but for vanilla Java apps, it is the case.
It works much the same way as for finding class files. So first try the parent class loader (recursively) then do whatever the class loader implementation does to find files.
There is no checking of the immediate caller class loader (as ResourceBundle does - see section 6.3 of the Java Secure Coding Guidelines). However, you do need permissions to open the URL, as ClassLoader.getResourceAsStream just calls URL.openStream in the default implementation.
Specify the package. Assuming you use com.yourcompany.file it SHOULD be unique. (Unless someone WANTS to override your config file via the classpath.)
If you want to read the file only from a specific JAR you can open the JarFile and read it directly.

Categories

Resources