简体   繁体   中英

Programmatically creating Runnable JAR that has external JARs from a GUI?

I have a simple test program that has one button. When a user clicks the button, the program is supposed to create a Runnable JAR. The Runnable JAR is a simple program that opens google.com in Firefox. The program has three classes.


1) Main.java

 package test;

    public class Main {

        public static void main(String[] args) {

            Selenium.getGoogle();

        }

    } 

2) Selenium.Java

package test;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;

public class Selenium {

    public static void getGoogle () {
        WebDriver driver = new FirefoxDriver();
        driver.get("http://google.com");
    }

}

3) TestGUI.java

package test;

import javax.swing.*;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;


public class TestGUI extends JFrame implements ActionListener {

    private static final long serialVersionUID = 1L;

    public TestGUI() {

        setSize(200, 100);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
        /*
         * JButton.
         */
        JButton startButton = new JButton("Create Runnable JAR file ! ");
        add(startButton);
        startButton.addActionListener(this);
    }


    public static void main(String[] args) {
        new TestGUI();
    }


    public void actionPerformed(ActionEvent e) {
        System.out.println("The Button Works");
        String file1ToCompile = "test" + java.io.File.separator + "Main.java";

        String file2ToCompile = "test" + java.io.File.separator + "Selenium.java";

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

        int compilationResult = compiler.run(null, null, null, file1ToCompile, file2ToCompile);

        if (compilationResult == 0) {

            System.out.println("Compilation is successful");

        } else {
            System.out.println("Compilation Failed");
        }
    }
}

I have two main classes . I added TestGUI.java to the manifest so the GUI will show up . Once a user clicks the button, I want my program to create a Runnable JAR that consists of Main.java and Selenium.java .

However, I get an error null pointer exception for int compilationResult = compiler.run(null, null, null, file1ToCompile, file2ToCompile);

How can I do this?

You are getting the NullPointerException most likely because you are using a JRE and not a JDK. Download a JDK and add it to the buildpath.

Secondly, you are just compiling the files. You then need to create a jar file and add the .class files to it. it is important that they have the same package structure in the jar. In the jar you will also need:

Manifest file

Dependant jars

Classloader

The manifest file describes which class contains the main method and info on the classpath. Dependant jars(selenium) also need to be present for obvious reasons. a classloader is needed to load the classes in the dependant jar. below is my implementation:

public void actionPerformed(ActionEvent e){
        System.out.println("The Button Works");
        String file1ToCompile = "src" + java.io.File.separator + "test" + java.io.File.separator + "Main.java";
        String file2ToCompile = "src" + java.io.File.separator + "test" + java.io.File.separator + "Selenium.java";
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        int compilationResult = compiler.run(null, null, null, file1ToCompile, file2ToCompile);
        if (compilationResult == 0) {
            System.out.println("Compilation is successful");
        } else {
            System.out.println("Compilation Failed");
        }

        Manifest manifest = new Manifest();
        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
        manifest.getMainAttributes().put(new Name("Rsrc-Class-Path"), "./ selenium-server-standalone-2.39.0.jar");
        manifest.getMainAttributes().put(Attributes.Name.CLASS_PATH, ".");
        manifest.getMainAttributes().put(new Name("Rsrc-Main-Class"), "test.Main");
        manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS, "org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader");
        try{
            JarOutputStream target = new JarOutputStream(new FileOutputStream("output.jar"), manifest);
            add(new File("src" + java.io.File.separator + "test" + java.io.File.separator + "Main.class"), target);
            add(new File("src" + java.io.File.separator + "test" + java.io.File.separator + "Selenium.class"), target);
            add(new File("org"), target);
            add(new File("selenium-server-standalone-2.39.0.jar"), target);
            target.close();
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }

    private void add(File source, JarOutputStream target) throws IOException
    {
        BufferedInputStream in = null;
        try
        {
            if (source.isDirectory())
            {
                String name = source.getPath().replace("\\", "/");
                if (!name.isEmpty())
                {
                    if (!name.endsWith("/"))
                        name += "/";
                    JarEntry entry = new JarEntry(name);
                    entry.setTime(source.lastModified());
                    target.putNextEntry(entry);
                    target.closeEntry();
                }
                for (File nestedFile: source.listFiles())
                    add(nestedFile, target);
                return;
            }

            JarEntry entry = null;

            if(source.getPath().contains("jarinjarloader")){
                entry = new JarEntry(source.getPath());
            }
            else if(source.getName().endsWith(".class")){
                entry = new JarEntry("test/"+source.getName());
            }else if(source.getName().endsWith(".classpath")){
                entry = new JarEntry(source.getName());
            }else if(source.getName().endsWith(".jar")){
                entry = new JarEntry(source.getName());
            }
            entry.setTime(source.lastModified());
            target.putNextEntry(entry);
            in = new BufferedInputStream(new FileInputStream(source));

            byte[] buffer = new byte[1024];
            while (true)
            {
                int count = in.read(buffer);
                if (count == -1)
                    break;
                target.write(buffer, 0, count);
            }
            target.closeEntry();
        }
        finally
        {
            if (in != null)
                in.close();
        }
    }

I used Eclipse's jarinjarloader for my classloader which I obtained by creating a runnable jar in eclipse and extracting the org folder and copying it into my project directory.

在此输入图像描述

There is a slight problem with this but i suspect i am missing something small. output.jar will run fine if the classloader files are in the same directory as the jar. I am currently trying to fix this so that it can find the classloader from within the jar.

Here is the answer Oo finally found it!! you just have to see this way the last part of the code:

File folder = new File("C://Users//YourUser//YourWorkspace//YourProject//bin");
File[] files = folder.listFiles();
File file=new File("C://Users//YourUser//Desktop//Examples.jar");

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM