Can I use -cp argument twice when executing Java? - java

I am trying to run a Java application which has many dependencies. In the past I have use the following command to launch the application
java -cp "program.jar:jar1.jar:jar2.jar:jar3.jar:[...]" program
However as the list of dependencies have grown, I have moved them into an arguments file, the contents of this file are:
-cp "\
program.jar:\
jar1.jar:\
jar2.jar:\
jar3.jar:\
[...]"
And I am running the application with
java #arguments-file program
Everything up to this point works fine.
Sometimes I end up with beta versions of program.jar, they share all of the same dependencies, but program.jar is renamed program-beta.jar.
So to run the jar the following command would be used
java -cp "program-beta.jar:jar1.jar:jar2.jar:jar3.jar:[...]" program
or more specifically, I would use an environment variable, so that the same script can be used, and the variable would be set to either program.jar, or program-beta.jar, depending on the circumstance
java -cp "$PROGRAM_JAR:jar1.jar:jar2.jar:jar3.jar:[...]" program
Now that I am using an arguments file I was hoping to be able to be able to do something like:
java -cp "$PROGRAM_JAR" #arguments-file program
However using -cp twice causes one of the two to be ignored, resulting in a java.lang.ClassNotFoundException exception.
Is there any way around this that allows me to specify one jar file by name, but abstract away all of the others so that my java command isn't thousands of characters?
This will be running entirely on Linux, so any command line "magic", such as using grep is fine, so long as the resulting code is easily readable

You could just write two bash scripts production.sh and beta.sh that contain a reference on program.jar and program-beta.jar, respectively.
Also, the classpath can contain wildcards (see man-page), so if you can ensure that on disk exists only one of the two versions, you can write it like this:
java -cp "program*:jar1.jar:jar2.jar:jar3.jar:[...]"
In the long term, you might think about building/running it with Maven or Gradle, depending on your requirements.

Related

Why is my environment variable incorrectly processed as a JVM option?

I want to use an environment variable as a JVM option when executing java -jar.
The command I want to execute it:
java -XX:onOutOfMemory='echo test' -jar foo.jar
When I run the above command as is, the jar will run.
(If you don't have the foo.jar, you will get an Error: Unable to access jarfile foo.jar error. But this still means that the option gets used correctly).
But when I create an environment variable containing the JVM option, and run the command using that variable.
OOM="-XX:onOutOfMemory='echo test'"
java $OOM -jar foo.jar
Than I get the following error:
Error: Could not find or load main class test'
It seems like the java command is ignoring the quotes around 'echo test'.
After looking for similar questions on SO and on other websites, I tried various variations of using quotes:
OOM="-XX:OnOutOfMemoryError=\"echo test\""
OOM='-XX:OnOutOfMemoryError="echo test"'
OOM=-XX:OnOutOfMemoryError="echo test"
But they all result in the same error.
An article from Oracle concerning JVM options, mentions using a semicolon:
-XX:OnOutOfMemoryError="<cmd args>; <cmd args>"
But the purpose of the semicolon is to separate multiple commands, not command and arguments. So this does not fix my problem.
Does anybody know how I can correctly move the -XX:onOutOfMemory='echo test' option into an environment variable?
When running java, you should quote $OOM
Example:
java "$OOM" -jar foo.jar
See Why does my shell script choke on whitespace or other special characters? on Unix stackexchange for why this is needed.

convert a java program into a linux command

I have created one java program on my Linux system which indents and formats the given file. I want to make that program work like a command in Linux which will take file names and other options as arguments and then will produce the output. I can do this with a C program by just copying the compiled executable in /bin folder but I don't know how to do it with java.
Sample script that can might further help-
#!/bin/bash
#Set whatever number of arguments you expect for the Java jar you have
ARGS_EXPECTED=3
if [ $# -ne $ARGS_EXPECTED ]
then
echo "[$HOSTNAME]: Usage: `basename $0` filename arg1 arg2"
exit 1
fi
java -cp yourfile.jar com.yourpkg.Driver $1 $2 $3
Save the above content to a file, say test.sh
and use the command to give an executable permission chmod +x test.sh
run like ./test.sh filename arg1 arg2 from current directory where test.sh is
I thing this can be useful for your case: http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/binfmt_misc.txt?id=HEAD
You can simply run a Java class file or jar file with "java" command from command line. Example:
java -jar yourprogram.jar argument1 argument2
If you save this line in a text file saved for example as "script.sh" and then give it the permission for execution you can run it double clicking or from terminal typing ./script.sh in the same folder containing the file script.sh.
You can also produce scripts that use arguments with $1 $2 etc. avoiding the need of editing file.
http://www.linuxquestions.org/questions/linux-newbie-8/how-to-pass-command-line-parameter-to-shell-script-254396/
You can use named parameters, too.
You can also produce a C program for a new command like you suggested that run the "java" command. In this case you can introduce arguments directly from terminal and pass them to java command in the C source.
As others have pointed out it is probably best to use a small shell script to run the Java application. There are several open source products that will help you wrap your Java code to produce a runnable (set of) .jar(s).
If you have correctly separated your business logic from your interface (as you should) then it is probably best if your Java application parses the parameters given on the command line interface. To do this create a separate class for parsing such parameters and calling the classes making up the business logic. Of course this will lead quickly - if not immediately - in writing a parser for Linux like CLI parameters. When this happens you may wish to consider the Apache Commons CLI project.
If you don't want to use any wrapping application/runtime, my method is generally pointing to all the class file containers in the classpath and directly pointing to the class containing the static main method:
java -cp "path_to_jar;path_to_class_folder;etc" "nl.owlstead.stackoverflow.LinuxMain"

How to use java program as a command in cmd?

I have a Java program utility that I want to execute as a command in cmd. I added the location to the PATH variable, but java programs needs to be executed using java - jar "...". How do I shorten that to just the program name, like mysql or netstat?
Update:
I neglected to mention that this java program takes arguments of its own to handle its tasks, so the batch program would need to pass the arguments passed to it over the the java program. I'm not skilled enough in batch to know how to do this.
~Jacob
You could create a batch file or bash script (depending upon your OS) that calls the program with the proper java -jar commands, and simply name the batch (or bash) script whatever you would like to enter as the command. Place this in a directory that is in your PATH variable, and have at it.
Edit: Read this for info on how to parse command line parameters in batch scripts. Just take the parameters passed to the batch file, parse them, and pass them to your jar file with:
java -jar jarfile.jar param1 param2 ...
So for example, lets's assume that your program takes two arguments. Your script could then be as follows:
java -jar jarfile.jar %1 %2
I am not an expert in batch files by any means, so there is probably a more proper way to do this. That being said, why over complicate things?
With Launch4J you can wrap a Java program in a standalone executable file. I'm not going to copy their (long) feature list here, but definite highlights are the numerous ways presented to customize the resulting exe, its small size, the fact that it's open source and its permissive license that allows commercial usage.

Java path problems on Cygwin

I'm trying to compile a Java project under Cygwin using a native Win32 Java.
The Java binaries are correctly found under /cygdrive/c/jdk/bin on my machine.
The following command works fine:
javac -d . ./gnu/kawa/util/PreProcess.java
The PreProcess.class file is generated in ./gnu/kawa/util/. Trying to invoke Java on this fails however:
CLASSPATH=.:$CLASSPATH java gnu.kawa.util.PreProcess \
%java6 +use:com.sun.net.httpserver +enable:XML \
`sed -e 's|\([^ ]*\)|./\1|' < ./patch-source-list`
Error: Could not find or load main class gnu.kawa.util.PreProcess
...
This command was invoked by make, that's where the $CLASSPATH variable is set dynamically. patch-source-list is just a list of class names. The : in the classpath looks suspicious, but I'm not sure how to test ; while not annoying sh.
My only other suspicion is that the native Java is trying gnu\kawa\util\PreProcess, but I think cygwin can transparently handle that.
Any ideas? Thanks for your time.
Another option would be to build your path using the ':' and then fix the results using cygpath. This is probably overkill in your specific situation, but in a general case where you may have references to multiple directories, some of which may be referenced as absolute rather than relative paths, or if you are working with cygwin symlinks, it is much more useful.
$ ln -s /cygdrive/c/development/libraries/ ../libs
$ cygpath -pw /cygdrive/c/development/:.:../libs
C:\development\;.;C:\development\libraries\
so then you'd build your CLASSPATH variable as before, and in the final stage run
CLASSPATH="`cygpath -pw "$CLASSPATH"`" java (whatever)
Remember, the JVM has no idea that you are using the cygwin bash shell.
Two things:
for the classpath locations, use the windows path names. Thus, no "/cygdrive/c/somepath", but "c:\somepath\" ("/" and "\" can be used interchangeably however)
use ';' instead of ':' in the classpath list
This should work:
export CLASSPATH="./gnu/kawa/util/PreProcess.class"
CLASSPATH=".;$CLASSPATH" java gnu.kawa.util.PreProcess
The : in the classpath looks suspicious, but I'm not sure how to test ; while not annoying sh.
You're exactly right: you need to use ; instead of :. As for how to use it — as Mat alludes to above, you need to "quote" the semicolon. Any of these will work:
CLASSPATH=.\;$CLASSPATH java Foo
CLASSPATH=.';'$CLASSPATH java Foo
CLASSPATH='.;'$CLASSPATH java Foo
CLASSPATH=".;$CLASSPATH" java Foo
You can use whichever one you like best. (The first uses a backslash, which quotes a single following character. The second and third use single-quotes, which quote a sequence of zero or more characters. The fourth uses double-quotes, which are like single-quotes except that they still allow the variable $CLASSPATH to be expanded. For that matter, you could also write something like
CLASSPATH=".;"$CLASSPATH java Foo
if you want. See the above link for lots more information about quoting in Bash.)

Basic questions about Java CLASSPATH

Suppose I just created a package "example" and have two classes inside it, "Main" and "Helper".
With the simplest possible compilation (e.g., $javac Main.java Helper.java) I am already able to run it fine as long as I am in the directory containing the example package, by typing this in the command line:
$java example.Main
Questions:
Why would I want to set a CLASSPATH given I can already run the program? I am guessing to be able to type "$java example.Main" from any directory on my machine, but I am not sure.
What happens when I type "java -cp /path/to/your/java/class/file Main" on the command line? Right now I picture there's file containing all the different classpaths, and that command will just add another one to it. Is it the case?
Is there a difference between using "CLASSPATH=/path/to/your/java/class/file" and "java -cp /path/to/your/java/class/file Main" on the command line? How come the second one has the name of the class (i.e. Main) in the end?
Yea, pretty much. That of course assumes you have the path to java in your PATH variable
-cp or -classpath adds it's option (a string) in front of whatever is in your CLASSPATH
Yes, there is a difference. Using CLASSPATH is often more convenient as you tend to set your CLASSPATH once. From then on, java Main is enough to execute the main class. With java -cp /path/to/your/java/class/file Main you have to type the -cp /path/to/your/java/class/file every time.
That being said, both CLASSPATH and -cp or -classpath options usually contain entries pointing to directories containing java libraries used by your program, not the directory of your program itself.

Categories