can someone tell me please what are the difference between Rsrc-class-Path and Class-Path sections of a runnable-jar's mannifest file?
Now I take them as granted as generated by Eclipse, but I'd like to understand how it works.
What I think based on how Eclipse generates code seems that the first is about jars my app needs, the second is always .. But I have no clues what folder . refers to.
The Class-Path attribute. This is a standard attribute defined by the JAR file specification. It contains a list of relative URLs for that will be included on the runtime classpath when you run the JAR using java -jar ....
This provides a way to add external JARs (and directories) to the runtime classpath. The entries must be relative, and are resolved relative to the directory containing the main JAR. (For security reasons ...)
The Rsrc-class-Path attribute is non-standard. This is used by Eclipse's "jars-in-jar" launcher. A typical manifest looks like this:
Manifest-Version: 1.0
Rsrc-Main-Class: com.abc.Master
Main-Class: com.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader
Rsrc-Class-Path: ./ lib/xyz.jar
where com.abc.Master is your apps (real) main class, and lib/xyz.jar is a relative URL for a JAR file that is nested within this JAR.. You will also see that the JAR contains the ".class" file for JarRsrcLoader.
This is what happens with you run java -jar this.JAR arg1 arg2.
The JVM is created
The JVM jar loader opens the JAR, reads and parses the MANIFEST.MF above.
It loads the JarRsrcLoader class given by Main-Class/
It calls the above classes main method, passing it ["arg1", "arg2"]
The JarRsrcLoader examines the manifest, and extracts the Rsrc-Class-Path and Rsrc-Main-Class.
Then JarRsrcLoader creates a special classloader that knows how read JARs embedded within the current JAR. The classpath for this classloader is "./" follows by "lib/xyz.jar", where these URLs are resolved within the outer JAR file.
Then JarRsrcLoader loads the class com.abc.Master using the special class loader.
Then JarRsrcLoader calls the main method for com.abc.Master, passing the same string array containing the arguments.
Finally, the application runs.
In short, Rsrc-Class-Path is an attribute that the JarRsrcLoader class understands, and uses to construct the actual application classpath.
In this context, the Class-Path: . attribute serves no real purpose. Everything needed to run JarRsrcLoader will be in the JAR.
As a final note, the SpringBoot loading mechanism is similar, but it uses a different non-standard attribute for the application's main class, and puts the application's resources (e.g. JARs) into a particular directory ("/boot-inf") within the main JAR.
Related
I have recently come across of a scenario where we would have a empty manifest jar (i.e. jar with just META-INF/MANIFEST.MF having Class-Path attribute) referring to other jar file somewhere on file system. I believe this manifest jar has been created to act like a soft link to a specific version jar so that consumer need not deal with minor version changes of implementing jar.
manifest.jar:-
----META-INF/
--------MANIFEST.MF
Actual version specific implementation jar (test-1.1.jar):-
----META-INF/
--------MANIFEST.MF
----test/
--------Test1.class
Including manifest.jar in classpath in application does not load actual implementing jar and hence I see ClassNotFoundException.
Any idea why it does not load and how can I get this jar loaded as expected?
Update:
I tried this with a sample executable jar and it (sun.misc.Launcher$AppClassLoader) seems to load the class as expected. Referring just manifest.jar in Class-Path in-turn includes the actual implementing jar.
Also, I found that we do have a legacy custom class loader for the project, not sure if that causing this weird behavior.
By default, shouldn't it take care of the referencing of other jars from manifest.jar?
Is there a way to check whether this custom class loader is actually referencing jars mentioned in Class-Path?
When using java -jar somejar.jar, then the classpath is set up as follows:
Environment variable CLASSPATH is entirely ignored.
The -cp / -classpath switch is entirely ignored.
The Class-Path entry in the manifest of the jar you specified explicitly, i.e. somejar.jar is read, split on spaces, any relative paths are resolved relative to the directory the jar is in, and that is used as classpath.
The Class-Path entry in any other jar file, including jar files listed in the Class-Path entry of somejar.jar is entirely ignored.
The Main-Class entry in the manifest of somejar.jar is read, the string found there is loaded as class, and its main method is invoked. Any Main-Class entries in any jars you mentioned in the Class-Path entry is entirely ignored.
In other words, this proxy concept cannot really work. The jar you put in java -jar thejar.jar must have the Main-Class attribute and must list all the required jars to run that application in its Class-Path attribute.
If you use this instead: java -cp somejar.jar:lib/dep1.jar:lib/dep2.jar:etc com.foo.Main, then the manifest is entirely ignored, and thus whatever you set up in any manifest's Class-Path or Main-Class attribute is irrelevant here.
I think what you are observing may be explained by the JAR file spec section on the Class-Path attribute.
Specifically:
"Currently, the URLs must be relative to the code base of the JAR file for security reasons. Thus, remote optional packages will originate from the same code base as the application."
"Each relative URL is resolved against the code base that the containing application or library was loaded from. If the resulting URL is invalid or refers to a resource that cannot be found then it is ignored."
So, if the Class-Path attribute in the MANIFEST.MF file for manifest.jar uses absolute URLs, or URLs starting with "/", or if they do not resolve relative to the location of manifest.jar itself, the corresponding JARs, etc won't be added to the internal classpath. They are silently ignored.
This would happen if you just copied the manifest.jar into your project ...
I am using web application in which jar files are there in lib folder. In web.xml, servlet class is provided.how does web.xml knows in which jar file that specific class is there???
This is handled by the classloader mechanism:
A JAR file usually contains a "manifest" -- a file which lists the
contents of the JAR. The manifest can define a JAR-class-path, which
further extends the class path (but only while loading classes from
that JAR). Classes accessed by a JAR-class-path are found in the
following order:
In general, classes referenced by a JAR-class-path entry are found as
though they were part of the JAR file. The JAR files that appear in
the JAR-class-path are searched after any earlier class path entries,
and before any entries that appear later in the class path. However,
if the JAR-class-path points to a JAR file that was already searched
(for example, an extension, or a JAR file that was listed earlier in
the class path) then that JAR file will not be searched again. (This
optimization improves efficiency and prevents circular searches.) Such
a JAR file is searched at the point that it appears, earlier in the
class path. If a JAR file is installed as an extension, then any
JAR-class-path it defines is ignored. All the classes required by an
extension are presumed to be part of the SDK or to have themselves
been installed as extensions.
source
The magic word in this case is class path .
The book "Learning in Java" describes this nicely.
https://www.safaribooksonline.com/library/view/learning-java/1565927184/ch03s03.html
Java CLASSPATH variable, holds a list of locations that can be searched for packages containing Java class files. The Java interpreter and Java compiler use CLASSPATH when searching for packages and classes on the local host.
Just like we add directory location to path variable in dos/linux we can specify the location of jars in classpath variable.
Java also provided some default locations that it will look in. For example the ext directory. The path of ext folder in java on a windows installation may look like "C:\Program Files\Java\jdk1.6.0\jre\lib\ext" .
You can put your jars in the ext folder and class files in them will be automatically located..
A location on the class path can be a directory name or the name of a class archive file. Java supports archives of class files in its own Java archive ( JAR) format . This allows large groups of classes to be distributed in a single file; the Java interpreter automatically extracts individual class files from an archive,when required.
The Java interpreter knows how to find core classes, which are the classes included in every Java installation. For example , the classes in the java.lang, java.io etc. Their location need not be put in class path, the Java interpreter can find them by itself.
To find other classes, the Java interpreter searches the locations on the class path in order.
For example consider a search for the class vehicle.cars.Porche. Searching the class path directory /usr/lib/java means the interpreter looks for an individual class file at /usr/lib/java/vehicle/cars/Porche.class. If the class is present in a jar file say CompanyVehicle.jar then the compiler will be looking for vehicle.cars.Porche.class in the CompanyVehicle.jar.
To sum it up , Java has a list of location it knows it should look into. Other than that, it will look into locations you provide in classpath variable.
if you want to understand more about how classpath, classloaders work behind the scene check out the links below
How JVM starts looking for classes?
http://javapapers.com/core-java/java-class-loader/
https://en.wikipedia.org/wiki/Classpath_(Java)
I am developing a software with an automated update function, which works even when the user has no write permission to the installation folder.
For that, the application analyses the classpath and checks whether there are newer JAR files for the entries in the classpath in a separate user directory. In that case the application generates a revised classpath and starts a new VM with that classpath.
My problem arises when the java default class loader changes the order of the JAR files by inserting libraries, which are mentioned in a manifest file.
I will give you a simplified example, which lacks a lot of details and does not make sense for a real update scenario, but it will explain my problem. So let's assume I am not allowed to change the contents of main.jar.
I start the application with
java -jar main.jar
main.jar has a proper MANIFEST file, which contains the Main-Class and a Class-Path, referencing the additional libraries c.jar and d.jar.
So the complete Class-Path during runtime would be:
main.jar, c.jar, d.jar
The order is important, since main.jar overrides classes from c.jar.
Now the application finds out that there are updates available, which require the additional libraries a.jar and b.jar.
So the application now calculates the new classpath:
main.jar, a.jar, b.jar, c.jar, d.jar
Again, the order is important, since a.jar overrides classes from d.jar.
Now the application starts a new VM with an explicit classpath:
java -cp main.jar:a.jar:b.jar:c.jar:d.jar <Main-Class>
Unfortunately, java alters the class-path by inserting the libraries, referenced by the MANIFEST of main.jar, at the position of main.jar. So the effective classpath would be:
main.jar, c.jar, d.jar, a.jar, b.jar, c.jar, d.jar
-------- ------------ --------------------------
CP MANIFEST CP
With that order, c.jar now has a higher priority than a.jar. And the overriding classes from a.jar will not get loaded.
The obvious solution to this problem would be to provide a main.jar without a manifest.
But this would lead to other problems, I would like to avoid.
My question is: Is there a way to disable the behaviour of java to include the referenced JAR files from a MANIFEST while searching the classpath?
Just a quick and untested idea. If you are able to dynamically load your updated jars at runtime, instead of specifying them at the command line, you could create a URLClassLoader for these jars and explicitly load your classes using that custom class loader:
URL[] jarurls = new URL[]{jar1,jar2,...};
URLClassLoader customCL= new URLClassLoader (jarurls, getClass().getClassLoader());
Class clazz = Class.forName ("com.blablabla.MyClass1", true, customCL);
MyClass1 instance = (MyClass1)clazz.newInstance();
You could load every jar which resides in your user folder into that class loader. When instantiating a class you would prioritize your custom class loader and failover on the default one.
This certainly adds a complexities/risks to your design since classes would now need to be instantiated by reflection.
I am relatively new to Java but have a fair understanding about how the class path works with respect to providing a list of folder and jars that make classes available to other classes.
I have compiled a JAR (lets say example.jar) that has a main function where execution normally begins. Sometimes I want execution to begin in a different class (lets say myAlternateClass.java), with its own main. I can achieve this by doing using the -cp argument when executing the jar, for example;
java -cp example.jar myAlternateClass
This works as I require but I am unsure of what exactly is happening here.
I'm not 100% sure on exactly what you're looking for, but I'll give it a shot.
There are two ways to use a jar file. If the jar file has a Main-Class specified in its META-INF/MANIFEST.MF file, then you can load java with the jar file and execution will start in the main method of that class.
java -jar example.jar
On the other hand, a jar file can simply be loaded onto the classpath, which makes all of the classes within it available for use. This is the example you are giving:
java -cp example.jar org.somewhere.MySecondClass
The -cp example.jar puts all of the classes within the jar on the class path and the second argument org.somewhere.MySecondClass gives the class at which execution should begin. This second argument would have to be within the jar since specifying a classpath overrides the default (which is just the current directory). In this case, java ignores any Main-Class specified in the MANIFEST.MF file of the jar (if one even is specified).
Multiple jar files as well as directories of java files not in a jar can be specified by putting colons between them. So,
java -jar example.jar:. MyClass
could launch MyClass from the current directory, but place example.jar on the classpath so that MyClass could create instances of whatever classes are available within example.jar.
Can you see anything wrong with the below code? It's a bat file and I'm trying to set some dependency classes before executing my jar (jdbc oracle driver).
set CLASSPATH=lib\dbdriver.zip;%CLASSPATH%
java -jar sql2java.jar test.properties
pause
I always get class not found exception (the class is in the zip I'm trying to add in the classpath).
I even tried this by executing as admin, but to no avail
set CLASSPATH=lib\dbdriver.zip;%CLASSPATH%
java -jar %~dp0sql2java.jar %~dp0test.properties
pause
The class is inside the zip file (path \oracle\jdbc\OracleDriver) and I'm trying to retrieve it with
jdbc.driver=oracle.jdbc.driver.OracleDriver
Check this doc about java (Java application launcher).
For the -jar option it says -
Executes a program encapsulated in a JAR file. The first argument is the name of a JAR file instead of a startup class name. For this option to work, the manifest of the JAR file must contain a line in the form Main-Class: classname. Here, classname identifies the class with the public static void main(String[] args) method that serves as your application's starting point.
When you use this option, the JAR file is the source of all user classes, and other user class path settings are ignored.
So you either need to package everything in your jar (sql2java.jar) or don't use the -jar option with java launcher command.
You can use java -jar to execute it and define a classpath for the application within the jar's manifest file. See the Java Tutorial on jars for how to set a classpath for the application at http://docs.oracle.com/javase/tutorial/deployment/jar/downman.html
It says:
For example, in a typical situation an applet is bundled in a JAR file
whose manifest references a different JAR file (or several different
JAR files) that serves as utilities for the purposes of that applet.
You specify classes to include in the Class-Path header field in the
manifest file of an applet or application. The Class-Path header takes
the following form:
Class-Path: jar1-name jar2-name directory-name/jar3-name
By using the Class-Path header in the manifest, you can avoid having
to specify a long -classpath flag when invoking Java to run the your
application.