Execute Java Class from JAR on Linux command line - java

I have a working Spring 5 application which works great. BTW, it is Spring 5 CLASSIC, and does NOT use Spring Boot. So, it is not using an embedded Tomcat. I built a traditional WAR file, and deployed it to an existing Tomcat installation.
This project is a multi-maven module project that is made up of several jars, and ending in one WAR file. This runs great. What I am trying to do is execute one class in one of these jars as a standalone piece of code, it's a utility which I want to run nightly. I know it runs in Eclipse just fine, so I know the code is ok.
Based on the readings here, it is very obvious what has to be done.
java -cp SomeJava.jar com.tomholmes.products.myproject.server.util.MyClassUtil
Yes, this 'MyClassUtil' does indeed have a "main" method as you would expect.
public static void main(String[] args) throws Exception {
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
MyClassUtil myClassUtil = new MyClassUtil();
myClassUtil.processBackLog();
}
I have deployed a the WAR file (MyProject.war) is deployed on my linux box, and tomcat is located at: /opt/tomcat the war file is deployed to: /opt/tomcat/webapps
and when we restart tomcat, we have:
/opt/tomcat/webapps/MyProject
/opt/tomcat/webapps/MyProject/META-INF
/opt/tomcat/webapps/MyProject/WEB-INF
/opt/tomcat/webapps/MyProject/WEB-INF/classes <--- no files at all
/opt/tomcat/webapps/MyProject/WEB-INF/libs <--- contains all JAR files including
the one that has my class in it
/opt/tomcat/webapps/MyProject/WEB-INF/libs/my-utils.jar <--- contains the class I want to
execute
I have a script file 'my_util.sh' that looks like this.
export JAVA_ROOT=/opt/java
export JAVA_HOME=/opt/java
export JAVA_BINDIR=/opt/java/bin
export JRE_HOME=/opt/java/jre
export JRE_BINDIR=/opt/java/jre/bin
export PATH=${JAVA_HOME}:${JAVA_BINDIR}:$PATH
export PATH=${JRE_HOME}:${JRE_BINDIR}:$PATH
export PATH=${JAVA_HOME}:${JAVA_BINDIR}:$PATH
datestr=$(date +%Y%m%d_%H%M%S)
export CLASSPATH=/opt/tomcat/webapps/MyProject/WEB-INF/libs/my-utils.jar
export MAINCLASS=com.tomholmes.products.myproject.server.util.MyClassUtil
java -cp ${CLASSPATH} ${MAINCLASS}
To which I get the response:
Error: Could not find or load main class com.tomholmes.products.myproject.server.util.MyClassUtil
Based on the past messages here, of which there are many, and I did research, I should have had this resolved by now. And the way I setup everything should be working, but it is not. I am sure I am missing something small, and I feel like I am almost there. If anyone can make a suggestion or help me out with this, that would be great. I'll keep looking for the answer myself to see if I can get it before this question gets answered.
Thanks!

I just figured this out. As I said, the code itself works great from within Eclipse and is unit tested. When you run a class within Eclipse, you can run a Java Class as a standard Java application, and it will create a Configuration for you with a setup classpath. There is even a button that says "Show Command Line" which showed the full classpath, call to java, any -D arguments, and the actual class.
So, there were two things that I missed:
1) I needed to include ALL the jars that were associated with this class. I know that was mentioned in the comments, and I had tried that because I suspected it was the case.
2) However, what I messed up on was the separator between jars. I tried comma (,) and semi-colon (;) before figuring out that I needed the colon (:). Looking the command-line that Eclipse put together clued me in on that.
It was asked, did I try to execute this on the command-line before I did the script, and the answer is: I did not. I just thought I'd go to the script directly.
BTW ... I figured that having some standalone utilities with my overall web-application wasn't a huge problem. It wasn't worth it to create a whole new project for one utility.
Now that I know this script runs, now I can put it on a cron job.
Thanks for the help. And I hope this helps someone else.

Related

Java Jar file main class run when runs from command line, but not from Windows batch file

I'm re-using a standalone Swing-based Java class which backs up and restores mysql databases.
I've tested running it from a Windows batch file (.bat) on my dev system, and it works there.
But, if I run the batch file on a different Windows , I get a "main class not found" exception.
However, when I run the command directly on the command line, it works.
The command in the batch file to run it is:
java -cp lda-services.jar;bip-services-1.6.0.0-SNAPSHOT.jar;decryptor-1.6.0.0-SNAPSHOT.jar;slf4j-api-1.7.31.jar;commons-io-2.6.jar com.ilcore.util.SosaMaintenanceJFrame
The SosaMaintenanceJFrame class is contained in the lda-services jar.
Here's the error message:
Error: Could not find or load main class com.ilcore.util.SosaMaintenanceJFrame
Caused by: java.lang.ClassNotFoundException: com.ilcore.util.SosaMaintenanceJFrame
The class is definitely in the jar file, as I've extracted it the file and seen it.
Any thoughts on why this would be happening? I need to run inside a batch file so the user can just click on it to run it.
Most likely explanation
Your paths are relative, which means that the batch file isn't going to work unless you run it from the right place. In general, having a batch file that has an invisible rider stapled to it with: "I break in mysterious ways if not run from the appropriate dir" is a crappy batch file - make it better.
Better solution
Or, even better, get rid of it. You don't need batch files to distribute java programs.
Proper ways to distribute java programs:
The modern take is very very different from what you have here: JREs are dead, you must ship an installer that does the whole thing, notably including a java runtime (no longer called a JRE, and one you ship and keep up to date if relevant). That's perhaps a bridge too far for what you're doing here. Relevant tools include jlink.
A slightly less modern take involves jars with manifests:
Your jar file should contain a manifest. This manifest must contain 2 relevant entries:
Class-Path: lda-services.jar bip-services-1.6.0.0-SNAPSHOT.jar decryptor-1.6.0.0-SNAPSHOT.jar slf4j-api-1.7.31.jar commons-io-2.6.jar
and
Main-Class: com.ilcore.util.SosaMaintenanceJFrame
You can use jar's -m switch, or just include the manifest (it's just a file in the jar): it's at META_INF/MANIFEST.MF and it's a text file, each line is an entry, and an entry consists of a key: value pair.
When a jar contains this, just double clicking the jar and running java -jar thejar.jar will then take care of it all: Java will load the stated jars as part of the classpath, and these, crucially, are resolved as paths relative to the directory the jar is in, so it DOES work when you try to launch them from elsewhere, i.e. if you do:
C:
CD \
java -jar "c:\Program Files\MyApp\myapp.jar"
it works fine, whereas that batch script would fail due to being in the wrong place.
Build systems let you define the manifest too, check your build systems docs for how to do this, it'll be easy, and there are tons of tutorials if you search the web for e.g. 'manifest executable jar maven' or whatnot.
You can consider making a shaded jar. But I wouldn't.
A shaded jar takes all your dependencies and packs them into your main jar, so that there is only one jar. There is now no need for a Class-Path entry (the jar you run is obviously already on the classpath and there's nothing else to include) and your app is shipped as 'just' a single jar file.
But this is mostly a red herring: There are no consumer JREs anymore so you've made the user experience from a D- to a D. If you actually care about giving your users a nice experience, there's no getting around an installation process of some sort and once you have that, having the separate jars is no longer a problem. Separate jars are less hairy when signed jars are involved, are much easier to keep up to date, and have a significantly faster turnaround (when you build your stuff and want to ship what you built, shading takes ages, so it's nice to cut that step out). The faster your CI system tells you about failing tests, the better.
Meet in the middle
You don't have to upgrade to modules and the like. What you can do instead is use something like launch4j. The aim is to end up with a zip file along with the installation instructions: Make a dir somewhere. unzip this zip in it. Doubleclick 'myapp.exe'. Done.
The zip would contain an entire JRE, all your jar file deps, and your main app, and an exe file which launch4j made for you, that launches your app using the JRE packed into the jar. This means you know exactly which JRE is being used, and it'll work even on systems that didn't have one installed yet (which, these days, should be all of them - the notion of 'end user downloads a JRE from oracle and the user + oracle work together to keep that thing up to date and security-issue-free', is dead).
The fact that it's an EXE is nice: Now if the user e.g. alt+tabs through their apps, they get your app, with your name, and your icon, instead of 'javaw.exe' with an ugly coffee mug logo.
But when I try running it from the jar file generated by Maven, however, I get a "class not found" exception.
Even if you didn't get that error, you'd get another one unless you'd used Maven Shade, as that's the only way you're going to run that with a single jar. My guess as to why that particular error occurs is that the app class you're attempting to run is in fact in one of the *SNAPSHOT* jars

Java main class autocomplete

If I'm trying to run a java program and I don't know the exact name of the Main class, is there any way to use tab completion to figure it out?
java -cp stackoverflow.jar org.<tab>
stackoverflow serverfault stackexchange
java -cp stackoverflow.jjar org.stackoverflow.<tab>
Main IntegrationTest QuestionAnswerConsole
Something along those lines.
Basically you are asking how to configure shell autocompetion to support java. It is possible. Take a look on this discussion: How does bash tab completion work?
I have to say that this is a good idea not only for discovering the main class but also to complete other command line options and a class path. I'd be glad to use such script if you develop it. Good luck.
EDIT
At least on my Ubuntu file less /usr/share/bash-completion/completions/java exists and therefore some completion should work. You are always welcome to improve the script.
The only way to get the Tab Completion that you are talking about is to use a shell that is "Java aware" or a shell script that provides this feature for the java command. git has a similar feature, so I don't think it's completely impossible.
Edit:
According to this question on SU, it is possible to create an autocompletion script for the bash shell. Since the question on SU is slightly different than what you are asking, I don't see a lot of specific details that relate to this question. However, it looks like a good place to start.
I don't know any easy way to get the autocompletion of classes names if they are hidden in a jar file.
On the other hand, you can add a Manifest to your jar to make it auto-executable (ie., you just have to run java -cp ... -jar stackoverflow.jar) !
In the jar archive, add a META-INF folder, and inside that folder, create a MANIFEST.MF file that reads :
Main-Class: org.stackoverflow.Main
(or whatever your main class really is).
Here is some documentation : Setting an application's Entrypoint
Other answers suggest you solutions but as a sidenote, you don't need it if you provide maintainer of the jar file provides a manifest file with path META-INF/MANIFEST.MF. Then java automatically extracts main class from the manifest and you can run it this way:
java -jar stackoverflow.jar

NoClassDefFoundError in the simplest tomcat web app ever

I have an extremely simple web app for experimenting in java. I have the following java class defined:
UtilDate.java http://www.philbair.org/samples/UtilDate_java.jpg
And a JSP file as follows:
UtilDate.java http://www.philbair.org/samples/getdate_jsp.jpg
The tomcat folders/files look like this (this is not official syntax, just an abstract representation of the folder structure):
UtilDate.java http://www.philbair.org/samples/tree.jpg
mywebapp/WEB-INF/classes/com/udate/UtilDate.class
The jsp file is under the root 'mywebapp' folder (name is inconsequential), and all these are under the webapps folder in tomcat.
After compiling this java class (UtilDate.java), I restart tomcat to make sure it's not hanging on to any old cache stuff.
I get the following...
javax.servlet.ServletException: java.lang.NoClassDefFoundError: UtilDate
when I try to put the jsp page in the browser.
If I replace the uDate() method in the print statement of the jsp file with
out.print("hello");
I get the word hello on the browser output.
This is crude code for testing a simple web app. I am not looking for advice on best practices (although that advice is excellent from what I've seen on this site), I just want to know why this doesn't work. It worked once, and after recompiling the class, it stopped working. I can run the class on my command line and it works just fine (outputs the date as in the main method).
I'm running Windows 7, and tomcat 7.0.34. I'm not using eclipse, or netbeans, etc. at the moment. I'm using a simple text editor, compiling the java class with the command line javac, creating the web folders manually, and placing all the files where they belong myself instead of having maven or ant or anything else build it for me. No war file at the moment. Just working with the files right in the tomcat folders under webapps. I know there are more sophisticated and recommended ways of doing this, but it's only a test for my own understanding with nothing to shield me from what's going on.
Again, this worked once.
Thanks in advance for any help.
you forgot to add the statement package com.udate; at the top of your java class. Add and compile , you should be through

java -cp on Windows problem

Can someone please tell me why this java command in a .bat file won't run my java program on WinXP? I have all my jar files in a folder called lib and my class files in a package folder mypackage.
java -cp ".;.\lib\poi-3.7-20101029.jar;.\lib\jsr173_1.0_api.jar;..." mypackage.MyClassWithMain
I have tried all sorts of things to no avail. I get a ClassNotFound error as soon as the program attempts to use some of the classes in the jar files. So, I think there's something wrong with my -cp option. It does find my main().
I want to give this program to someone who doesn't know a thing about computers, so I want them to be able to double-click the .bat file and go.
I wrote everything on a mac without much problem.
I bet the moderator is going to slap me upside the head for this question, but I did search extensively for an answer to this.
Thanks you!
John
Sorry, I should have put in the entire command line:
java -cp ".;.\libs\jsr173_1.0_api.jar;.\libs\poi-3.7-20101029.jar;.\libs\poi-ooxml-3.7-20101029.jar;.\libs\poi-ooxml-schemas-3.7-20101029.jar;.\libs\resolver.jar;.\libs\xbean.jar;.\libs\xbean_xpath.jar;.\libs\xmlbeans-qname.jar;.\libs\xmlpublic.jar" excelsifter.ExcelSifterController
This is all on one line. I tried / instead of \, but that didn't seem to work. Everything I could find on this indicates that for windows you have to use the backslash.
All the dependencies are here, as far as I know. At least my mac doesn't complain when I use essentially the same command.
My directory containing my .bat file contains the excelsifter package (a folder called excelsifter) and the folder libs with all the jar files in it.
Thanks, John
Your starting string looks ok to me, try to check if there are other dependencies in the libraries you use as #Said mentioned. The best way is to search for class your java cannot find, probably you'll find it declaration in some other library you didn't included in your classpath.

lambdaj installation

we have downloaded jar files for lambdaj and its dependencies which are again jar files.
we do not know how to go about it. we have copied these files in the
C:\Program Files\Java\jre6\lib\ext
have set the class path in environment variales as:
variable: classpath
path: C:\Program Files\Java\jre6\lib\ext
but we do not know how to go further. we want to run some lambdaj programs.
can anyone suggest how to run lambdaj programs?
You would run a Java program that requires lambdaj in exactly the same way you'd run any other java program with an external dependency, i.e. by invoking the java executable passing in the fully-qualified name of the Main class, or the JAR with an appropriate manifest, or by deploying it in a servlet container, etc. Additionally you should be putting the LambdaJ JAR on the classpath for this invocation, not in the lib folder for your entire JVM.
What have you tried so far and why/how is it not working? Your question at the moment is a bit analogous to "I want to use Microsoft Word to view some Word documents, how do I do this?".
Update for comment 1: You said "it's not working". That doesn't help anyone address your problem as it gives no clue what you expected to happen and what you observed, only that they were different. As for where JAR files can be stored - you can put them in any directory, so long as that directory is on the classpath (or you add it to the classpath) of the Java application that runs. The canonical place to put external dependencies is in a folder called lib below the root of your project, but the important thing is that you choose somewhere consistent and sensible.
It sounds like you don't quite grok Java and classpaths yet. If you have followed some tutorials and are still stuck, ask for help to let you understand. Adding more detail to your question, including the layout of your files, the commands you issued, and the response that came back would be useful too.
If you are using Netbeans create a project and right click on the Libraries folder within the desired project. Click Add JAR/Folder...

Categories