-3

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

Knight
  • 1
  • 1
  • 1
    If you don't want the code to be messy, don't manually construct the classpath in the first place. Make a [fat jar](https://stackoverflow.com/questions/19150811/what-is-a-fat-jar). – Michael Jun 09 '21 at 13:34

1 Answers1

2

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:

  1. 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.

  2. signed jars do not work well with this model.

  3. 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:

  1. 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

  2. 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.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72