Why java with -jar ignores -classpath? - java

I have read in docs, especially here
https://docs.oracle.com/javase/7/docs/technotes/tools/solaris/java.html
https://docs.oracle.com/javase/1.5.0/docs/guide/jar/jar.html#JAR%20Manifest
that -classpath is ignored when java is called with -jar option.
In this case, resources are read from manifest file.
Maybe I misunderstanding and the way to set paths from out resource is exist when call java -jar?
If not why so?

This is just how the java programs was coded to behave.
What's important is that you can still use a JAR and specify the class path on the command line. This requires that you make the jar itself part of the class path, and then specify the class, such as:
java -cp javafile.jar fully.qualified.ClassName
Java will run the fully.qualified.ClassName main class, which it will find on the class path (in which the jar file is included).

Related

How do I run a JAR while including another JAR as class path? [duplicate]

What is the difference between running a Java application withjava -cp CLASSPATH and java -jar JAR_FILE_PATH? Is one of them preferred to the other for running a Java application? I mean which one of these ways is more expensive for JVM (according to their machine resources usage)?
Which one will cause JVM to spawn more threads while trying to run the application?
I prefer the first version to start a java application just because it has less pitfalls ("welcome to classpath hell"). The second one requires an executable jar file and the classpath for that application has to be defined inside the jar's manifest (all other classpath declaration will be silently ignored...). So with the second version you'd have to look into the jar, read the manifest and try to find out if the classpath entries are valid from where the jar is stored... That's avoidable.
I don't expect any performance advantages or disadvantages for either version. It's just telling the jvm which class to use for the main thread and where it can find the libraries.
With the -cp argument you provide the classpath i.e. path(s) to additional classes or libraries that your program may require when being compiled or run. With -jar you specify the executable JAR file that you want to run.
You can't specify them both. If you try to run java -cp folder/myexternallibrary.jar -jar myprogram.jar then it won't really work. The classpath for that JAR should be specified in its Manifest, not as a -cp argument.
You can find more about this here and here.
PS: -cp and -classpath are synonyms.
When using java -cp you are required to provide fully qualified main class name, e.g.
java -cp com.mycompany.MyMain
When using java -jar myjar.jar your jar file must provide the information about main class via manifest.mf contained into the jar file in folder META-INF:
Main-Class: com.mycompany.MyMain
java -cp CLASSPATH is necesssary if you wish to specify all code in the classpath. This is useful for debugging code.
The jarred executable format: java -jar JarFile can be used if you wish to start the app with a single short command. You can specify additional dependent jar files in your MANIFEST using space separated jars in a Class-Path entry, e.g.:
Class-Path: mysql.jar infobus.jar acme/beans.jar
Both are comparable in terms of performance.
Like already said, the -cp is just for telling the jvm in the command line which class to use for the main thread and where it can find the libraries (define classpath). In -jar it expects the class-path and main-class to be defined in the jar file manifest. So other is for defining things in command line while other finding them inside the jar manifest. There is no difference in performance. You can't use them at the same time, -jar will override the -cp.
Though even if you use -cp, it will still check the manifest file. So you can define some of the class-paths in the manifest and some in the command line. This is particularly useful when you have a dependency on some 3rd party jar, which you might not provide with your build or don't want to provide (expecting it to be found already in the system where it's to be installed for example). So you can use it to provide external jars. It's location may vary between systems or it may even have a different version on different system (but having the same interfaces). This way you can build the app with other version and add the actual 3rd party dependency to class-path on the command line when running it on different systems.
There won't be any difference in terms of performance.
Using java - cp we can specify the required classes and jar's in the classpath for running a java class file.
If it is a executable jar file . When java -jar command is used, jvm finds the class that it needs to run from /META-INF/MANIFEST.MF file inside the jar file.

how to run kotlin class file and specify one or more library jar files?

I am looking for a way to run the standard class file as produced by Intellij for hello world, using a gradle build with kotlin-stdlib-1.3.11 as the only dependency.
I know I could make a jar file and run that but that is not the question. That question is already answered in many places, but please do not answer with those solutions as that is not the question I am asking.
The simple class file for 'hello world' needs access to the kotlin-stdlib-1.3.11.jar, and I am looking for a way to run the class file and manually specify jars to use for satisfying the dependencies.
I am making notes for team members on why:
java HelloKt
in folder where the class file is located, should give a NoClassDefFoundError and also looking to then show how manually specifying 'run the class but with the kotlin std lib as well' should then work.
I repeat, I am not trying to just get the program to run. I am trying to show how run the standard library is required to run the class file.
This is about the classpath.
The easy answer is to run kotlin instead of java, as that adds the Kotlin support to the classpath automatically:
> kotlin HelloKt
However, if you need to run java, then you'll need to set up the classpath yourself.
The manpage for java says:
The Java runtime searches for the startup class, and other classes used, in three sets of locations: the bootstrap class path, the installed extensions, and the user class path.
The first two are part of the Java installation, and rarely touched, so it's the user class path that you need to look at.
You need it to contain both kotlin-runner.jar and the path for your HelloKt.class file.  The latter could simply be . for the current directory; the former will depend where you've installed Kotlin.  (For example, I installed it using Homebrew, and that jar is currently /usr/local/Cellar/kotlin/1.3.31/libexec/lib/kotlin-runner.jar.)
The manpage continues:
-classpath classpath
-cp classpath
Specifies a list of directories, JAR archives, and ZIP archives
to search for class files. Class path entries are separated by
colons (:). Specifying -classpath or -cp overrides any setting
of the CLASSPATH environment variable.
 
If -classpath and -cp are not used and CLASSPATH is not set, the
user class path consists of the current directory (.).
So there are two ways you can do this: either set the $CLASSPATH environment variable before running java:
> export CLASSPATH="/usr/local/Cellar/kotlin/1.3.31/libexec/lib/kotlin-runner.jar:."
> java HelloKt
Or pass a -classpath or -cp flag:
> java -cp /usr/local/Cellar/kotlin/1.3.31/libexec/lib/kotlin-runner.jar:. HelloKt
(Or, as you say, you could build a jar file which includes the Kotlin support classes as well as your own.  That's probably the best option if you're going to distribute it to machines which might not have Kotlin installed.  But it's not the only option.)
As stated by #gidds, yes it is about the classpath.
The command java HelloKt is telling java the class to run is HelloKt, which is the class Kotlin uses to provide a containing class for an app called Hello, but that command does not identify where the code is that should be run. The code must be specified by the classpath. With the HelloKt.class file in the current directory, then '.' as a classpath with allow the HelloKt class to be found and the code to start, but it will quickly die because the repository 'kotlin-stdlib-1.3.11' as specified in the gradle build, must also be available to supply classes for run time. so
java -cp ".";"<path to stdlib>\kotlin-stdlib-1.3.11.jar"
will successfully run the file. Note, each jar must be a entry in the classpath, just having the folder containing the jar is not enough. On windows ';' separates entries, on mac or linux, use ':'. Each entry can be in quotes, and will need to be only if there are special characters in the path.

Java classpath (-cp) and passing execution to that class

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.

Run java weka utility

I wonder how it is possible to use specific weka utility.
For exaple, I want to use the following utility TextDirectoryToArff.
I tried to run it as follows
javac TextDirectoryToArff.java
TextDirectoryToArff.java:21: package weka.core does not exist
and like following
java -jar /usr/share/java/weka-3.6.6.jar TextDirectoryToArff.java
and in this case weka starts and not the utility.
How it's possible to run the utility.
You first have to compile the TextDirectoryToArff.java file. This is done with javac. The error message in your case just indicates that you did not tell him where to find the weka classes that are required for the compilation. You can tell him where he can find these classes by specifying the classpath (cp) :
javac -cp /usr/share/java/weka-3.6.6.jar TextDirectoryToArff.java
This should create a file TextDirectoryToArff.class in the current directory.
In order to start this utility, you can simply call
java -cp .:/usr/share/java/weka-3.6.6.jar TextDirectoryToArff
Note that you do not use the -jar parameter, because you do not want to run what is inside the JAR, but the TextDirectoryToArff class directly. Therefore, you again have to specify the classpath. In this case, the classpath contains current directory (indicated by the .) (because it contains the TextDirectoryToArff.class file), and, separated from that with a :, the path to the weka JAR.
You'll also have to specify a directory name, so the full command line will be something like
java -cp .:/usr/share/java/weka-3.6.6.jar TextDirectoryToArff your/directory/name

How to add multiple jar files in classpath in linux

Okay, I'm very new to linux and command line, and fairly new to java. I got an internship building a java program. I finally got it done on my machine (windows) and now I have to migrate it to a linux machine to test and then have it run as an executable. I have done much reading and researching on linux and understanding classpaths but it is still all very hard to fully comprehend. It's just not clicking for me yet. Can anyone explain the purpose of classpath in a simplified way using examples? One of the most confusing aspects to me is actually defining the physical path to the jar. Do I start all the way from usr or do I only need to begin from the jvm folder? If it matters, my java program is not located in the jvm folder. Can anyone shed some light for me?
EDIT: thank you guys very much for your help, I can't say that I'm fully in the clear but my understanding of my situation is a lot better.
Say you have multiple jar files a.jar,b.jar and c.jar. To add them to classpath while compiling you need to do
$javac -cp .:a.jar:b.jar:c.jar HelloWorld.java
To run do
$java -cp .:a.jar:b.jar:c.jar HelloWorld
You use the -classpath argument. You can use either a relative or absolute path. What that means is you can use a path relative to your current directory, OR you can use an absolute path that starts at the root /.
Example:
bash$ java -classpath path/to/jar/file MyMainClass
In this example the main function is located in MyMainClass and would be included somewhere in the jar file.
For compiling you need to use javac
Example:
bash$ javac -classpath path/to/jar/file MyMainClass.java
You can also specify the classpath via the environment variable, follow this example:
bash$ export CLASSPATH="path/to/jar/file:path/tojar/file2"
bash$ javac MyMainClass.java
For any normally complex java project you should look for the ant script named build.xml
The classpath is the place(s) where the java compiler (command: javac) and the JVM (command:java) look in order to find classes which your application reference.
What does it mean for an application to reference another class ? In simple words it means to use that class somewhere in its code:
Example:
public class MyClass{
private AnotherClass referenceToAnotherClass;
.....
}
When you try to compile this (javac) the compiler will need the AnotherClass class. The same when you try to run your application: the JVM will need the AnotherClass class.
In order to to find this class the javac and the JVM look in a particular (set of) place(s). Those places are specified by the classpath which on linux is a colon separated list of directories (directories where the javac/JVM should look in order to locate the AnotherClass when they need it).
So in order to compile your class and then to run it, you should make sure that the classpath contains the directory containing the AnotherClass class. Then you invoke it like this:
javac -classpath "dir1;dir2;path/to/AnotherClass;...;dirN" MyClass.java //to compile it
java -classpath "dir1;dir2;path/to/AnotherClass;...;dirN" MyClass //to run it
Usually classes come in the form of "bundles" called jar files/libraries. In this case you have to make sure that the jar containing the AnotherClass class is on your classpaht:
javac -classpath "dir1;dir2;path/to/jar/containing/AnotherClass;...;dirN" MyClass.java //to compile it
java -classpath ".;dir1;dir2;path/to/jar/containing/AnotherClass;...;dirN" MyClass //to run it
In the examples above you can see how to compile a class (MyClass.java) located in the working directory and then run the compiled class (Note the "." at the begining of the classpath which stands for current directory). This directory has to be added to the classpath too. Otherwise, the JVM won't be able to find it.
If you have your class in a jar file, as you specified in the question, then you have to make sure that jar is in the classpath too , together with the rest of the needed directories.
Example:
java -classpath ".;dir1;dir2;path/to/jar/containing/AnotherClass;path/to/MyClass/jar...;dirN" MyClass //to run it
or more general (assuming some package hierarchy):
java -classpath ".;dir1;dir2;path/to/jar/containing/AnotherClass;path/to/MyClass/jar...;dirN" package.subpackage.MyClass //to run it
In order to avoid setting the classpath everytime you want to run an application you can define an environment variable called CLASSPATH.
In linux, in command prompt:
export CLASSPATH="dir1;dir2;path/to/jar/containing/AnotherClass;...;dirN"
or edit the ~/.bashrc and add this line somewhere at the end;
However, the class path is subject to frequent changes so, you might want to have the classpath set to a core set of dirs, which you need frequently and then extends the classpath each time you need for that session only. Like this:
export CLASSPATH=$CLASSPATH:"new directories according to your current needs"
For linux users, you should know the following:
$CLASSPATH is specifically what Java uses to look through multiple directories to find all the different classes it needs for your script (unless you explicitly tell it otherwise with the -cp override). Using -cp (--classpath) requires that you keep track of all the directories manually and copy-paste that line every time you run the program (not preferable IMO).
The colon (":") character separates the different directories. There is only one $CLASSPATH and it has all the directories in it. So, when you run "export CLASSPATH=...." you want to include the current value "$CLASSPATH" in order to append to it. For example:
export CLASSPATH=.
export CLASSPATH=$CLASSPATH:/usr/share/java/mysql-connector-java-5.1.12.jar
In the first line above, you start CLASSPATH out with just a simple 'dot' which is the path to your current working directory. With that, whenever you run java it will look in the current working directory (the one you're in) for classes. In the second line above, $CLASSPATH grabs the value that you previously entered (.) and appends the path to a mysql dirver. Now, java will look for the driver AND for your classes.
echo $CLASSPATH
is super handy, and what it returns should read like a colon-separated list of all the directories you want java looking in for what it needs to run your script.
Tomcat does not use CLASSPATH. Read what to do about that here: https://tomcat.apache.org/tomcat-8.0-doc/class-loader-howto.html
Step 1.
vi ~/.bashrc
Step 2. Append this line on the last:
export CLASSPATH=$CLASSPATH:/home/abc/lib/*; (Assuming the jars are stored in /home/abc/lib)
Step 3.
source ~/.bashrc
After these steps direct complile and run your programs(e.g. javac xyz.java)

Categories