How do I set a classpath for multiple jar files? [closed] - java

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I don't want to make the code messy Is there a way to set a classpath to exexute my java program, because my jar files are from different directories they are like 30 jar files

Various strategies are available.
runnable jars
A 'runnable jar' can be run with java -jar thejar.jar, and depending on your OS and how you installed java on it, just doubleclicking the jar in the user interface works too (and does the same thing as java -jar thejar.jar.
This 'maps' to java -cp someclasspath myapp.pkg.Main as follows:
The thejar.jar is scanned for the file META-INF/MANIFEST.MF. This is a text file containing key/value pairs separated by a colon.
java.exe looks for an entry named Main-Class and takes the value of that as myapp.pkg.Main. It then looks for an entry named Class-Path and parses the value for that to use as classpath. This classpath string is space-separated, and all paths are relative to the same path the jar you run is in.
Thus, if you have, say:
/usr/local/myapp/myapp.jar
/usr/local/myapp/deps/guava.jar
/usr/local/myapp/deps/jooq.jar
and the myapp.jar file contains the class file /my/app/Main.class, then this needs to be in the manifest:
Main-Class: my.app.Main
Class-Path: deps/guava.jar deps/jooq.jar
and then you can just java -jar myapp.jar, from anywhere (you don't need to be in the /usr/local/myapp directory. Note that -jar automatically uses this mechanism for the classpath. $CLASSPATH and the -cp option are completely ignored.
So, how do you get that file in there? Well, build systems tend to have ways to let them automatically build up the list of 30-some jar files, but your question doesn't explain how your build works, so you'd have to peruse the documentation of maven or gradle or whatnot. Alternatively, script it together with some bashisms if you've handrolled your build (not recommended).
The * include
Another option is to use the * include option. java.exe itself can pick up every jar file in a given directory (not recursively, though - it won't pick up jars in subdirectories). The problem is, on every OS except windows, it is the job of the shell to unpack * into a list of files, so your shell will do that. Which you don't want it to do, because it unpacks using spaces, and the -cp command line option doesn't work that way. Thus, on ALL OSes except windows, you must put the classpath in single quotes in order to suppress the shell's feature that it will detect that * and unpack it.
Thus, with the same setup as above, you can also run:
java -cp 'myapp.jar:deps/*' my.app.Main
and that also works, though you'd have to [A] be running some posix based OS and [B] have to be in the /usr/local/myapp directory. On windows, I think java -cp myapp.jar;deps/* my.app.Main will work.
Note that it has to be just *. Not *.jar.
Use your build tooling
Most build tooling has a 'run' command that includes the proper (runtime) dependencies automatically. This includes IDEs.
Shading
Also called 'fatjars' or 'uberjars' or 'striped jars', this is the notion of taking all class files in all the jars needed at runtime and packing them into one gigantic jar file.
This is a dumb idea for many reasons:
It's slow as molasses; your average serious project has hundreds of dependencies, so that one gigantic jar is half a GB in size. Just writing that out to disk takes time, let alone transferring it around to your deployment and test servers. You want the time and effort it takes to make a change in your code and see your test results to be as low as it reasonable, and the fatjar concept fundamentally clashes with this notion.
signed jars do not work well with this model.
It makes updating deps harder.
You could in theory consider the notion of 'I make a single fat jar and now I can just zip that up and mail it around to folks', and thus conclude that fat jars are nice, but this is no good either:
gmail and other services will flat out strip that jar right out of your email because it's generally used to convey malicious code. Mailing jars around isn't a good deployment model
The current only official supported style of distributing java apps is NOT by just shipping jar files around; there are no JREs anymore (JRE8 was the last official release. Some third parties still make them. Doesn't make it any more officially supported though). You're supposed to ship an installer which takes care of setting up a treeshaken JRE and keeping it up to date. Various tools make this possible such as jmod and jlink. These aren't used much, but that's probably mostly because the vast majority of java apps run on servers and aren't themselves client apps meant to run directly on end user hardware. See for example minecraft or eclipse: Java-written desktop apps. Neither 'ships as a jar file' and certainly not as fat jars.
"Just shade it / make a fatjar" is extremely common advice. You are now armed with sufficiently knowledge to know it is bad advice.

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

Decompile JAR, Modify it, Recompile again?

I have an older Jar file which I have to modify in order to get it to work.
I have already decompiled it with jd-gui and changed the parts that needed change. I would now like to know what I have to do in order to get a jar back?
I guess that I have to add the libraries, but there is no extra lib directory.
How do I put everything together?
#Stephen C
"Therefore, when you make your changes and recompile, the resulting ".class" file could have unexpected "
How exactly is this relevant? I only want the function of the Jar file and not a 1:1 copy of it.
The Jar file is unsigned. What exactly would be the reason to sign a Jar file anyway? If I start it from another program?
If you are missing dependencies after decompiling the entire jar, that could mean the jar did not include them. When you create a jar, you can choose not to include dependencies in it, but this assumes that they will be available on the classpath at runtime. Check if there are any relationships with other jar files on the classpath when you run the original jar. See this answer for details on how to achieve this. Once you have identified all the dependencies, you can easily compile the jar from an IDE such as IntelliJ/Eclipse or from command line.
As a side note, if the changes you would like to make to the jar are minor or isolated I recommend editing the bytecode (much easier for small edits). I prefer this bytecode editor.
If decompilation fails on some parts of the code, the functionality of the jar will not be restored upon recompilation. In this case, instead of going through all available decompilers and hoping that you can decompile that code, I suggest you identify the set of classes which you are trying to edit, modify the rest of the classes such that they compile and do not have any side effects on the classes which are of interest to you (do not delete anything that is referenced by these) and compile the jar (even if this isn't the jar you want). You can then extract only the class files which you wanted to modify from the jar, and overwrite them in the original jar. This will only work if your changes didn't have any side effects.
If you are asking how to create a JAR:
You can do it using a build tool such as Maven, Gradle, Ant and so on.
You can do it using a Java IDE
You can do it using the command line jar tool; see How to create a JAR file from the official Oracle Java tutorials.
But it there is no guarantee that what you are doing will actually work. Here are a couple of the problems.
If the original JAR file was signed, you won't be able to re-sign the new JAR ... unless you have the private key that was used when signing.
Decompilation is often inaccurate. There is no guarantee that the Java code that it produces is a correct representation of the original class. Therefore, when you make your changes and recompile, the resulting ".class" file could have unexpected / unwanted differences relative to the original.
I guess that I have to add the libraries, but there is no extra lib directory.
I'm not sure what you mean by that. The (new) dependencies of the JAR don't necessarily need to be in the JAR itself. It depends on what kind of JAR it is; e.g. is it a so-called "executable" JAR that you launch using java -jar my.jar ....
Use jar plugins for gradle or maven for example, or build it with intelliJ, here's an answer in another post: How to build jars from IntelliJ properly?
Since I had trouble getting the .jar file completely recompiled and working with my application, I wanted to share here the steps that were missing in the previous answers.
In my specific case, to make things as easy as possible, I wanted to only modify one .java file in the result of the decompilation and update it directly in the .jar file.
I started from the answer from Paulo Ebermann on a similar question, and I ended up with:
javac -source 8 -target 8 -d classdir -cp \
"\
path/to/dep1.jar; \
path/to/dep2.jar; \
path/to/myoriginal.jar \
" path/to/my/java/class.java
cd classdir
jar -uf path/to/myoriginal.jar path/to/my/java/class.class
Notes:
the path/to/myoriginal.jar which is in the path of the dependencies, this forces the java compiler to look for missing classses in the original .jar file rather than in the local decompiled code (which would require recompilation and more dependencies) and therefore I have intentionally left out the . path in the list of class paths
the -source 8 -target 8 which were required to force the class to compile for the appropriate version of the runtime machine.
the -uf which indicates that the jar tool should only update the file with the provided class

Steps for installing JRE to users' pc manually

I am currently working on a jar file and I am going to distribute it soon. I was looking forward to distributing it for user without him needing installing JRE to run my app. I am well aware of tools like Jar2Exe but those tools inject some additional content to the executable and I am not going to buy license for that! So I am working on a solution. And I have the idea, So I will create a C file with this content ( I am using C Language power in nativity! ) :
#include <stdlib.h>
int main() {
system("java -jar test.jar");
return 0;
}
And I will put the jar file and the compiled file of the C file above and java.exe located in my JRE installation dir in a same directory and this should work but surely there are a bunch of dlls that java.exe depends on them! A list of those dependencies to put them all in a directory and make the idea work will be very helpful:)
Please Note : I don't want to create an executable from the JAR file, I only want the user need not INSTALL JRE.
EDIT
I realized the above approach isn't practical, so I am going to install JRE behind the scenes ( my only goal is that the user doesn't find out whats going on in the installation process ) I am going to copy required files to ProgramFiles/jre_version but what in addition should I do? In the other words what does the JRE installer do under the hood?
Why do you think those other companies want you to pay money for their product?
Hint: because it ain't that easy.
The point is that not only need your JAR file. You need to make each and any class that your code will be using available to the JVM you are starting up.
You know, exactly that other stuff that those commercial tools are backing into their EXE files; the stuff that you think makes their EXE files so big and slow. That is the stuff that you need to run your java classes ...
The JRE alone comes with megabytes and megabytes of java classes, packaged up in various JARs. And any 3rd party library that you are using will be required too.
So, of course, when you are able to find all classes that will be loaded when your code is executed, then you could manually package that into a single JAR. But as said: if that would be an easy undertaking, those other people wouldn't have much of a business case. And of course: as soon as reflection and Class.forName() kicks in, you are completely broken. Because you can't predict at all, which classes will be used at runtime then.

Where do you use Java code? Trouble sending .jar files [closed]

Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 10 years ago.
Improve this question
Apologies for the generic nature of this question.
I have been learning Java for a few months now, and I've created a few simple projects which have some functionality. I wanted to send the projects to a friend and I'm running into countless errors and struggles. Passing a .jar file is causing "Main class not found" errors when they try to open it.
I tried using third party software to wrap the .jar files into an .exe file and the same errors still persist.
Beyond that, I'm convinced that passing around .jar files wrapped into .exe files via third party software is NOT how Java was intended to be used. I've read two books on Java and they all talk about structuring the language, but I'm confused about WHERE I'm supposed to be using this code because it has become painfully obvious that it is NOT intended to be passed around in file format.
Is this a server programming language? Used on the back end of websites mainly? I'm not sure where one would be using the code written in Java.
You should build an executable jar.
Check here and here.
You need to create your JAR file as an executable JAR file if you want someone to be able to run it. That is how you send around Java executables. Look here for more info:
How do I create executable Java program?
I'd recommend you spend some time with http://docs.oracle.com/javase/tutorial/. Java program basically runs on top of virtual machine (not directly on your physical machine). Java program is compiled into *.class files, and a collection of *.class is commonly bundled into a *.jar file.
If you want to run your java program, one common way is to execute the java virtual machine runtime (JRE) and specify your jar package to be loaded into the classpath, eg:
java -jar /path/to/my.jar
Your jar file has to be packaged properly such that it indicates what is the main class (if any)
Packaging jar into exe is possible, but is not best practice. Java paradigm is a write & compile once -- runs everywhere.
from Oracle:
If you have an application bundled in a JAR file, you need some way to indicate which class within the JAR file is your application's entry point. You provide this information with the Main-Class header in the manifest, which has the general form:
Main-Class: classname
The value classname is the name of the class that is your application's entry point.
Recall that the entry point is a class having a method with signature public static void main(String[] args).
After you have set the Main-Class header in the manifest, you then run the JAR file using the following form of the java command:
java -jar JAR-name
The main method of the class specified in the Main-Class header is executed.
Setting an Entry Point with the JAR Tool:
The 'e' flag (for 'entrypoint'), introduced in JDK 6, creates or overrides the manifest's Main-Class attribute. It can be used while creating or updating a jar file. Use it to specify the application entry point without editing or creating the manifest file.
For example, this command creates app.jar where the Main-Class attribute value in the manifest is set to MyApp:
jar cfe app.jar MyApp MyApp.class
You can directly invoke this application by running the following command:
java -jar app.jar
If the entrypoint class name is in a package it may use a '.' (dot) character as the delimiter. For example, if Main.class is in a package called foo the entry point can be specified in the following ways:
jar cfe Main.jar foo.Main foo/Main.class
Setting an Application's Entry Point
For a quick answer, libraries are bundled in jars, which are then added to the classpath of another application.
Pretend you invent a new sort algorithm which is faster than all the others. You could bundle up a small number of classes into a jar, which is basically just a zip file containing your compiled .class files.
Now your friend needs to sort some data and wants to use your classes. He would write his code to import your classes (using import at the top of his .java files), and then work with them in his code. When it came time for him to compile his .java files into .class files, he would add your jar to his classpath. Eclipse/netbeans can set this up for you, or if you're running javac from the command line it would look something like this:
javac ... -cp "fastsorting.jar" ...
Sometimes (rarely, in the real world) someone has a JAR which is really a fully fledged program, meant to be run. I say rarely, because jars aren't the most common way to distribute software. More popular is as a web service or through an applet on a website. In this case, a jar's manifest file (just a text file telling java information about this jar) will have a main class, which is run when the the jar is invoked through java.

How to share log4j library most effectively

i have a few batch java command-line applications which are planned to be deployed as:
batch_apps
app_1
batch1.jar
run_batch1.sh
app_2
batch2.jar
run_batch3.sh
{...etc...}
what would be the best practice on organizing a shared library pool - for example log4j:
batch_apps
app_1
batch1.jar
run_batch1.sh
app_2
batch2.jar
run_batch3.sh
libs
log4j.jar
ojdbc.jar
?
and include individual log4j.xml's in each app's own jar file?
i understand i would need to add 'libs' to the classpath either in manifests or in run_batchX.sh
(which way is preferable?)
I am mostly wondering what would be the most efficient setup performance-wise.
thanks
Having a shared libs directory at the root of your install dir is definitely the way to go. Since libs will be loaded in memory once, when the JVM launches, there is no impact on performance whatever solution you choose.
I would not put the classpath in the jar files, as this would force you to change your jars if you need to relocate your lib dir. Editing a script is much simpler.
I would not include the log4j conf file in your jar files either, for the same reason.
It appears your applications don't share a single JVM instance. (i.e. They are individually started via 'java -jar batch1.jar' or some such.) Therefore, sharing library .jar files only saves you DISK space not RAM.
If the apps are not sharing a single JVM then ease-of-deployment should take precedence over disk space. (Your time is worth more than a few "wasted" MB.)
In this instance I would recommend making each application self contained, in either a single .jar file including all its libraries, or a single folder of .jar files. (i.e. Put the libs for each app in a folder for that app.)
If the apps were sharing a single JVM then I would recommend the shared library folder option.
You can use the java extension mechanism. Place them in JAVA_HOME/lib/ext and they will be accessible by all apps. Of course, this may not be the best for all deployments, but its certainly easier.
This doesn't directly answer your question, but I have already tried the approach that you propose but would now create a single jar per application (see how to do it with Ant). That way, no need to include anything in the classpath:
java -jar myApp.jar
is all you need. I find it cleaner but everything is debatable.
It doesn't make any difference from a performance point-of-view since each application is run inside its own JVM.
The only downside is that some libraries will be present in each jar file. It only costs more to store on the HD, but these days, MB are pretty cheap :-) I trade simplicity (no external lib folder) and no jar hell (not placing your jars inside the Java ext folder) over storage price any time. If your application doesn't include terrabyte of libraries, I think it's fine.
For the Log4j configuration file, I would place one default file inside the jar but provide a sample config file (log4j-custom.xml.sample) that someone can modify and specify in the command line:
java -Dlog4j.configuration=log4j-custom.xml -jar myApp.jar

Categories