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.
Related
I encountered a case where I need to use ClassLoader:
I have a XML file which specifies the configuration detail for sql, and I want to load it into a configuration class. The first step is to load what is in the XML into an Inputstream.
public class Resources{
public static InputStream getResourceAsStream(String path){
InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path);
return resourceAsStream;
}
}
I only know vaguely what is a classloader: It loads classes into JVM. It is not clear to me at all why one would use classLoader here. Can't we just read what is in path directly? My guess is that this might have something to do with the timing of when one wants to load the resource.
A project is composed of two things:
Code compiled into .class file
Ressources (any file such as properties, xml...)
Then it is packaged. The packaging can be mainly:
a JAR
a directory
A ClassLoader is what is capable to access to packaged projects (jars, directories...). The main ClassLoader is accessing jars and directories specified in the classpath, but additional ClassLoader may be added at runtime. For example, on an application server, where you can deploy new packaged applications at runtime, for every application a ClassLoader will be created.
That's why, to access ressources from a packaged project, you need to use a ClassLoader (even the name is not clear about ressources).
If you want to access a ressource packaged together with your class in the same project, you get the ClassLoader of your class so you are sure it can access the ressources of the same project.
The most typical ClassLoader is java.net.URLClassLoader, which takes a list of URLs (local or remote JARs, directories...) such as the classpath, and look into every URL to search for .class files or ressources files.
To sum up, you can see a ClassLoader as a list of locations where to search files, either .class to load classes, or any other type of file as ressources.
I'm working with a local WebSphere server configured in IntelliJ Idea, and the application I'm working on is using a third-party library that loads a properties file with:
ThirdPartyClass.class.getClassLoader().getResourceAsStream(fileNameParameter);
It uses the default bootstrapClassLoader.
I've been instructed to make sure the properties file is in a config directory so that it can be edited without deploying a code change. My project looks something like this:
ProjectName
Configs
my.properties
src
java (sources root)
packages, .java files, etc
main (resources root)
schemas, web docs, etc
I have tried several of paths to make it work but it always returns null. Since I initially thought it was reaching from within the third party library package, I tried adding several ..\'s to the file path, but then I learned that this method loads from the classpath, so I pulled a
String test = System.getProperty("java.class.path");
and upon inspection, my classpath is all made up of websphere directories and jars within them:
C:\Users\me\Programs\IBM\AppServer\profiles\AppSrv01/properties
C:\Users\me\Programs\IBM\AppServer\AppSrv01/properties
and several jar files in C:\Users\me\Programs\IBM\AppServer/lib/
So just as a test I stuck the file in C:\Users\me\Programs\IBM\AppServer\AppSrv01/properties, then tried to grab it with just its file name (my.properties), but still couldn't reach it. I've also tried moving the file into the src directory and the main directory, but no matter what I do it just can't seem to find the file.
I'm aware that this method is typically used to grab resources from within a jar file, but from my understanding it seems like it should be possible to reach my file from outside of one as long as it's in a directory in the classpath... but apparently not since that didn't work.
I have the absolute path on my hard drive and will have said path on the server; is there a way to derive the path that ClassLoader.getResourceFromStream() wants with that info? Failing that, is there some obvious mistake I'm making with the resource url?
I think your fileNameParameter simply needs to start with / to indicate that it is in the root level of the classpath. Otherwise it will be searched relative to the class it is loaded from, i.e. the package of ThirdPartyClass in your example.
I am creating a horizontal module which acts as knowledge base for all the verticle products. I build the jar of my codebase independently which is consumed by all these vertical products. Each vericle product can be deployed on application server and can act as a web application.
My utility jar has an API class. Whenever user creates object of this class, locale is accepted as parameter of constructor. I have few exception messages which needs to be localized since these exceptions are shown on GUI. Based on the locale I load the bundle to get the localized messages.
I am not able to understand where do I put my i18n files inside my jar since it has no WEB-INF and is just a utility jar. Also how these i18n files will be copied in the WAR file?
To load a message bundle you can call:
ResourceBundle.getBundle(baseName, locale, loader);
As specified in javadoc:
... getBundle attempts to locate a property resource file. It generates a path name from the candidate bundle name by replacing all "." characters with "/" and appending the string ".properties". It attempts to find a "resource" with this name using ClassLoader.getResource
So the main thing is that your bundles are visible to the classloader.
You can create in your project a package, say resource, and put your bundles inside it. Suppose that your bundle name is mycomponent.properties the baseName should be: resource.mycomponent
The content of this package should seem something like a list of i18n files:
mycomponent.properties (this is the defualt bundle when no locale is specified)
mycomponent_en.properties
mycomponent_it.properties
...
This package is distributed in your jar within you code. A web module that use your component has your jar in its WEB-INF\lib (or in a shared folder of the application server) and your i18n exception messages too.
I have a a library that is bundled as an executable jar file and added to weblogic / tomcat classpath, how can I execute a main method from the jar file when the server is starting and loading the classes from the jar file.
what I want to is to have some initialization code to be executed first thing when the jar file is loaded and server is starting without any user intervention.
Note: I know I can bundle my jar in a war file, but I have some aspectj code in my library that I want to weave all running applications in the jvm, when I bundle my jar in war file, the aspectj code will only weave into the classes in the war file so I added my library jar file in the classpath.
Thanks in advance.
Add a class inside your JAR with the following code:
public class TomcatStartupListener implements org.apache.catalina.LifecycleListener {
public void lifecycleEvent(org.apache.catalina.LifecycleEvent event) {
if (event.getType().equals("after_start")) {
// call your main method here
}
}
}
Note: In order to compile this, you need to add <tomcat-dir>/lib/catalina.jar to your classpath. Otherwise when compiling it won't be able to find the necessary interfaces (org.apache.catalina.LifecycleListener and org.apache.catalina.LifecycleEvent). Once you're done with the compiling, put the JAR as usual under <tomcat-dir>/lib.
Now open <tomcat-dir>/conf/server.xml and add the following under the <Server> section:
<Listener className="com.yourpackage.TomcatStartupListener" />
Now whenever your Tomcat server starts, this TomcatStartupListener class inside your JAR will be called, and you can invoke your main method. There are a whole lot of other event types too! You can use any of these event types:
before_init
after_init
before_start
configure_start
start
after_start
before_stop
stop
configure_stop
after_stop
before_destroy
after_destroy
This approach is necessary because of the way the classloaders work in Tomcat (or even most JVMs). Here are the important points from that link:
There are three aspects of a class loader behavior
Lazy Loading
Class Caching
Separate Namespaces
The JVM will get very heavy if all classes inside all JARs get loaded indiscriminately. So the classes inside shared JARs are loaded only on-demand. The only way for you to invoke the main method is to add the above lifecycle listener.
Perhaps the simplest thing to do is to deploy a trivial servlet in a .war file that references your .jar file. The servlet can be configured to start up upon deployment/container start, and then it can invoke the class containing your main() method.
As application servers / servlet containers typically have a lot of different classloaders, you'll most likely need a different strategy for weaving aspects into your code than in standalone applications.
I would recommend to add the aspects to every war file deployed at build time. This might be following a common technique - as opposed to a server specific one.
Further, I'm not sure it can actually be done (properly & supported) on a server. Typically a server is built to separate all webapps from each other. You might get it to work, but it might break on the next update of the server.
It might be easier to suggest an alternative technique if you'd state the problem that you want to solve with your proposed approach.
Edit after your comment: Consider the standard web application lifecycle: You can execute some code, e.g. in a servlet, upon it being deployed. If you insist on your code being contained in main, you can call this method from your webapp's initialization code.
You need to register a Java Agent. See this link: java.lang.instrument.
java.lang.instrument provides services that allow Java programming language agents to instrument programs running on the JVM.
This is the right way to do this.
I am using one third party jar in my code. In the jar file , in one of the classes, when I opened the class using de-compiler, the code below is written:
java.net.URL fileURL = ClassLoader.getSystemResource("SOAPConfig.xml");
Now I am using this in my webapplication, where should I place this SOAPConfig.xml so that it will find the fileURL.
Note: I have tried putting this XML in WEB-INF/classes folder. But it is not working. Your help will be appreciated.
In Addition: In the explaination you have given, It is telling me not to use this code snippet inside the third party jar in this way...What is the exact usage of this statement
ClassLoader.getSystemResource will load the resource from the system classloader, which uses the classpath of the application as started from the command line. Any classloaders created by the application at runtime (i.e. the one that looks in WEB-INF/classes) are not on the system classpath.
You need to
Look through the script that starts your server, find out which directories are on the classpath there, and put your SOAPConfig.xml in one of those. If necessary, change the classpath in the script to look in a separate directory that's just used for your config file.
Track down the person who used ClassLoader.getSystemResource in the library, kick them squarely in the nuts, and tell them never to do that again.