0

Consider this - I have a simple one-class application called "TestApplication" (source code below):

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class TestApplication extends Application {

    public Parent createContent() {

        /*layout*/
        BorderPane layout = new BorderPane();

        /*layout -> center*/
        Button button = new Button("Run JAR");
        button.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent ae) {
                // Call TestJar.class from TestJar.jar
            }
        });

        /*add item to the layout*/
        layout.setCenter(button);
        return layout;
    }

    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(createContent()));
        stage.setWidth(200);
        stage.setHeight(200);
        stage.show();
    }

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

Now, in its button.setOnAction() method I want to call a "TestJar.class" (extends javafx.stage.Stage), which is the only class of non-runnable "TestJar.jar" archive. But problem is, I want to be able to call "TestJar.class" from the level of "TestApplication" without the prior possibility of adding imports to the code. For example I have an application, for which I provide jar location and information about the name of the class in this jar, which should be call out. Code of the "TestJar.java" class below:

import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class TestJar extends Stage {

    public TestJar() {
        super();
        BorderPane layout = new BorderPane();
        Label label = new Label("This is test");
        layout.setCenter(label);
        Scene scene = new Scene(layout, 200, 200);
        this.setScene(scene);
        this.show();
    }
}

And additionaly structure of the JAR file:

<archive = TestJar.jar>
    <folder = META-INF>
        <file = MANIFEST.MF>
            Manifest-Version: 1.0
        </file>
    </folder>
    <file = .classpath>
        <?xml version="1.0" encoding="UTF-8"?>
        <classpath>
            <classpathentry kind="src" path="src"/>
            <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
            <classpathentry kind="output" path="bin"/>
        </classpath>
    </file>
    <file = .project>
        <?xml version="1.0" encoding="UTF-8"?>
        <projectDescription>
            <name>TestJar</name>
            <comment></comment>
            <projects>
            </projects>
            <buildSpec>
                <buildCommand>
                    <name>org.eclipse.jdt.core.javabuilder</name>
                    <arguments>
                    </arguments>
                </buildCommand>
            </buildSpec>
            <natures>
                <nature>org.eclipse.jdt.core.javanature</nature>
            </natures>
        </projectDescription>   
    </file>
    <file = TestJar.class></file>
</archive>

Is there any way I can call that "TestJar" class programmably from my "TestApplication" class?

UPDATE 2014/07/26, 11:37AM

I've used the example written by @Allain Lalonde in How should I load Jars dynamically at runtime? called ClassPathHack

Next, I've merged his solution with suggestion made in this topic by @Doswell, which looks like this:

button.setOnAction(new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent ae) {
        try {
            ClassPathHack.addFile("C:/Users/John Smith/Desktop/TestJar.jar");

            Class<?> clazz = Class.forName("TestJar");
            Stage testJar = (Stage) clazz.newInstance();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } 
    }
})

Everything works perfectly. Thank you!

Community
  • 1
  • 1
bluevoxel
  • 4,978
  • 11
  • 45
  • 63

2 Answers2

2

If the class you want to run from the jar extends/implements something you know about in your code already you can use reflection to load the class but work with it as whatever it is you know it as ie javafx.stage.Stage vs TestJar.

If the JAR is already on the classpath you can just do;

Class<?> clazz = Class.forName("TestJar");
Stage testJar = (Stage)clazz.newInstance();

If you don't have the JAR on the classpath, but do have the path to the jar, you can follow this How should I load a jar And then load the class as above

Community
  • 1
  • 1
Doswell
  • 418
  • 3
  • 11
0

Classloader/security/correct-environment/correct-context concerns aside:

  1. put TestJar.jar on your application's classpath
  2. from your application, invoke

    /*TestJar ignored =*/ new TestJar();
    
0xb304
  • 93
  • 7
  • Sorry, I missed your clarification (preview fail by me). So you want to load TestJar at runtime? If so, you can use java.lang.Class.forName("TestJar") and java.class.Class.newInstance() to instantiate classes based on their name (assuming your classloader can find the class and allows you the privilege) – 0xb304 Jul 25 '14 at 21:38