Java Classpath patch concept for WAR files - java

There is a classpath patch concept frequently used by adding patch.jar file in classpath before main jar file. I have used this method for running J2SE applications.
I wonder if same concept is applied for J2EE projects such as in WAR files. If so how I can make it possible?

I'm not sure whether there's any guaranteed ordering among different JAR files in WEB-INF/lib, but it is guaranteed (Servlet Specification version 3.0, section 10.5) that WEB-INF/classes will be ahead of all the WEB-INF/lib JAR files. So you could put your "patch" class files under WEB-INF/classes instead of putting them in a JAR.

Related

Make JAR available to JDK and JRE

I am running a web application using Tomcat, JDK8 and Netbeans IDE (using ANT for build and IVY for dependency management).
I currently place JARs that need to be available to the JRE (servlet-api.jar, jsp-api.jar, el-api.jar, tomcat-dbcp.jar) in JAVA_HOME/jre/lib/ext.
I'm upgrading to a new JDK version (JDK17), which no longer has the JRE extensions folder. I'm wondering where I should place these JARs.
According to this post (and others I've seen), it is better practice to use a dependency manager and add these jars to your classpath anyway.
I currently use IVY to manage my dependencies and have customized my ANT build to add run-time dependencies to the WEB-INF/lib folder of the built WAR file.
However, I do not need the JARs I listed above to be available to my application at runtime, I need them to be available to the JRE. That is, I do not want the JARs (servlet-api.jar, etc.) to be in WEB-INF/lib of my built WAR file.
How can I do this?
Sharpening my final questions:
How can I make certain JARs/dependencies available to the JRE in Netbeans in my development environment?
How can I make certain JARs/dependencies available to the JRE in the built WAR file used on my production environment?
Am I correct in saying that these JARs need to be available to the JRE? All of the posts I've seen discuss compile-time vs run-time dependencies but it seems the case I'm describing is a different category of dependency. Is this correct?
I currently place JARs that need to be available to the JRE (servlet-api.jar, jsp-api.jar, el-api.jar, tomcat-dbcp.jar) in JAVA_HOME/jre/lib/ext.
That's not the best way to do things. See Is putting external jars in the JAVA_HOME/lib/ext directory a bad thing?
Note that the jre/lib/ext mechanism has been removed from newer versions of Java, so this will not work anymore if you use a newer version of Java. (This has been removed in JDK 9).
However, I do not need the JARs I listed above to be available to my application at runtime, I need them to be available to the JRE.
Why?
That is, I do not want the JARs (servlet-api.jar, etc.) to be in WEB-INF/lib of my built WAR file. How can I do this?
Why not? Putting the dependencies that your application needs in WEB-INF/lib is the normal thing to do in Java web applications. Why do you want to do things the non-standard way?
But: Some JAR files, such as servlet-api.jar, jsp-api.jar and el-api.jar are not supposed to be included in your application. Those JAR files define standard Java EE / Jakarta EE APIs and will be provided to your application at runtime by the application server (Tomcat, etc.) that you deploy your WAR file in.
You can add those JAR files as dependencies using Maven with provided scope, which means they will be used while compiling, but won't be packaged into your application.
Am I correct in saying that these JARs need to be available to the JRE?
No, those JARs do not need to be available to the JRE. JAR files that contain standard APIs will be provided by your Java EE / Jakarta EE container at runtime. Other JAR files should be included in your application in WEB-INF/lib.

EJB Timer (#Schedule) not recognized by WildFly when inside a JAR which is also inside an EAR

I have an EAR file with 3 WARs and some common dependencies within lib/ folder. One of those dependencies has an EJB with a scheduled method, but it seems that the server is not recognizing it. No evidence in log, no code executed.
But when I deploy a simple war with the same jar within WEB-INF/lib, it works. I've already tried to package the jar as an ejb-jar with the maven plugin, but no success at all.
Any ideas on what must be going on?
Jars in the lib directory of an EAR are not considered to be EJB jars and their content is not considered at deployment time in that respect. The lib directory only exists to make it easier to provide library jars whose classes are automatically made visible to all the modules in an EAR file.
Your EAR file contains a META-INF/application.xml file that lists out the various modules that it contains. Prior to Java EE 6, only ejb-modules were considered for potential EJBs. Java EE 6 and newer added web-modules to that list. All jars within a web-module's WEB-INF/lib directory are considered to be part of the web-module, even if they contain ejb-jars. Any ejb-jar.xml files found within a web-module are merged together. This applies even if the web-module is deployed as a standalone WAR file.

Java Libraries Runtime vs Compile Time

When setting up a Java web application using Tomcat as an application server I often get confused about when libraries are available. Through some discussion on Stack Overflow, I have learned that some libraries (.jar) files are available at runtime, while others are available at compile time. Often I will get errors and will resolve them by trial and error, placing jar files in different directories until the application runs or compiles. It was recently pointed out to me that you can make .jar libraries available at runtime via the WEB-INF/lib folder. I started thinking about this and had a few question. I have read up on this topic in the past and haven't found a source that puts the information into a context I easily understand and retain.
Is there a compile time classpath and a runtime classpath you can set for a project?
a. Is classpath even an applicable term for discussing libraries available at runtime?
Is WEB-INF/lib the only way to make libraries available at runtime? What about the lib folder in Tomcat is this available at runtime?
How does this relate to classloaders? I know that a hierarchy of classloaders is created. Are these strictly for Runtime operations?
The compile classpath is the classpath used to compile your Java source files (using javac -cp ..., or your IDE). Every class referenced in the source file must be present in the compile classpath, else the compiler will complain that it can't find the class.
Once you have compiled the classes, you can run a program using them (using java -cp ...). Obviously, the libraries on which your source code depends directly should be in the runtime classpath. But that's not all. If you depend directly on CoolLibrary.jar, and this library internally depends on Guava.jar, then Guava.jar must also be in the runtime classpath, although it was not needed when compiling.
Webapps are a bit special. The servlet specification specifies that the classpath used to execute the webapp is composed by the WEB-INF/classes directory of the deployed webapp, and of all the jars contained in WEB-INF/lib. All the webapps also have access to the native servlet and JSP jars, which are directly provided by Tomcat. In reality, Tomcat's internal classes (like the implementation classes of the servlet-api interfaces) are also available to the webapp, but relying on these classes is not a good idea, since it would tie your webapp to tomcat.
Talking of the runtime classpath, in the case of a webapp, is a bit of a simplification. In reality, every webapp's classes are loaded dynamically by a specific classloader by tomcat. And this webapp classloader is a child of the tomcat's classloader. So, in theory, you could place the webapp jars in Tomcat's classpath directly, but this would mean that all the webapps would share these libraries, and that you would have problems undeploying and redeploying webapps. The goal of having a specific classloader per webapp is to be able to have, in the same JVM, an app relying on Guava 11.0, and another one relying on Guava 12.0, for example.
For more information about tomcat classloaders, read the documentation.
in eclipse, you have the java build path which include the libraries during compile time, and you have order and export, which is for the runtime.
only the tomcat libraries available by default

JAR vs WAR file specification

I noticed that WAR files are supposed to have classes/ and lib/ directories for containing their classfile root and dependencies, respectively.
I also noticed that it is not common practice for JARs to contain such a lib/ directory and to contain their own dependencies.
So now I'm wondering why JARs shouldn't/usually-don't contain their own dependencies, but WAR files are expected to. Unless I'm missing something, both require their dependencies to be on the classpath at runtime (JARs won't run if they're missing dependencies, just like WARs won't run). So to me, all the arguments for putting dependencies in a WAR file also apply to a JAR.
What am I not "getting" here?!?
From a conceptual point of view:
A jar typically represents a single library which can be used and may or may not have dependencies.
But the idea is that a specific functionality can be provided as a library in a jar.
A war is an application by itself and as such it should include all the dependencies
WAR files (mainly WEB applications) are uncompressed, and all JAR files inside the lib folder will be in the classpath of your web application while running.
JAR files might also contain jar files, but will not be found and loaded by the default class loader.
There are special class loaders that do load jar files inside jar files, but not standard.
Because unless you use a custom classloader that can load classes from nested jars (like JarClassLoader), you can't load classes from nested jars. Servlet and app containers will add jars contained inside war/ear files to their application's classpath.
They are containers with different purposes.
Jar is intended for Java class files and resources and can be a library or a directly runnable application.
War is a container to package complete Web applications including all dependencies (such as Jar files and other web resources).
The question boils down to, why can't a jar include other jars. This I would take as a design decision taken to allow maximum flexibility in interchanging dependencies.

eclipse tomcat why does it keep copying the servlet api jar

I am using eclipse and added tomcat 6 server. whenever i try to start the server it automatically copies the server api jar into the WEBINF/lib folder and the app does not start
INFO: validateJarFile(/media/01CB9CAC704E03A0/Projects/.metadata/.plugins/org.eclipse.wst.server.core/tmp1/wtpwebapps/myapp/WEB-INF/lib/servlet-api-2.5.jar) - jar not loaded. See Servlet Spec 2.3, section 9.7.2. Offending class: javax/servlet/Servlet.class
I got the same problem and finally got it to work after looking at this among other pages. I am going to list it here in case other people run into the same issue.
In my case, the myeclipse project was created from a maven .pom file which lists the JEE 1.5 files as "provided" scope i.e. these files will be provided by the container (in my case tomcat 6). So if I create a war file using maven and drop it in the webapps dir it just works fine. It gets a little tedious doing that every time you make a change, so I decided to go for exploded deployment and thats when I ran into the same issue.
Since the project file was created from the pom file, these dependencies came in but Eclipse project file does not recognise these as "provided" and not to be deployed. And TOMCAT is not happy when it finds two implementations of the spec:
"Servlet spec 2.3 sec 9.7.2 recommends … The classloader that a container uses to load a servlet in a WAR must allow the developer to load any resources contained in library JARs within the WAR following normal J2SE semantics using getResource. It must not allow the WAR to override J2SE or Java servlet API classes. It is further recommended that the loader not allow servlets in the WAR access to the web container’s implementation classes. It is recommended also that the application class loader be implemented so that classes and resources packaged within the WAR are loaded in preference to classes and resources residing in container-wide library JARs."
I toyed with the above solution to change my deplyment assembly config but that is not helpful alone since these jars get bundled in if I select the "JARS from the build path"
in the menu project properties/MyEclipse/Web/Deplyment/Configure workspace settings.
And I need to do that cos I have other dependencies jars (not provided by container).
Solution:
I removed these jars from the dependencies in the build path manually.
Instead I added a dependency on User Library - JAVA EE5.
And in the deployment configuration I removed the option of JARS exported from User Libraries of required projects.
YMMV. But in any case if you read the servlet spec definition above and then look at the config in your workspace, you can sort it out.
Hope this helps.
Go to your project properties and look under Deployment Assembly page. This page describes how your app will be packaged for deployment or export. Take a look at entries regarding libraries. You have to figure out which of those entries points to the servlet api jar and remove it. If the entry points to other jars that do need to be packaged, you will need to split it into several separate build path entries, so you can tell Eclipse exactly what does and does not need to be packaged.

Categories