Java resource not found in Jar, in web app in Tomcat 8 - java

I have a web app running in Tomcat 8 and this web app war files includes several jars (through Spring framework). One of them is a jar called connector.jar. This connector.jar contains a method that receives a package name, and the method checks if a certain file is present. I am sure the file is present (triple checked it, also in the jar file), so in development, when running as exploded jar, it returns true, but when it's packaged in a jar and deployed as a war in Tomcat, it returns false.
Please help me understand what the problem can be, and how to solve this or debug it?
The method:
public boolean isPresent(String packageName) {
return getClass().getClassLoader().getResource(packageName + "index.json") != null;
}
Notes:
Example of the package name: "com/bla/connector/" (the package name
doesn't contain a leading "/").
I also used class.getResource(), but same result. In SO the advised
not to use this when finding a resource in a jar, as you might use
the wrong class loader. I am using ActiveMQ embedded in the same war.
The code can be called by a Queue Consumer. But I don't think that is
a problem the way I am using the ClassLoader.
UPDATE, some more details as requested:
I use maven as build tool (latest version), and maven builds the war. I use the default maven war layout. WEB-INF/classes, WEB-INF/lib. The lib folder contains all the jar, and the connector.jar.
The method isPresent is located in the connector.jar in the class com.company.HtmlWorker.
The files index.json is located in the package: com.company.json.account.
Output of the isPresent method when logging the created index.json name: "com/company/json/account/index.json"

Related

Java class not loading from javaclassloader service in IIB

I have an application build in IIB v10 where i am referencing a java routine from esql. I have created the java class as an independent resource, then exported it as a JAR file and placed it in a folder under C://lib. There was an existing configuration service for java class loader which also pick yet another jar file from C://lib. I then amended the existing configuration service for Java Class loader with the new jar name and its path. Also, did a restart of the broker and inspected via mqsireportbroker command and webconsole to confirm the new jar has been updated in the configuration service. Now when i deploy the BAR file to the server, I am getting the below error.
BIP3202E: (com.xxxx.soa.xx.xxx.createMD5Hash, 1.12) : An error occurred when trying to resolve the Java class or method 'com.xxxx.gen.createMD5.createMD5Hash' which is referred to by the routine 'createMD5Hash'.
Further messages are generated that explain the error in more detail.
Correct the syntax of your Java expression in node 'com.xxxx.soa.xx.xxx.createMD5Hash', around line and column '1.12', then redeploy the message flow.
BIP2943E: The Java Method 'com.xxxx.gen.createMD5.createMD5Hash' could not be found as its containing class could not be found in the deployed bar file or in the 'workpath/shared-classes/' directory.
The specified Java Method 'com.xxxx.gen.createMD5.createMD5Hash' belongs to a class that could not be found in the deployed bar file or the 'workpath/shared-classes/' directory. Ensure that the fully qualified name of the method is correct and that its containing class or jar file is in the deployed bar file or in the 'workpath/shared-classes/' directory.
Examine and correct the SQL program.
When I put the jar file under workpath/shared-classes directorey, its working. But I want the jar file to take from the shared class librarry which is C://lib. What am I missing in this case?
All you need to do it to put your jar in the shared-classes folder.
You can find all the information about it in the IBM documentation really easily, but here is an example on Unix :
/var/mqsi/config/MY_BROKER/shared-classes
You can either put the .jar there, and it will be loaded for each execution group (also called integration servers).
If you know you only need this for a specific execution group, then you can copy it there :
/var/mqsi/config/MY_BROKER/MY_EG/shared-classes
And I would highly recommand you to use the second option, otherwise you might have performance issues if you do this with a lot of libraries
There are only a few paths where the .jar files are loaded from.
See for more information: https://www.ibm.com/support/knowledgecenter/en/SSMKHH_9.0.0/com.ibm.etools.mft.doc/bk58210_.htm
JAR files are loaded in the following precedence order:
JAR files placed in the integration server shared-classes directory allow only a single defined integration server to access them. Files placed in here are loaded first.
JAR files placed in the broker shared-classes directory allow only a single defined broker to access them. Files placed in here are loaded after any files placed in the integration server shared-classes directory.
JAR files placed in the top level shared-classes directory are made available to all brokers and all integration servers. Files placed in here are loaded after any files placed in the broker shared-classes directory.
I found it on windows here C:\ProgramData\IBM\MQSI\config*\shared-classes
using this command mqsireportbroker <my_broker_name>

class.getResource() is null though it is in classpath

I have a jar containing the main class of a project. This depends on several other jars that reside in a lib directory. One class of such a dependend jar loads a ressource "/Data/foo/bar/file.txt". But loading this file as ressource leads to null.
Here is the directory structure:
./main.jar
./lib/lib1.jar
./lib/lib2.jar
./lib/lib3.jar
./lib/runtimedata/Data/foo/bar/file.txt
This is the classpath of the manifest.mf of the main.jar:
lib/lib1.jar lib/lib2.jar lib/lib3.jar lib/runtimedata
I start the application via
java -jar main.jar
The lib2.jar contains a class that tries to load the file with
ThatClass.class.getResource("/Data/foo/bar/file.txt");
But that happens to be null. Why? lib/runtimedata is in the classpath.
I even tried to put the Data directory into lib/lib/runtimedata, in case the path is relative to the jar file containing the loading class. But that doesn't help.
What am I doing wrong here?
EDIT:
Running the application with
java -cp main.jar:lib/*.jar:lib/runtimedata my.package.Main
works correctly.
EDIT 2:
I cannot change that lib that does the resource loading. I am only using that lib. The only things I can change is the main.jar the classpath and the command line.
When you start the path with a "/", it's considered as absolute. Try ThatClass.class.getResource("/runtime/Data/foo/bar/file.txt");
Otherwise, if you cant't change the code, put the file on /Data/foo/bar/file.txt
In a development environment, this can sometimes happen when resources have not been processed during the build. Using Gradle and building your main JAR or main test JAR will depend on the compileJava tasks in your libs, but will not trigger their resources to be processed. You can see if this is happening by looking in your build dir to see if resources for your libs have been copied over. If they haven't been copied the resource loader won't find them at runtime.
If this is the problem, a full build will fix the issue and published JARs should always have their resources. But, it's nice to be able to trigger e.g. the test task for an individual module and know that it will always pull in everything it needs. If you have a library with essential resources that must always be present, you can force them to be processed in partial builds by adding this to the build.gradle of the library:
compileJava.dependsOn(processResources)

Externalising resource Adapter giving : java.lang.ClassCastException someclass incompatible with someclass

I am working on externalizing Resource adapter rars.
Earlier, the rar were packaged inside the /lib of war and everything worked good. Now to make the war light and also generic, I want to externalize resource adapter.
What I have done yet
Removed rars from war
installed rar externally through WAS7.0 Admin console
configured J2C connection factories for each RAR
I did a clean ,restart and I got some ClassNotFoundErrors.
Why were these errors there :
Basically the rars use some jars that are present inside /lib. so earlier there was no problem but now when I externalized it, I started getting CNFE`s.
How I resolved:
When we install a rar through WAS admin console , there is an option to provide classpath. I provided the jars that were causing issues on classpath there. And I could deploy and start my application
The problem:
When I login to my application. There is a line of code in one of the jars (that was causing issues and was added to classpath of resource adapter , note that currently this is present inside war and also on classpath of resource adapter), that is doing a type cast.
Now on this statement
I get an exception
java.lang.ClassCastException: com.csc.fs.ra.SimpleMappedRecord incompatible with com.csc.fs.ra.SimpleMappedRecord
I dug up and found that a possible cause is multiple version of same jars. which is a case in my case.
i have a version of jar inside war library and also on classpath of resource adapter.
I am kind of out of ideas here. what to do to resolve this kind of situation. please help
Regards
The RAR and the WAR got their own ClassLoader, even if you use the same version of the jar, each one of them loads the class separately and you get ClassCastException.
Before when it was embedded it worked because the RAR was using the same ClassLoader.
If the RAR are now separate I think you will have to put the jars in a shared library so it will be loaded by a single ClassLoader.
You can check the classloaders. It will show you all jars that are loaded.

my jsp code works only if i keep jars in Tomcat lib apart from lib folde in classpath

In my application I am calling a bean from JSP and displaying data.I have kept all required jars in lib folder and that is in build path.When I run this bean it fetches the data successfully. But when I call this bean in JSP it says No Class defination.I found a workaround for this that is putting all the jars also in the lib folder of Tomcat.(apart from keeping in classpath). Means now i have to deploy the war and also put jars in tomcat lib.but why do i have to do this when jars are already in classpath.
thanks.
It sounds like you might be having a conflict with some of the jars. If one of the jars is depending on something provided elsewhere in the tomcat class loader, or if it is providing the same functionality - you could end up with a problem. You used to see this often with a conflicting XML parser implementations.
This has some more information on what is included: http://tomcat.apache.org/tomcat-7.0-doc/class-loader-howto.html

Tomcat - won't load my META-INF\services\javax.servlet.ServletContainerInitializer file?

I have a web project that has a \META-INF\services\javax.servlet.ServletContainerInitializer file with its content pointing to the fully qualified name of a class that implements the ServletContainerInitializer interface. I basically followed the example given here: http://nullhaus.com/2011/03/using-servlets-3-0-servletcontainerinitializer/
I put debug lines in my class that implements the ServletContainerInitializer interface and it never makes it there. Not even the default constructor...
My application folder structure is as follows:
\MyApp
\META-INF\services\javax.servlet.ServletContainerInitializer
\WEB-INF\classes\
... [list of classes and packages go here]
Any ideas what I need to check for??
Note 1: My Tomcat publishes from an exploded external folder that contains my application
Note 2: I started my Tomcat from Eclipse - if that makes a difference!
Well, I think that you'll need to wrap your initializer class (and it's services-related META-INF directory) into a separate *.jar and put it in the WEB-INF/lib.
This is a JAR service, so I guess it could have something to do with problems with discovering services in a *.war file. Moreover, it doesn't even help if you put your META-INF directory inside WEB-INF/classes and set unpackWAR=false in your Tomcat's server.xml.
HTH.
The first thing to check is that you are actually using Servlet 3.0 and not an earlier version. For Tomcat, this means that you must be using Tomcat 7.0.22
Second, make sure that the \META-INF\services\javax.servlet.ServletContainerInitializer file actually exists in the exploded war file.
Third, when in doubt, configure and start Tomcat directly (not from Eclipse) - I've seen developers have endless problems with configuration of Tomcat using the Eclipse plugin.
For tomcat to load the META-INF directory , it has to be in classes folder . If you are using maven project , just put the META-INF directory inside src/main/resources directory .. on mvn package the same will be copied to classes directory .. No need of separerate jar .. if jar is prefered you can use
HandlesTypes annotation ..
I would like to quote some good explanation from Mark Thomas <markt#apache.org> given on the user mailing list of Tomcat:
Service files are loaded by class loaders from the META-INF/services
directory.
*.jar!/META-INF/services
and
*.war/WEB-INF/classes/META-INF/services
are visible to class loaders
*.war!/META-INF/services
is not.
The servlet expert group recently discussed WAR vs JAR in the context of
Java 9 and mutli-version JARs. The conclusion was (I'm paraphrasing)
that WARs are not a specialised form of JAR and while they share a
common format a feature that is available to a JAR is NOT automatically
available to a WAR unless the Servlet spec (of Java EE spec) explicitly
states otherwise.
Containers are free to add container specific extensions if they wish
but they come with the usual (lack of) interoperability warnings.
http://mail-archives.apache.org/mod_mbox/tomcat-users/201808.mbox/

Categories