Repackaging jars using ANT

Discussion in 'Java' started by Chris, Jul 26, 2003.

  1. Chris

    Chris Guest

    I'm using ANT to create a jar file for my app. For a variety of reasons I
    have to include some external jar files in the main jar. Short of unjarring
    all of the external jars by hand and then including the files, is there a
    way to automate this in ANT?

    The JBuilder Archive Wizard does this very nicely -- it even allows you to
    optionally include only dependent classes from external jars. Can't seem to
    find a way to do this using ANT alone.
     
    Chris, Jul 26, 2003
    #1
    1. Advertising

  2. Chris

    Tom Davies Guest

    dhek bhun kho wrote:
    > "catalysoft.com" <>, Sun, 27 Jul 2003 00:44:02
    > +0100:
    >
    >
    >>I always thought it a pity that you can't have jar files inside jar files
    >>and still pick up the
    >>classes that the inner jars contain on the classpath. If that were possible
    >>it would reflect the idea
    >>that components of a large grain size are often composed of more
    >>finely-grained components.
    >>
    >>Any genies around to grant me that wish? No? Oh well!
    >>

    >
    >
    > Yes, the Java Genie. You just have to rub me more often:
    >
    > http://java.sun.com/products/jdk/1.2/docs/guide/extensions/spec.html#deployment
    >
    > So what you are saying is incorrect.


    No, the extension jars cannot be contained in the referring jar.

    Tom
     
    Tom Davies, Jul 27, 2003
    #2
    1. Advertising

  3. Tom Davies <>, Sun, 27 Jul 2003 13:17:48 +1000:

    > dhek bhun kho wrote:
    >> "catalysoft.com" <>, Sun, 27 Jul 2003 00:44:02
    >> +0100:
    >>>I always thought it a pity that you can't have jar files inside jar files
    >>>and still pick up the
    >>>classes that the inner jars contain on the classpath. If that were possible
    >>>it would reflect the idea
    >>>that components of a large grain size are often composed of more
    >>>finely-grained components.
    >>>Any genies around to grant me that wish? No? Oh well!

    >> Yes, the Java Genie. You just have to rub me more often:
    >> http://java.sun.com/products/jdk/1.2/docs/guide/extensions/spec.html#deployment
    >> So what you are saying is incorrect.

    > No, the extension jars cannot be contained in the referring jar.

    Okay, I am incorrect.
    > Tom


    But, I don't have to be wrong, you can just tweak the process to suit your
    needs:

    listing contains: 3 files


    A. create a manifesto:

    The manifest usually contains a class path that points to an external
    location according to the extension mechanism. But it's really little
    effort to make the class loader work the way YOU want.

    I am using a small class that does nothing more than instantiate a new
    class loader and launching the main class.

    <code file="manifesto">
    Manifest-Version: 1.0
    Created-By: _nuhb_
    Main-Class: Boot
    Class-Path: application.jar
    </code>

    B. Booting your VM.

    Basically when the VM has started and called main, the only class loader
    active is the system class loader. Using a class loader has a lot of
    benefits (and I am still discovering more of them). What this code does is
    1) Obtain the URL to the jar file and pass this to the init() method.
    2) Read the main attributes of the JAR that contains the class Boot.
    3) There in an attribute "Class-Path" must be defined just as with the
    extension mechanism; prevents confusion and can do no harm.
    4) Then it parses each Class-Path entry; malformed url's are simply
    rejected. People ought to package stuff correctly.
    5) Create an array of URL's and pass this to the boot() method.
    6) Instantiate a new class loader and use the URL's as defined in the
    classpath to be the classpath of the class loader.
    7) Currently the code is hard coded, you can manipulate this anyway you
    like, and make it more dynamic.
    8) Then the classloader will load the class as defined by the string
    passed to the boot() method and instantiate it, since all entries of the
    class path have been added, there's no problem with jars enclosed in
    another jar.

    <code file="Boot.java">
    // GPL
    // general disclaimer: people are known to get blind from using this code.

    import java.util.*;
    import java.net.*;
    import java.util.jar.*;
    import java.io.*;

    public
    class Boot
    {
    /**
    * @param baseURL is the main JAR, format-> jar:....!/entry
    * @param mainClass fqn of class to load, must be runnable in version.
    * @param manifestURL url of the main manifest
    **/
    protected
    void init(final URL baseURL, final URL manifestURL, final String mainClass)
    {
    final Manifest manifest;
    final StringTokenizer tokenizer;
    final List list;
    try
    {
    System.out.println("Manifest url:"+manifestURL);
    list = new LinkedList();
    manifest = new Manifest(manifestURL.openStream());
    for (Iterator i=manifest.getMainAttributes().keySet().iterator();i.hasNext();)
    {
    System.out.println(i.next());
    }
    tokenizer = new StringTokenizer(manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH));
    while (tokenizer.hasMoreTokens())
    {
    try
    {
    list.add(new URL(baseURL+""+tokenizer.nextToken()));
    }
    catch (MalformedURLException possibleSoWhat) {}
    }
    boot((URL[])list.toArray(new URL[0]),mainClass);
    }
    catch (NullPointerException noURL) {noURL.printStackTrace();}
    catch (IOException noConnection) {noConnection.printStackTrace();}
    }

    /**
    * @param urls URLs defining the classpath for the application
    * @param mainClass name of the class to start as a thread
    */
    protected
    void boot(final URL[] urls, final String mainClass)
    {
    URLClassLoader loader;

    try
    {
    loader = ((URLClassLoader)getClass().getClassLoader()).newInstance(urls);
    new Thread((Runnable)Class.forName(mainClass).newInstance()).start();
    }
    catch (ClassNotFoundException obeyYourParents) {}
    catch (IllegalAccessException eatMoreFruit) {}
    catch (InstantiationException spreadMoney) {}
    catch (ClassCastException stupidProgrammer) {}
    }

    public
    static
    void main(String[] args)
    throws Exception
    {
    Boot boot;
    URL manifestURL;
    URL baseURL;

    // retrieve manifest relative to this class.
    baseURL=new URL("jar:"+((JarURLConnection)Boot.class.getResource((Boot.class.getName()+".class")).openConnection()).getJarFileURL()+"!/");
    manifestURL = new URL(baseURL+"META-INF/MANIFEST.MF");

    System.out.println
    (
    "This classloader is a subclass of URLClassLoader:"+
    URLClassLoader.class.isAssignableFrom
    (
    new Boot().getClass().getClassLoader().getClass()
    )
    );

    boot = new Boot();
    boot.init(baseURL,manifestURL,"Start");
    }
    }
    </code>

    C.
    Here's just a simple Runnable that references a class in application.jar
    statically. This causes no problems as Boot.class does not contain static
    references to the Start class.

    <code file="Start.java">
    public class Start implements Runnable{ public void run() {.out.println("I
    found a B:"+new B()); }}
    </code>


    D.
    Just an empty class, to prove the class is located

    <code file="B.java">
    public class B {}
    </code>

    E.
    Contents of the jar and how to make them:

    <code file="application.jar">
    $ javac B.java
    $ jar cf application.jar B.class
    </code>

    <code file="boot.jar">
    $ javac Boot.java Start.java
    $ jar cfm manifesto boot.jar Boot.class Start.class application.jar
    </code>

    F.
    How I did it with the above listings:

    <code>
    ~ $ jikes Start.java Boot.java B.java ; jar cf b.jar B.class; jar cmvf manifesto a.jar Boot.class Start.class; rm B.class Start.class Boot.class ; java -jar a.jar
    added manifest
    adding: Boot.class(in = 3906) (out= 2034)(deflated 47%)
    adding: Start.class(in = 600) (out= 376)(deflated 37%)
    This classloader is a subclass of URLClassLoader:true
    Manifest url:jar:file:/home/bhun/a.jar!/META-INF/MANIFEST.MF
    Class-Path
    Created-By
    Main-Class
    Manifest-Version
    I found a B:B@187aeca
    </code>

    QED.
    Have fun.
     
    dhek bhun kho, Jul 27, 2003
    #3
  4. Chris

    Chris Guest

    Very cool -- the code work perfectly. I was worried that all the unjarring
    and rejarring would be slow, but it took only a few seconds.


    "Tom Davies" <> wrote in message
    news:3f2343ee$0$1210$...
    >
    >
    > Chris wrote:
    > > I'm using ANT to create a jar file for my app. For a variety of reasons

    I
    > > have to include some external jar files in the main jar. Short of

    unjarring
    > > all of the external jars by hand and then including the files, is there

    a
    > > way to automate this in ANT?
    > >
    > > The JBuilder Archive Wizard does this very nicely -- it even allows you

    to
    > > optionally include only dependent classes from external jars. Can't seem

    to
    > > find a way to do this using ANT alone.
    > >
    > >

    >
    > Here's a snippet of ant I use:
    >
    > <delete dir="tmp"/>
    > <mkdir dir="tmp"/>
    > <unjar dest="tmp">
    > <fileset dir="lib" includes="*.jar"/>
    > </unjar>
    > <delete file="yame.jar"/>
    > <jar jarfile="yame.jar">
    > <fileset dir="build"/>
    > <fileset dir="tmp"/>
    > </jar>
    >
    > I have a lib directory containing all the .jar files I want to package
    > into my jar file, plus my classes in the build directory.
    >
    > The destination jar 'yame.jar' is built in .
    >
    > Hope that helps,
    > Tom
    >
     
    Chris, Jul 27, 2003
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ike
    Replies:
    6
    Views:
    1,104
    Andrew Thompson
    Sep 24, 2004
  2. Replies:
    5
    Views:
    9,119
  3. JavaEnquirer
    Replies:
    2
    Views:
    550
    JavaEnquirer
    Feb 22, 2006
  4. Ulf Meinhardt
    Replies:
    2
    Views:
    1,436
  5. Martin DeMello

    repackaging a compiled gem as a binary

    Martin DeMello, Sep 22, 2010, in forum: Ruby
    Replies:
    2
    Views:
    140
    Martin DeMello
    Sep 22, 2010
Loading...

Share This Page