3

Re-writing this for clarity before I bounty it:

What I want to do is make a bootstrap loader for a program that is already made in (executable)jar form. This bootstrap program would have three simple goals during it's runtime:

  1. Compare a local xml file for the target program to one that's hosted on a server (to make sure that they are the same version)

  2. If they are not the same version, and the online version is newer, download the newer version.

  3. Re-write the xml file to reflect this change.

  4. Execute the second jar file (launch it as though you launched the executable).

The issue I'm having is with step number 4. I have found myself struggling to find a solid way to launch a jar from within my bootstrap program despite looking at UrlClassLoader and other libraries.

Due to some outside issues, JNLP / Web-start is not an option for this case.

TL;DR: I need to find a way to download / launch a jar from within a jar in order to update a program at the time the bootstrap is run.

Thanks!

Siguza
  • 21,155
  • 6
  • 52
  • 89
A_Elric
  • 3,508
  • 13
  • 52
  • 85
  • Possible duplicate of: http://stackoverflow.com/questions/12520395/how-to-run-a-jar-file-from-another-jar – dan Nov 12 '12 at 21:26
  • It's pretty close actually, The implementation on the top answer there may even fit my own needs, thanks for pointing that out @dan – A_Elric Nov 12 '12 at 21:35

4 Answers4

0

Runtime.getRuntime().exec()

There are some pitfalls here though if your MyApp.jar will have any output.

Check the docs for details of how to properly use exec...

http://docs.oracle.com/javase/6/docs/api/java/lang/Process.html

And see this SO post about some of the pitfalls...

Java Runtime.exec()

Community
  • 1
  • 1
Jim
  • 6,753
  • 12
  • 44
  • 72
0

When you say 'executing the new jar', do you mean starting a standalone application? One possibility could be to simply execute a new Java process from the Updater.jar bootstrap logic and then Exit itself.

Udo Klimaschewski
  • 5,150
  • 1
  • 28
  • 41
  • The second jar is itself a standalone application (it's a runnable jar, that just needs an updater, which is the only purpose of updater.jar I have no problem running the second jar and exiting the bootstrap, but how do I do that? I know that System.exit(0); will kill the application itself, but I'm not sure how to run the other beforehand? – A_Elric Nov 12 '12 at 21:32
  • See Jim's answer. Problem is, you will loose the original stdin and stdout channels, when you exit the bootstrap process. I made something similar with one application, but there I keep both processes alive. But the bootstrap process does nothing but to read the stdout of the application process, after it started it and echoing it to its stdout. – Udo Klimaschewski Nov 12 '12 at 21:55
0

By other words you want to implement self-updatable application. It is possible and even not so difficult.

Your program should be designed to have 2 parts. The loader (and updater) and the business logic. Loader should start business logic using separate class loader. You can use UrlClassLoader. The loader will go to the online service and check version. If it is needed it will create URLConnection, download the new jar and store it somewhere in the filesytem (e.g. in user home directory). Then it will run the business logic using the already mentioned class loader that loads classes from just downloaded jar file.

This actually mimics the JNLP behavior.

AlexR
  • 114,158
  • 16
  • 130
  • 208
  • Do you have an example where this is implemented as I'm interested in seeing if it would provide the functionality I would need to get my project working. – A_Elric Nov 12 '12 at 21:33
0

I hate having to answer my own question, but in this case I feel like it needs to be done...

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import org.w3c.dom.Document;


public class updater {
public static void main(String[] args) throws IOException {
    try{
        DefaultHttpClient httpclient = ClientMaker();
        HttpGet get = new HttpGet("http://encorpops04:8080/Updater-test/Version.xml");
        HttpResponse response = httpclient.execute(get);
        InputStream in = response.getEntity().getContent();

        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = domFactory.newDocumentBuilder();
        Document doc = builder.parse(in);

        //Parse the Xml.
        XPathFactory factory = XPathFactory.newInstance();
        XPath xpath = factory.newXPath();
        XPathExpression expr = xpath.compile("//version/number/text()");
        String result = (String) expr.evaluate(doc, XPathConstants.STRING);
        System.out.println(result);

        File f = new File(System.getProperty("user.dir")+ "\\Version.xml");
        in = new FileInputStream(f) ;
        doc = builder.parse(in);
        expr=xpath.compile("//version/number/text()");
        String result2 = (String) expr.evaluate(doc, XPathConstants.STRING);
        System.out.println(result2);


        if(Double.parseDouble(result2) < Double.parseDouble(result)){
            HttpGet get2 = new HttpGet("http://encorpops04:8080/Updater-test/MyOutput.jar"); 
            HttpResponse response2 = httpclient.execute(get2);
            InputStream in2 = response2.getEntity().getContent();
            File f2 = new File("MyOutput.jar");
            OutputStream fos = new FileOutputStream(f2);
            byte buf[] = new byte[1024];
            int len;
            while ((len = in2.read(buf)) > 0) {
                fos.write(buf, 0, len);
            }
            fos.close();
            in.close();
        }
        System.out.println("cmd.exe /C  javaw -jar"  +System.getProperty("user.dir") + "\\MyOutput.jar");
        Process p = Runtime.getRuntime().exec("cmd.exe /C  javaw -jar "  +System.getProperty("user.dir") + "\\MyOutput.jar");
        p.waitFor();
        p.destroy();
    }catch(Exception e){ e.printStackTrace(); }


}

public static DefaultHttpClient ClientMaker() {
    int connectiontimeout = 30000; // 1 second int sockettimeout = 1000;
    HttpParams httpparameters = new BasicHttpParams();
    HttpConnectionParams.setConnectionTimeout(httpparameters,
            connectiontimeout);
    HttpConnectionParams.setSoTimeout(httpparameters, connectiontimeout);
    DefaultHttpClient httpclient = new DefaultHttpClient(httpparameters);
    return httpclient;
}

}

Version.xml looks like this:

<?xml version="1.0" encoding="ISO-8859-1"?>
<version>
    <number>1.0</number>
</version>

Sidenote- I didn't make version.xml update automatically, you can either edit the number in there to match, or just pull the one that you check against to replace it.

A_Elric
  • 3,508
  • 13
  • 52
  • 85