I try to understand what CLASSPATH does for Java and Spring Boot projects. And I see that it is am environment variable and it is used to provide the root of any package hierarchy to java compiler.
But I didn't set this CLASSPATH, it's missing from environment variables and the projects are working properly. I set only the PATH environment variable and I put the location of jdk/bin.
On the other hand, I'm working now at a multimodule Maven project on Intellij, and for example if I try to use the class ObjectMapper in a module where I don't have the dependency it says:
Cannot resolve symbol 'ObjectMapper'
Add library 'Maven com.fasterxml.jackson.core:jackson-databind.2.13.1' to classpath
And when I click it, it adds the jackson-databind dependency in the pom.xml at this modul.
So in this case it seems that classpath refers to the pom.xml, not an environment variable. Are these different types of classpath? And why my projects are working properly without having the classpath environment variable? Thank you!
The IDE puts 'Maven Dependancies' on the class path.
When a jar is built for deployment Maven puts all the listed dependencies in the jar file it builds. Java knows how to read this info and put the right locations on the classpath.
The locations of jars are listed in the META-INF\MANIFEST.MF
Manifest-Version: 1.0
Created-By: Maven JAR Plugin 3.2.2
Build-Jdk-Spec: 11
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.altron.ne.SpringBootApp
Spring-Boot-Version: 2.7.2
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
In this case (Spring Boot application) the dependency jars are all in BOOT-INF/lib/ within the main jar file.
The classpath is a feature of the JVM (actually: the built-in ClassLoaders) that allows you to specify where Java (without any extensions) should load classes from: a list of .jar files that contain classes and a list of directories that contain classes.
When you start a Java application with
java -jar yourapp.jar
the classpath (as defined either through the environment variable "CLASSPATH" or through the command line argument "-cp") is ignored.
Instead of that, Java reads the file "META-INF/MANIFEST.MF" and uses two attributes from that file:
Class-Path contains the list of jar files and directories to read class files from
Main-Class contains the name of the class that contains the public static void main(String... args) method
Spring Boot is a special case because it brings its own ClassLoader implementation that loads additional classes not from Javas classpath.
In a Spring Boot application only the "Main-Class" attribute is set, meaning that only classes directly contained in the jar are on the classpath. Among these classes is a Spring Boot specific ClassLoader (the LaunchedURLClassLoader) and it is this special ClassLoader the interprets the "Spring-Boot-Classes" and "Spring-Boot-Lib" attributes.
Using this method allows Spring Boot to package all dependencies into a single "yourapp.jar" file - something that Java doesn't support out-of-the-box.
Related
I have a SpringBoot application that is run with the command java -classpath <longlist of JARs> com.example.MyApp.
I have the path to a JAR - in this case liquibase-core-4.4.0.jar - specified on the classpath. However, on startup the app complains about missing class definitions.
Attaching a debugger I can see that the URLClassPath does have this JAR in its path list. However, there is no loader for it.
What would cause a JAR to be on the classpath but have no loader?
Spring boot applications have pretty clever system of class path searching, for example it finds jars in BOOT-INF/lib - something that a regular java system can't do. So spring has custom class loaders for this purpose.
Now its possible to add an external jar with the help of LOADER_PATH environment variable or loader.path parameter.
Bottom line, instead of using "standard" java mechanism, consider using "special" spring boot's mechanisms.
Read the official documentation for concrete examples
In the Spring Boot's docs here, about serving static content, it says:
By default Spring Boot will serve static content from a directory
called /static (or /public or /resources or /META-INF/resources) in
the classpath.
I found that all the content in the directory:
src/main/resources
will be copied inside the classpath, so I can put my static content in:
src/main/resources/static
and all will work fine and I'm happy since I can have my static content under the src directory.
But, I have some questions about this:
Why the documentation doesn't say to put static content in src/main/resources/static instead of speaking about the classpath (I think this is a bit confusing)?
Is it good to assume that the content in src/main/resources/ will be always copied in the classpath?
Is there some Spring Boot official documentation explaining what I'm supposed to find in the classpath other than Java classes and packages (up to now I only know I can found all the content from src/main/resources/)?
/src/main/resources is a Maven project structure convention. It's a path inside your project where you place resources. During the build step, Maven will take files in there and place them in the appropriate place for you to use them in your runtime classpath, eg in an executable .jar, some physical file system location used in the classpath (with java's -cp option), etc.
I could choose to build my application myself or with a different build tool. In such a case, /src/main/resources would not exist. However, the intention is for the classpath to be the same, ie. to contain the same resources and .class files.
The Spring boot documentation talks about the classpath because it shouldn't make assumptions about how your project is set up.
The classpath also contains additional libraries (JARs), which also can have a static folder, which would then be included for serving static resources. So if the documentation would only state the folder src/main/resources/static, it would be incomplete.
Ad 2: As long as you don't mess with the default Maven configuration, then it's safe to assume this.
Ad 3: Maybe start with the official Oracle documentation: https://docs.oracle.com/javase/8/docs/technotes/tools/windows/classpath.html. Hint: Of course, it's not only the contents of the resources folder, which are in the classpath, but also of course all compiled classes, hence its name.
To add to previous answers, it's the Spring Boot Maven Plugin (spring-boot-maven-plugin in the pom.xml) that that enables you to run the application using Maven. One of its functions is to ensure contents in the app including dependency libraries are available on the runtime classpath, such as within the the executable JAR file.
I am building a spring boot fat jar using gradle script.
My fat jar has two libs among others say -
open-source.jar
open-source-modified.jar (In this I have few updates
for files from earlier jar(#1) placed with same name and same
package structure)
both the jar are present in gradle dependency section like
compile project('open-source-modified.jar')
compile 'path.open-source.jar.<version>'
How do I ensure while launching app (java -jar fatjar) that modified files in open-source-modified.jar gets loaded instead of those with same name & package in open-source.jar.
If it was not a fatjar we could have achieved same by constructing class path and placing modified.jar before the actual jar in class-path. But how do we achieve same with fatjar ?
I would like to get a classpath corresponding to the bundled dependencies included in my nar.
Context :
I've coded a Custom Processor that runs a java application as a command line, like this :
java -cp <classpath> com.my.package.MainClass
My MainClass uses classes from a dependency of my nar, i.e included in the nar.
I'm trying to get the nar classpath using the system property java.class.path :
String myclasspath = System.getProperty("java.class.path");
Problem :
By doing so i'm not having the classpath expected. The jar included in my bundle dependencies does not appear. I get a package com.my.dependency does not exist
A workaround is to put the jar in the nifi "lib" folder directly, but I would like to have just one nar.
Question :
If "java.class.path" doesn't return the classpath included in my nar, how do I get this classpath ?
Using Eclipse I created some parser classes I want to provide to another project as a jar archive for validation purposes. So the parser project look like this:
ParserProject
- src
-- com.package.x
--- ClassA
--- ClassB
- lib
-- external1.jar
-- external2.jar
The ClassA and ClassB use the external jar archives, like Jackson or some Apache commons. To provide the functionality to another project, I exported the entire project as jar archive and executable jar archive (Right click on project > Export... > Java > JAR file > Select all files and "Export generated class files and resources" > Finish).
The jar file is created without any errors. When I use the parserproject.jar in my validation project, I can access all my methods using auto completion, but when I run the validation project, I get a java.lang.ClassNotFoundException: com.fasterxml.jackson.core.JsonParseException.
Now three strange things:
All jackson jars are included in the parser project. Besides, I can run a main() method in the parser project and everything works fine, no ClassNotFoundException occurs.
When I add the parserproject.jar to my validation project in the class path and open the jar archive in the Package Explorer, the parserproject.jar seems to contain all jars it needs.
For the executable jar archive, all required external jars are contained in the MANIFEST.MF (Package Explorer > validation project > Referenced Libraries > + besides parserproject.jar > META-INF > MANIFEST.MF). It looks like this:
Manifest-Version: 1.0
Rsrc-Class-Path: ./ json-20140107.jar jackson-annotations-2.5.4.jar ja
ckson-core-2.5.4.jar jackson-databind-2.5.4.jar commons-io-2.4.jar co
mmons-validator-1.3.1.jar slf4j-api-1.7.5.jar slf4j-log4j12-1.7.5.jar
json-schema-validator-2.2.6.jar jackson-module-jsonSchema-2.4.4.jar
juniversalchardet-1.0.3.jar snakeyaml-1.15.jar commons-beanutils-1.7.
0.jar commons-digester-1.6.jar commons-logging-1.0.4.jar joda-time-2.
8.1.jar jopt-simple-4.6.jar jsr305-3.0.0.jar json-schema-core-1.2.5.j
ar libphonenumber-6.2.jar jackson-coreutils-1.8.jar commons-lang-2.6.
jar guava-16.0.1.jar msg-simple-1.1.jar btf-1.2.jar mailapi-1.4.3.jar
uri-template-0.9.jar
Class-Path: .
Rsrc-Main-Class: com.package.SchemeValidator
Main-Class: org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader
I get the exception if and only if I use the generated jar file in my validation project. In case I get rid of the parserproject.jar and define a dependency to the ecplise parser project instead (Right click on validation project > Properties > Java Build Path > Projects) I do not get the ClassNotFoundException.
So now my question is, how I should export the jar so that every class is found. Thank you!
Eclipse only takes care of the compile-time dependencies while generating a .jar
Since your generated .jar can be moved to virtually anywhere, the dependencies must again be present during execution time.
You have two options:
Execute your jar with the -jar option, while leaving all
dependencies in the same folder. Since your manifest uses "./" as classpath, this means all dependencies must be on the same directory you are executing your jar from. NOTE classpath is relative to the directory you are executing from, not the directory the file is on.
Execute your jar withour the -jar option, and specify the -cp option to point to the dependencies, and the specify the main class.
java -cp "<path to your jar>;<path to dependency 1>;<path to dependency 3>[;...]" <your main class>
You might consider creating a so called fat jar which will contain all the needed classes. For example: http://fjep.sourceforge.net/
If you do not want to go through the hassle of managing all the depencencies by yourself, consider using a build tool like
Maven https://maven.apache.org/ or Gradle https://gradle.org/.