MoonOcean

Main | A simple example of... »
Tuesday Nov 27, 2007

Bundle an executive binary program with a Java Web Start application

When an Java application needs to call native program(s) to handle situations, JNI is frequently the preferred technology.

If the Java application is deployed with Java Web Start. The embedded native program could be packaged in a jar file, and the jar is deployed as a native library of the Java application.

Following is a piece of JNLP file. It is obvious that the foo.jar is the Java application, and the libfoo.jar including a native program which normally should be an shared object (e.g. libfoo.so on Unix-like OS or foo.dll on Windows OS).

------------------------------------------

<resources>

    <j2se version="1.4+" java-vm-args="-client"/>

    <jar href="/foo.jar" main="true" download="eager"/>

    <nativelib href="/libfoo.jar"/>

</resources>

------------------------------------------

However, sometimes a shared object doesn't meet our requirement. For example, an executive program is always much smaller rather than a dynamic-link library. Considering to reduce launch time, we prefer to bundle an exe file with our JavaWS application instead of a DLL file.


Technically we can bundle any files that we want in our application JAR. The executive binary program could also be packaged in a JAR file. But the methods in the native program cannot be referenced by the Java application directly. In such scenario, the JNI is inapplicable.


The executive program should be executed on users' system, and interface between it and the Java application is its out put message.


Although, it is possible to reference to the contents of the Java Web Start cache directly. We don't recommend it, since the cache is a private implementation of Java Web Start, and is subjected to change anytime.


The URL returned for calls to ClassLoader.getResource() is now the proper JAR URL of the item on the net. (http://java.sun.com/javase/6/docs/technotes/guides/javaws/enhancements6.html, see bullet item related to "Jar Indexing")


We could to access the EXE in the JAR by using a JarURLConnection to get to the resource. e.g ,

| jar:http://www.foo.com/bar/baz.jar!/foo.exe |


We can then get the InputStream of the binary program, and then copy it to a temp location on disk, and then execute it from there.

If the JAR resource is already cached, the contents of the resource will be returned from the cache directly, so there won't be extra network connections to the JAR itself.

NOTE: This solution is only applicable with JVM 1.6.

Following is an example of such an implementation.

--------------------------------------------------------
File of TestFoo.java

====================

... ...

   private String execute() throw IOException {   

    ... ...

    ClassLoader loader = this.getClass().getClassLoader();

    /* jnitest is an binary program running on Solaris OS.

        It is packaged in a Jar file of libjnitest.jar */

    URL url = loader.getResource("jnitest");                           

    /* Note: the size of the buffer must not less than the size of jnitest */

    byte[] buf = new byte[20000];

    int len = 0;

    JarURLConnection uc = (JarURLConnection)url.openConnection();             

    uc.connect();


    InputStream in = new BufferedInputStream(uc.getInputStream());

    len = in.read(buf);

    in.close();             


    File tmpFile = new File(fileName);           

    FileOutputStream output = new FileOutputStream(tmpFile);

    output.write(buf, 0, len);  

    output.close();                                       

    runCommand("chmod +x " + fileName);

    /* Execute the executive native program, and get its output message */

    String outputMessage = runCommand(fileName);            

    return outputMessage;

    }

    private String runCommand(String command) {

        String[] cmd0 = {"sh", "-c", command};

        try {

                Process ps = Runtime.getRuntime().exec(cmd0);

                String str1 = loadStream(ps.getInputStream());

                return str1;

        } catch (IOException ioe) {

                System.err.println("Error: running command on Solaris OS failed.");
        }

        return null;

    }


    private String loadStream(InputStream input) throws IOException {

        int count;

        int BUFFER = 20;

        byte[] data = new byte[BUFFER];

        input = new BufferedInputStream(input);

        StringBuffer buff = new StringBuffer();

        while ((count = input.read(data, 0, BUFFER)) != -1)

            buff = buff.append(new String(data, 0, count));

        return buff.toString();

    }

... ...


File of testfoo.jnlp

====================

... ...

 <resources>

    <j2se version="1.4+" java-vm-args="-client"/>

    <jar href="/testfoo.jar" main="true" download="eager"/>   

    <nativelib href="/libjnitest.jar"/>

 </resources>

... ...

Comments:

Hello Ye Julia ,

Thnaks a lot. You do save my day. I have been searching the meterals about how to Bundle an executive binary program with a Java Web Start application. Yours is the best I have found. Most of them are talking about the nativelib of dll, but not exe. .......

Thanks again

Mei-Chun

Posted by Mei-Chun Lo on July 07, 2009 at 10:07 PM HKT #

Post a Comment:
  • HTML Syntax: NOT allowed