/WEB-INF/classes vs /WEB-INF/lib - java

I'd like to package my Java EE6 web classes (beans, filters, servlets) into jar and place it into /WEB-INF/lib/ directory along with other utility jars and abandon /WEB-INF/classes/ directory totally.
Are there any substantial differences between the two in terms of classloading, acessing application context, etc?
Thanks.
PS: Whenever googling any of java specs I'm always redirected to Oracle documentation index which is dozen clicks away from original url. Anyone knows what's happening there?

I'd go for /WEB-INF/classes. It allows you to run your application in debug mode and hot-swap classes on change. If you package everything as a jar, you'd have to repackage and redeploy the app every time you change a class.

Well, shortly: Imagine you have class org.example.Test.class, if you put it into jar and in WEB-INF/lib/ directory, and copy the same class into WEB-INF/classes/ then classloader of that application will use last one (from WEB-INF/classes/).
Sometimes you can use it as advantage - I have a library, and it has a bug... I look for source of that class (where bug is; I miss the part of how I know that bug is in that class, that's another story), I add that class to the project with fixed code, and it is compiled into WEB-INF/classes/ while library still exist in WEB-INF/lib/. Fixed class will be used until library will be fixed.

In Tomcat Servlet container's definition: WEB-INF\classes is searched before WEB-INF\lib. You can choose to delegate your classloading to your custom classloader - even then the order above is maintained.
If you choose to go with a different provider e.g. JBOss, Glassfish, Jetty it might have a different order, but I am not sure about those.

Related

What's the expected resource loading priority when Jetty's setExtraClasspath is used?

Is it expected that resources in .jar files included via Jetty's WebAppContext.setExtraClasspath method would be loaded in preference to resources with the same name/path within the .war?
——
I’m in the process of moving some dependencies out of .war files and including them instead via WebAppContext’s setExtraClasspath method (http://www.eclipse.org/jetty/documentation/current/jetty-classloading.html#using-extra-classpath-method)
While doing so, I ran into a problem where one of the .jar files now moved out contains an ehcache.xml file, which seems to be read in preference to the one within the .war when Thread.currentThread().getContextClassLoader().getResourceAsStream(“ehcache.xml”) is used.
Now that I know that to be the case, I guess it’s not a big inconvenience to me if I need to rename the file to be unique, but I guess I’m wondering…
Am I loading the resource incorrectly (and if so, how can I do it to the .war takes priority)?
Was it only luck that the .war version took precedence previously (when the .jar in question was packed within the .war)?
Is this an expected drawback of using setExtraClasspath that I just have to live with?
Did I miss some documentation somewhere which would have clarified this for me?
Thanks to Jan on the jetty-user's list...
AFAIK we don't do any special handling of the extra classpath. The order
of paths that are added to the URLClassLoader that is the webapp
classloader is:
extra classpath
WEB-INF/classes
WEB-INF/lib/*.jar
The webapp classloader getResource(String) method first looks in itself
before looking in the parent (to conform to servlet spec inverted
classloading requirements), however the looking is all delegated to the
URLClassLoader, so it is whatever ordering the jvm has implemented, which
according to GrepCode
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/misc/URLClassPath.java#URLClassPath.0urls
looks like the search order will be as above.
Sad for my case, but makes sense.

Tomcat 8 classloading - difference between JAR in [WEB-INF/lib] and [tomcat/lib]

It says here that the common classloader is visible to all web application. So what is the difference between having a JAR file within the WEB-INF/lib folder of my war application and having the same JAR file within Tomcat's lib folder?
This JAR is a provider for a Java SPI that I created. When I have it under WEB-INF/lib, everything works perfectly. But if I try to put this JAR under Tomcat's lib folder (or under shared/lib after configuring it in catalina.properties) I get errors. The second option is better for me because I need total decoupling of my application and the provider.
The error I get is a ClassNotFoundException for the interface that represents my service (which the JAR implements). This interface is in a third project, which is a dependency for my war application.
The tomcat/lib directory should be used for shared libraries for the whole web server and WEB-INF/lib only for one web application.
The difference is the classloader being used. Tomcat has very specific rules for it's classloaders, some of them are counter intuitive. Your problem here, is that a class loaded in one classloader is not compatible with a class loaded in another. Even Object to Object will fail.
The jar containing your interface is loaded by the webapp classloader since it is located in the war-file. The implementation is loaded by the common classloader. This does not contain your interface, so it throws a ClassNotFoundException.
The solution is to move all jars into the same classloader. Either everything in the lib-folder, or everything in your war.
Implementing a plug-in architecture with Tomcat is a rather difficult undertaking. Using something like OSGi would probably help. But for small problems, it's probably overkill. Fun, but overkill.

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/

Java code - looking for source code

I have this Java code some other person wrote, specifically JSPs. I am trying to understand where everything is.
In my index.jsp (the main file which is loaded) it imports a certain namespace (I suppose tomcat does all the compiling, I don't know):
<%# page import="org.sgrp.SearchResults"%>
This physical location doesn't exist in my CLASSPATH so I suppose it's referring to a namespace inside the .jar code structure (correct me if I'm wrong).
So how am I suppose to find the source code for this? Does Tomcat set a specific CLASSPATH location for each project?
EDIT
I'm trying to understand if Tomcat follows a certain structure so I can find where the source code for this stuff is.
From the perspective of a web
application, class or resource loading
looks in the following repositories,
in this order:
* Bootstrap classes of your JVM
* System class loader classes
* /WEB-INF/classes of your web application
* /WEB-INF/lib/*.jar of your web application
* $CATALINA_HOME/lib
* $CATALINA_HOME/lib/*.jar
from the Tomcat docs.
The most likely locations for classes specific to your application are the WEB-INF/lib/*.jar and WEB-INF/classes. However as others have mentioned, this will contain compiled class files and you wont be able to see the exact java source (even after decompiling)
jars are compressed javabyte code. In order to get the sources you'll have to decompile them. There are a few utilities out there, but working with decompiled code is a bit of a nightmare if you ask me, I wouldn't recommend it, especially not if you didn't already know this. :D
The source code will generally be stored elsewhere.
As far as your specific questions on Tomcat, I don't know, having never working with Tomcat before.
You can look for the jars in two places:
/web-inf/lib directory (it has all the custom jars in use by the app)
The build file (mvn, ant etc)
Source code might not be there at all. So, first of all, you need to find which jar exactly has that imported class. Then just search the web to find the project/API website. There you can look for the source, if its an open source library. Otherwise, decomplilation is the only option left, and that might not be very helpful.
class path is, in my experience, commonly set in the ant build file of each project

Java distribuion as jar file containg config, libs and deps

I am developing a framework that needs a lot of stuff to get working. I have several folders inside of my Eclipse project that are needed
[root]
- config
- src
- lib
- serialized
Also there are important files like the log4j.properties and the META-INF dir inside the src directory.
I wonder if there is a way to distribute one JAR containing all essential files so my gui will just have to import one jar. I guess that I have to exclude the config folder in order to make the framework configurable.
I also wonder, if there is a way to move for example the log4j.properties to the config dir so that I have one config folder containg all needed configurations?
Thanks for help and advise on this matter!
Marco
Yes, but not really. You can take all your dependencies, unpack them and simply merge them into a bigger jar. This is what the maven jar plugin does if you make a jar with dependencies. The only problem is that this might result in conflicting files (suppose two of your dependencies contain a log4j.properties). This is one of the problems when doing the above with some of the spring libraries for instance.
I think someone actually wrote a classloader that allows you to bundle the whole jar inside of your jar and use it as is. I'm not sure how mature that is though and can't at the moment recall the name.
I think you're better off distributing all your dependencies separately. Setting up the classpath is a bit of a pain but surely java programmers are used to it by now. You can add dependencies to the Class-Path header in your manifest file, in simple cases. Bigger libraries have to rely on the classpath being set up for them though.
As to the second part of your question, probably dropping the conf/ directory under META-INF is enough for its contents to be picked up. I'm not sure about this. I'm fairly sure it will always be picked up if you put its contents at the top level of the jar. In any case, this is a distribution problem. You can easily have a conf/ directory inside your source tree and have your build scripts (whatever you might be using) copy the files in it to wherever is most convenient.
As to your users configuring. Try to establish some conventions so they have to configure as little as possible. For things that must be configured, it's best to have a basic default configuration and then allow the user to override and add options through his/her own configuration file.
In terms of the resources, it is possible except that if you do that you are not going to be able to load resources (non class files) from the filesystem (via a file path).
It's likely that you're currently loading these resources from the file system. Once in the jar you need to load them as class path resources via the class.getResourceAsStream or similar.
As for the dependent jars you may have, it's common practice for these to be placed as extra jars on the classpath. I know it's complicates things but developers are used to doing this. The nature of the java landscape is that this is inevitable. What the spring framework for example does is supply a bundled zip file with the core jar and the jar dependencies included.
Is your library going to be used in an EE context or an SE context? If it is an EE context then you really don't have to worry about configuration and class path issues as the container takes care of that. In an SE context it is a lot more tricky as that work has to be done manually.

Categories