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 ?
Related
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.
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 ?
My groovy (ant1.8.1 for build)project was build in jdk1.5 and now i am making some changes and upgrading to jdk1.8 .
When i build and make jar ,it works fine from the folder where code is in eclipse.
but when copy that jar and try to run get below-
Error :could not find or load main class Run.
Rest all jars are as below(same as used in jdk5)
groovy-all-1.6.9.jar
commons-codec-1.4.jar
commons-httpclient-3.0-rc4.jar
commons-logging-1.1.1.jar
commons-net-2.0.jar
lib/log4j-1.2.15.jar
jtds-1.2.5.jar
httpmime-4.0.1.jar
When you say "run", I understand you mean something like:
java -jar MyJar.jar
You should check your MANIFEST file inside the JAR and make sure your Main-Class is defined as you need.
Main-Class: pakage.name.ClassName.class
And the corresponding pakage.name.ClassName.class exists in your jar file (or is included in any of the libraries accessible through classpath)
Check this anyway Setting application entry point
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/.
After I build this project from an ant file, I recieve a jar that contains all of the classes I built. When I try to run this jar, I get the following error:
Exception in thread "main" java.lang.NoClassDefFoundError: javax/media/j3d/SceneGraphObject
This error indicates that a one of the jars, specifically the j3dcore.jar from java3d, I am using can not be found. However, this jar is on the classpath when compiling through the ant build into the class files.
Why can this class not be found at runtime, but it is found at compile time? Do I have to manually change my classpath in my shell when running the jar as well as change it in the ant build?
If I add the jars to my classpath using java -cp j3d/*.jar -jar idv.jar
I get the error Error: Could not find or load main class j3d.j3dutils.jar
Do I have to manually change my classpath in my shell when running the jar as well as change it in the ant build?
Yes, absolutely. Making a class available at compile-time doesn't embed the class into your output or anything like that. It just makes it available to the compiler (to find out what methods are present etc).
If I add the jars to my classpath using java -cp j3d/*.jar -jar idv.jar
Yes, it would - because that's being expanded into:
java -cp j3d/foo.jar j3d/bar.jar ... -jar idv.jar
It's not clear to me whether -cp is meant to work at all with -jar, given this documentation:
When you use this option, the JAR file is the source of all user classes, and other user class path settings are ignored.
One option is to set the classpath within the manifest of the jar file itself. For example:
Class-Path: j3d/foo.jar j3d/bar.jar
Another would be to ignore the -jar command-line option for now, and use:
java -cp j3d/*:idv.jar your.class.name.Here
Note the * rather than *.jar, as documented:
As a special convenience, a class path element containing a basename of * is considered equivalent to specifying a list of all the files in the directory with the extension .jar or .JAR (a java program cannot tell the difference between the two invocations).