Jar application referencing Jars into its own Jar
Is it possible for a JAR application, say:
"MyJar.jar"
one that executes as:
"java -jar MyJar.jar"
reference other jars, say:
"MyUtil.jar" and "Other.jar"
which are included into "MyJar.jar" ?
I used the MyJar.jar manifest:
"Class-Path: MyUtil.jar Other.jar"
but when I run the application:
"java -jar MyJar.jar"
the command "java" looks for the jar files in the "." (current) directory, insted of into the "MyJar.jar" itself.
Any suggestions (without having to write classloader code) ?
Daniel
[601 byte] By [
Dmazzuca] at [2007-9-26 13:28:29]

Well you can do it this way. When you specify a jar file in the Class-Path header of another jar file, this means that you don't have to manually set the classpath variable. The jar specified in the Class-Path header is a file located into the same directory as the current jar file or you can specify another folder with a complete URL.
But you cannot nest jar files and expect to call them using the Class-Path header.
> Well you can do it this way. When you specify a jarIt should read "Well you cannot do it this way"
Thank for your answer jquattro. However, I saw many applications distributed as one unique executable jar file, that contains inside many other jar files that it references. I was wondering how these applications work.
I think your refering to installation programs that extract their contents, and then run. As has already been stated, Java is unable to access a jar w/in another jar.-Ron
Thank you for your answer rSully.
However, I am not referring to applications we should extract its files from a jar file in order to run the application.
I am referring to a jar application itself , i.e. the one that you run issuing something like:
java -jar MyJarFile.jar
And this Jar application contains other nested jar files that it references.
I suppose the application may be using the java.util.jar API or the JarClassLoader class or something like this.
But I would like to know if there exits an automatic way the Jar application may find the classes in the nested Jars without having to add extra code to the application to do it.
Any suggestions ?
Sorr, no automatic way available.
Here's a partial solution. Some of the java file handling routines accept paths of the form file =
jar:file:myJar.jar!/file.txt
The part 'jar:file:' is the protocol. This is how you can access files in a jar form within a jar.
What I can't figure out is how to run a jar in a jar, e.g.
java -jar jar:file:a.jar!/b.jar
where b.jar is inside a.jar. The above line doesn't work.
All of your jar files must be in the same directory for this to work (using the manifest as you do)...we do EXACTLY what you are describing in your original post and it works fine.
Thank you for your answer smg123,As you said, if you have the referenced jar files in the same directory, it works fine. However the problem is what can we do when we have the referenced jar files nested inside the Jar file application.
I'm not sure I follow you but I'll take a stab at it:
If I have 2 jar files (each with routines the other may need to call) ... I have to do a couple of things:
1) Each jar file must have a manifest and in that manifest the jar files should reference each other:
Jar 1 needs functions in jar 2:
Class-Path: jar2.jar <-- some entry in Jar1's manifest
Jar 2 needs functions in jar 1:
Class-Path: jar1.jar <-- some entry in Jar2's manifest
I can't think of a good reason why you would store a "jar in a jar" at all....it would be very inefficient and it's not necessary.
Now, *IF* you mean you want to load and instantiate some object from classes stored in a .jar file, that is a totally different thing. It can be done, but man it ain't easy and it ain't pretty.
Thank you for your answer again smg123.
Let me try to put an example where we can think to use a jar in a jar:
1) The specifications says we have to deliver a unique runnable jar file containing all the files of the application.
2) The application uses oracle database, so we need to use Oracle classes12.jar package.
3) The application also uses Sun ONC RPC to communicate with a legacy system, and in order to do that, it needs to use oncrpc.jar package.
4) It also communicates with a mainframe application, using EntireX middleware, and it needs to use entirex.jar package.
5) It would be nice and simple if we could create a runnable jar application, say MyApp.jar, with all the coded classes and all the jars it references.
6) Another example, the Sun Java Developer Certification mentions that you may deliver a unique jar file of the final application, and that it may contain nested jars inside.
Well, one thing you can do is UNjar all the jar files you want to reside together. (jar -xf jarname) - but do it in some base directory (perhaps called \stage). Under that directory make another directory (perhaps \MyApp).
*Copy all jar files to \stage and issue that jar -xf command
As you unpack, you will create the directory structures in the jars under the \MyApp path. Once all the jars are unpacked. re-pack them in a single jar using the jar command:
jar -cvfm Whatever.jar manifest.mf -C stage .
Again, you issue this command from the \stage directory. When all is done, you'll have ONE jar called "Whatever.jar" in the \stage directory. The only catch is make sure your manifest is set up right (which I know you already know....)
Daniel,
J2SE simply cannot do what you are asking 'automatically'. What you need to do is supply your own custom classloader that can open a jar within a jar. ssozonoff just posted a link in another topic to a commercial program that does just that.
http://www.yagga.net/java/metajar/
It is also my opinion that J2EE can also do this, but in a WAR file. From the senario that you describe, you probably are already using J2EE so the packager tool might be just what you're looking for. :)
http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/WCC3.html
-Ron
Ron,
You are right. Is it possible to nest jars into a war in J2EE. Unhappily I am not using J2EE, just plain J2SE, and it is not my intention to instantiate a new class loader object to control all that things.
I supposed J2SE provides some way to do that kind of things automatically, but it seams there is not such a way.
Anyway thank you for your answer. Good hints.
- Daniel
Shaun,
In fact, it is possible to unpack all the classes of the jar files and create one single bundle jar with all the classes, with both, my own classes, and the classes from Oracle, EntireX and RPC jars.
However this is not what I want to do. The idea is to maintain third party classes in its own jars, in a clean organization, so we can replace them with new versions or substitutes ones.
Again, thank you for your answer.
- Daniel
I think you can do it. There are classes (and a package) for reading jars and jar entries from wihtin java code. So what you do is read an entry that is a jar, and then read its entries. At that point you have the class files and can use a class loader to load the bytes.
there is some discussion in this forum about reading and writing jars programmatically. Find that and also info on classloaders and i'll bet you can do it.
Thank you for your answer 6tr6tr,
You are right, there is a Java API to manipulate JAR files.
However, the intention was to do that without having to code and create extra class loader objects.
I thought we were able to nest jar files in a jar application, and that the application could reference the classes in the nested jar files without having to write extra code, just specifying some attribute in the manifest. But it seams I was wrong.
Thank you again,
-Daniel
I know this is an ancient topic, and that my included solution IS NOT exactly what the original author was looking for, but it does work.
As a further disclaimer, might I also add that I realize that this idea of nesting jars inside of jars has been hotly debated and that many believe it is a poor idea. Regardless, here is the solution I came up with....
Usage: java StandAlone.Builder MainClassName NewJar jar1 [jar2] [...]
This will create a jar file capable of running alone via "java -jar".
I managed to piece this code together out of several partial solutions I found on the web. The majority of the code included below I found in snippets elsewhere on the web (so if some of it is yours, thanks!). This is, however, the first completely working solution I've seen.
File StandAlone/Builder.java
--
package StandAlone;
import java.net.URLClassLoader;
import java.net.URL;
import java.io.*;
import java.util.*;
import java.util.jar.*;
public class Builder
{
static private void copyFile( OutputStream out, InputStream in )
throws IOException
{
byte buffer[] = new byte[4096];
while (true) {
int r = in.read( buffer );
if (r<=0) {
break;
}
out.write( buffer, 0, r );
}
}
static private void copyFile( OutputStream out, String infile )
throws IOException
{
FileInputStream fin = new FileInputStream( infile );
copyFile( out, fin );
fin.close();
}
static private void addFile(JarOutputStream jout, String path) throws IOException
{
JarEntry je = new JarEntry( path );
jout.putNextEntry( je );
System.out.println( "adding " + path );
copyFile( jout, path );
jout.closeEntry();
}
static private void addFile(JarOutputStream jout, String path, InputStream in) throws IOException
{
JarEntry je = new JarEntry( path );
jout.putNextEntry( je );
System.out.println( "adding " + path );
copyFile( jout, in );
jout.closeEntry();
}
static private void addJar(JarOutputStream jout, String path) throws IOException
{
String fname = path;
int index = fname.lastIndexOf("/");
if(index >= 0)
{
fname = fname.substring(index);
}
fname = "jars/" + fname;
JarEntry je = new JarEntry( fname );
jout.putNextEntry( je );
System.out.println( "adding " + fname );
copyFile( jout, path );
jout.closeEntry();
}
static public void main(String args[]) throws Exception
{
if(args.length < 3)
{
System.err.println("Usage: java StandAlone.Builder MainClassName NewJar jar1 [jar2] [...]");
System.exit(1);
}
String manifest = "Manifest-Version: 1.0\nMain-Class: StandAlone.Runner\nClass-Path: ";
for(int i=2; i<args.length; ++i)
{
manifest += args[i];
manifest += " ";
}
manifest += "\n";
FileOutputStream fout = new FileOutputStream( args[1] );
JarOutputStream jout = new JarOutputStream( fout , new Manifest(new ByteArrayInputStream(manifest.getBytes())));
String props = "MainClass="+args[0]+"\n";
addFile( jout, "StandAlone/Settings.properties", new ByteArrayInputStream(props.getBytes()) );
addFile( jout, "StandAlone/Runner.class" );
for(int i=2; i><args.length; ++i)
{
addJar(jout, args[i]);
}
jout.close();
fout.close();
}
}
File StandAlone/Runner.java
--
package StandAlone;
import java.net.URLClassLoader;
import java.net.URL;
import java.lang.reflect.*;
import java.util.ResourceBundle;
public class Runner
{
public static void main(String args[]) throws Exception
{
ResourceBundle props = ResourceBundle.getBundle("StandAlone.Settings");
String mainClassName = props.getString("MainClass");
URL urls[] = new URL[] { Runner.class.getResource("/jars/") };
URLClassLoader classLoader = URLClassLoader.newInstance(urls);
Class mainClass = Class.forName(mainClassName, true, classLoader);
Class argTypes[] = new Class[] { args.getClass() };
Method m = mainClass.getMethod("main", argTypes);
m.setAccessible(true);
int mods = m.getModifiers();
if (m.getReturnType() != void.class || !Modifier.isStatic(mods) ||
!Modifier.isPublic(mods)) {
throw new NoSuchMethodException("main");
}
try {
m.invoke(null, new Object[] { args });
} catch (IllegalAccessException e) {
// This should not happen, as we have disabled access checks
}
}
}
>
Hi,
I am trying to jar a couple of AXIS jar files along with my class file. I used the Standalone package code below and I am having the following error
Exception in thread "main" java.lang.NullPointerException
at sun.misc.URLClassPath$3.run(URLClassPath.java:313)
at java.security.AccessController.doPrivileged(Native Method)
at sun.misc.URLClassPath.getLoader(URLClassPath.java:310)
at sun.misc.URLClassPath.getLoader(URLClassPath.java:287)
at sun.misc.URLClassPath.getResource(URLClassPath.java:157)
at java.net.URLClassLoader$1.run(URLClassLoader.java:192)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at java.net.FactoryURLClassLoader.loadClass(URLClassLoader.java:580)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:242)
at StandAlone.Runner.main(Runner.java:16)
My jar files are at c:\API_Tool\axis
and the command i am using are
java StandAlone.Builder com.CLI_Tool MAIN.jar c:/API_Tool/axis/axis.jar c:/API_Tool/axis/axis-ant.jar c:
/API_Tool/axis/log4j-1.2.8.jar
Please could anyone help me to understand the problem
Thanks
