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
Related
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.
what is the difference between simple project/lib and project/web/WEB-INF/lib?
which jar should be in project/lib folder and which jars should be in web/WEB-INF/lib?
there is no standard project/lib thing, it might be a specific project designed that way to hold library there and while compiling and deploying it might be configured to read jars from there,
where as if you put it in WEB-INF/lib web contains puts all the jars from this directory in runtime classpath so they would be available when application is running
better to use maven without needing to holding library in source control and with lots of other advantages maven brings
What are the different ways that java programs gain access to external libraries. There is setting a classpath, modifying the build or build path, but I've seen other ways of adding jars.
Why do some libraries have to be added to the classpath while others do not. For example I'm using JSF, WTP tools, and other extra libraries but they are not in my buildpath when I view the build path of my project.
The classpath is used to find classes when executing a Java program. The build path is used when Eclipse is compiling a Java program.
The Java Build Path is just an Eclipse thing. It's where Eclipse finds the classes needed to compile and run the classes of the project. It's thus both th compile and the run classpath.
In the case of a webapp, the webapp runs inside a Java EE web container. The web container gives access to standard Java EE classes (javax.servlet, etc.). Moreover, all the jars in WEB-INF/classes are automatically included in the classpath of the web app. So Eclipse doesn't need you to specify them in the Java Build Path. They're included automatically.
On development time.
A build path is one where you can explicitly point to third party software / jars.
By default not all third party software are added into your classpath, hence you may have to explicitly add that to your path.
On runtime.
On the other hand when you run your applications from the command line, you would prefix the classpath by using -cp to specify the third party jars.
For example in web projects you would add it to your web-inf library when you deploy.
A classpath is simply an array of classpath entries (IClasspathEntry) that describe the types that are available. The classpath is an environment variable that tells where to look for class files and it is generally set to a directory or a JAR (java archive) file.
The Java build path is reflected in the structure of a Java project element. You can query a project for its package fragment roots (IPackageFragmentRoot). The build path is the classpath that is used for building a Java project (IJavaProject).
In my GWT web app I am keeping all my jar files outside of my project and referencing them using classpath variables. This allows me to link to jars from other projects/teams without having to put a copy of the jar in my web app lib directory. Hosted mode kindly looks up the classes in this system classpath and then adds them to the web-app classpath warning me that it is doing so. When I deploy my build system pulls in only the jars I need to ship in my web app and is not a problem.
The issue I have is that some code uses dynamic lookups, searching the classpath for implementations. If a jar has not yet been added to the web app classpath beacuse no classes have yet benn loaded from the jar it is not included in the search.
The particular problem I am having is with Persistence - it looks up EntityManagerFactory implementations by searching for META-INF/services files. I have also had a similar problem with Rome and its module extensions.
I have got a workaround, in the dev/hosted mode I simply refer to a class I know is in a jar I want and this causes it to be added to my web app classpath. I do this my by calling
private void devModeClassPathHack() {
Class<?> gwtDevModeHack1 = EntityManagerImpl.class;
}
from my development mode Guice module.
My question is simple - is there a "nicer" way of doing this?
I don't think, there's a nicer way - using this makeshift "self-healing" mechanism of the GWT Jetty/dev mode is already a hack :) If you ever have the need to run the server side code on e. g. JBoss during development (together with GWT dev mode for the client side), this will stop working.
(As an additional problem, the order of classloading - and consequently the precedence in cases when there are multiple versions of a class in different jars - will depend on program flow. This can be very nasty to debug.)
You mention, that you pull in all the jars from outside. What I would do in that case, is to set .svnignore (or .gitignore/...) to "*.jar" in the WEB-INF/lib dir. Then I'd run the same build step which is used to create the production build to copy the jars into the lib dir before starting the server.
I'm confused with these two terms.
Also what should I do to create a file under the src folder of a Spring MVC Project?
When I create using a File object it creates the file inside C:\SpringSourceTool...
I guess this is ClassPath right?
How can I get the applicationcontext folder or root of the application whatever?
The build path is used for building your application. It contains all of your source files and all Java libraries that are required to compile the application.
The classpath is used for executing the application. This includes all java classes and libraries that are needed to run the java application. A Classpath is mandatory, the default path is . which is used if the java virtual machine can't find a user defined path. (CLASSPATH environment variable, -cp flag or Class-Path: attribute in a jar manifest)
The classpath is the conventional way to tell the (standard) Java compiler and the Java runtime where to find compiled classes. It is typically a sequence of JAR file names and directory names. The classpath used by the compiler and the runtime system don't have to be the same, but they typically should be, especially for a small project.
Buildpath is not standard Java terminology. It is the term for the richer way that a typical IDE specifies the relationship between the "projects" that make up an application. The IDE uses this to figure out the classpath and sourcepath for compiling the Java code, and the classpath for running it. The IDE also uses the build path to figure out how to package up your code and its dependencies as (for example) a WAR file.
For example, an Eclipse build path for a project includes the other projects that it depends on, and lists any additional library JARs that the project contains / relies on. It also lists the packages in the current project that downstream projects can depend on.
(If you are using Maven for your project, the IDE buildpath mechanism is secondary to the dependencies declared in the POM files. For example, using Eclipse with the m2eclipse, the buildpath is synthesized from the POM files.)
The class path is used at runtime to load compiled classes and resources.
The build path is used at compile time to find the dependencies needed to build your project.
I would like to add to Andreas_D's answer to explain that the build path is required by the IDE/compiler to locate external packages and classes used by your code. We sometimes refer to these as 'dependencies'.
NB: These external packages may be packaged inside a compressed .jar file or indeed, there may be several jar files packaged inside a 'library'. A library or group of libraries often make up a 'framework'.
If your code requires code written by others, you can import them into your class using the import command. However, this command on its own is insufficient as the compiler or IDE needs to know where those classes are located. You specify this in the build path.
The classpath on the other hand tells the JVM running your application where to find any dependencies during the actual execution of your code.
Also to note:
Classpath is for use by the JVM.
Buildpath is for use by the IDE/compiler and is a means to construct the classpath from your development environment. When you configure your buildpath via your IDE, you are also configuring a hidden file in your project called .classpath. This is used to provide the classpath to JVM at deployment.
Each Java project has its own build path that specifies all dependencies required to compile the project. Those dependencies may come from other Java projects in the workspace, from Java archive .jar files, or from folders containing .class files.
In CLASSPATH environment you need to specify only .class files (i.e., jar, zip files – Inside jar, zip files you will find only java classes) i.e. you are helping Java Virtual Machine (JVM) to find Java class files
Also what should i do to create a file
under the src folder of a Spring MVC
Project? When i create using a File
object it creates the file inside
C:\SpringSourceTool...
This is where the JVM was started, if you want to create the file else where, use relative path from here.
See this and this for more info.
Classpath (from Wikipedia):
Similar to the classic dynamic loading behavior, when executing Java
programs, the Java Virtual Machine finds and loads classes lazily (it
loads the bytecode of a class only when the class is first used). The
classpath tells Java where to look in the filesystem for files
defining these classes.
The virtual machine searches for and loads classes in this order:
bootstrap classes: the classes that are fundamental to the Java
Platform (comprising the public classes of the Java Class Library, and
the private classes that are necessary for this library to be
functional).
extension classes: packages that are in the extension
directory of the JRE or JDK,
jre/lib/ext/ user-defined packages and
libraries
By default only the packages of the JDK standard API and
extension packages are accessible without needing to set where to find
them. The path for all user-defined packages and libraries must be set
in the command-line (or in the Manifest associated with the Jar file
containing the classes).
Simply put - while your program is running, the JVM loads classes only as needed. When a class is needed, the JVM will depend on the classpath to it know where to load the bytecode from (i.e.: .class files).
Build path, on the other hand, is typically used by an IDE, such as Eclipse, to know where to look for additional libraries that are required to compile a project's source code. Build path isn't used during runtime.