I am trying to design a minimal framework with a launcher which will be common to several projects. I am however encountering difficulties when it comes to running the JARs.
My purpose is to have a self-sufficient java -jar
command: the MANIFEST knows the classpath and the entry point. The delicate bit is this: since I want the launcher to be common to all projects, and to be DRY-compliant, I have a specific JAR with it, which will always be on the classpath.
Now, I have trouble launching my jar:
java -jar test.jar
should load classpath and entry point from the Manifest. It does find the Manifest, but I end up with the Could not find or load main class
.
Launching with more explicit details does work:
java -cp test.jar;lib/* org.keyboardplaying.test.TestLauncher
I did not find any specification to say whether it is possible or not, and this is not the standard case, but can I specify in my MANIFEST.MF an entry point that will be found on classpath but is not included within my JAR?
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: Chop
Build-Jdk: 1.7.0_45
Main-Class: org.keyboardplaying.test.TestLauncher
Class-Path: lib/slf4j-api-1.7.12.jar lib/logback-classic-1. 1.3.jar lib/logback-core-1.1.3.jar
Class-Path: lib/*
which I like better, but you cannot use wild cards for classpath in Manifest1. You can still use directories, but the class loader will not examine the content of jars within this directory.1. One source among others: https://stackoverflow.com/a/4756762/1734119
2. Very interesting link for Class-Path entry of MANIFEST: http://todayguesswhat.blogspot.com/2011/03/jar-manifestmf-class-path-referencing.html
Does the JAR entry point actually have to be inside the JAR?
No, it doesn't. It's entirely reasonable to put the launcher into a separate jar file, and so long as that's in the classpath specified by the manifest, it should be fine. Here's an example:
launch\Launchable.java:
package launch;
public interface Launchable {
void launch();
}
launch\Launcher.java:
package launch;
public class Launcher {
public static void main(String[] args) throws Exception {
System.out.println("I am the launcher!");
Class<?> clazz = Class.forName(args[0]);
Launchable launchable = (Launchable) clazz.newInstance();
launchable.launch();
}
}
demo\Test.java:
package demo;
import launch.Launchable;
public class Test implements Launchable {
public void launch() {
System.out.println("I am Test, being launched");
}
}
manifest.txt:
Manifest-Version: 1.0
Main-Class: launch.Launcher
Class-Path: launcher.jar
Now compile and run:
$ javac -d . launcher/*.java
$ javac -d . demo/*.java
$ jar cvf launcher.jar launcher
$ jar cvfm demo.jar manifest.txt demo
$ java -jar demo.jar demo.Test
I am the launcher!
I am Test, being launched
See more on this question at Stackoverflow