Executing a new JavaFX application on a separate JVM - java

I'm trying to launch a new process on a separate JVM via code following the method illustrated here:
Executing a Java application in a separate process
The code I'm using is the following (taken from the question above):
public static int exec(Class klass) throws IOException, InterruptedException {
String javaHome = System.getProperty("java.home");
String javaBin = javaHome +
File.separator + "bin" +
File.separator + "java";
String classpath = System.getProperty("java.class.path");
String className = klass.getName();
ProcessBuilder builder = new ProcessBuilder(javaBin,"-cp",classpath,className);
Process process = builder.inheritIO().start();
process.waitFor();
return process.exitValue();
}
...in which klass is the class I want to launch.
This would work for a normal Java process, but the problem is that I'm trying to launch a JavaFX application, and the code above generates the following error:
Error: JavaFX runtime components are missing, and are required to run this application
So, to add the JavaFX modules, I tried including the --module-path and --add-modules commands in the declaration of builder, I even attempted copying and pasting the entire execution command, and I kept getting this other error:
Unrecognized option: (command string with modules)
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
How could I solve this?
Let me know if details are needed.

Since the advent of modules, there are at least three different ways a JavaFX application can be configured:
Put everything on the module-path, including the JavaFX modules and your module.
This is the ideal situation but not always possible/viable (e.g. because of incompatible dependencies).
Put the JavaFX modules on the module-path and your own code on the class-path.
This configuration requires the use of --add-modules.
Put everything on the class-path, including the JavaFX modules and your own code.
With this configuration your main class cannot be a subtype of Application. Otherwise you get the error you mentioned in your question: "Error: JavaFX runtime components are missing, and are required to run this application".
This configuration allows for easy use of so-called fat/uber JARs.
Warning: This approach is explicitly unsupported.
The command line used with ProcessBuilder will depend on which configuration your application uses. You also have to take into account any other options passed the command line, such as the default encoding or locale. Unfortunately, your question doesn't provide enough information to tell what exactly is going wrong. The error you mention makes me think you're using the third configuration, but I can't be sure.
That said, I'll give some examples of launching the same application from within the application; you should be able to modify things to fit your needs. Note I used Java/JavaFX 13.0.1 when testing the below code.
Configuration #1
Put everything on the module-path.
module-info.java:
module app {
requires javafx.controls;
exports com.example.app to
javafx.graphics;
}
Main.java:
package com.example.app;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import static java.lang.System.getProperty;
public class Main extends Application {
private static void launchProcess() {
try {
new ProcessBuilder(
Path.of(getProperty("java.home"), "bin", "java").toString(),
"--module-path",
getProperty("jdk.module.path"),
"--module",
getProperty("jdk.module.main") + "/" + getProperty("jdk.module.main.class"))
.inheritIO()
.start();
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
#Override
public void start(Stage primaryStage) {
Button launchBtn = new Button("Launch process");
launchBtn.setOnAction(
event -> {
event.consume();
launchProcess();
});
primaryStage.setScene(new Scene(new StackPane(launchBtn), 500, 300));
primaryStage.setTitle("Multi-Process Example");
primaryStage.show();
}
}
Command line:
java --module-path <PATH> --module app/com.example.app.Main
Replace "<PATH>" with a path containing both the JavaFX modules and the above module.
Configuration #2
Put JavaFX modules on the module-path and your code on the class-path.
Main.java:
package com.example.app;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import static java.lang.System.getProperty;
public class Main extends Application {
private static void launchProcess() {
try {
new ProcessBuilder(
Path.of(getProperty("java.home"), "bin", "java").toString(),
"--module-path",
getProperty("jdk.module.path"),
"--add-modules",
"javafx.controls",
"--class-path",
getProperty("java.class.path"),
Main.class.getName())
.inheritIO()
.start();
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
#Override
public void start(Stage primaryStage) {
Button launchBtn = new Button("Launch process");
launchBtn.setOnAction(
event -> {
event.consume();
launchProcess();
});
primaryStage.setScene(new Scene(new StackPane(launchBtn), 500, 300));
primaryStage.setTitle("Multi-Process Example");
primaryStage.show();
}
}
Command line:
java --module-path <M_PATH> --add-modules javafx.controls --class-path <C_PATH> com.example.app.Main
Replace "<M_PATH>" with a path containing the JavaFX modules and replace "<C_PATH>" with a path containing the above code.
Configuration #3
Put everything on the class-path. Note the main class (now Launcher) is not a subclass of Application.
Launcher.java:
package com.example.app;
import javafx.application.Application;
public class Launcher {
public static void main(String[] args) {
Application.launch(Main.class, args);
}
}
Main.java:
package com.example.app;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import static java.lang.System.getProperty;
public class Main extends Application {
private static void launchProcess() {
try {
new ProcessBuilder(
Path.of(getProperty("java.home"), "bin", "java").toString(),
"--class-path",
getProperty("java.class.path"),
Launcher.class.getName())
.inheritIO()
.start();
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
#Override
public void start(Stage primaryStage) {
Button launchBtn = new Button("Launch process");
launchBtn.setOnAction(
event -> {
event.consume();
launchProcess();
});
primaryStage.setScene(new Scene(new StackPane(launchBtn), 500, 300));
primaryStage.setTitle("Multi-Process Example");
primaryStage.show();
}
}
Command line:
java --class-path <PATH> com.example.app.Launcher
Replace "<PATH>" with a path containing the JavaFX JARs and the above code.

Related

Couldn't find or load main class in JavaFx sample [duplicate]

I'm trying to build a small user interface using JavaFX, But I'm getting an error like this:
Error: Could not find or load main class myApp Caused by:
java.lang.NoClassDefFoundError: javafx/application/Application
This is my code:
and im using jdk 12.0.2
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class myApp extends Application{
public static void main(String[] args) {
Phonebook mybook = new Phonebook();
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Group group = new Group();
Scene scene = new Scene(group, 600, 300);
scene.setFill(Color.GRAY);
primaryStage.setTitle("Phone Book");
primaryStage.setScene(scene);
primaryStage.show();
}
This is the Libraries and jdk I'm using:
Image1
I think JavaFX is not a part of the JDK > 9 any more. (Your version is 12.x.x)
Probably this could help you:
https://openjfx.io/openjfx-docs/#install-javafx
(Assuming you are using Maven) If this does not help, try to clean and build your Application.
Sometimes Maven does not recognize newly added dependencies.

Run JavaFX app from the command line without GUI

I have a JavaFX in which the user can select files to be processed. Now I want to automate it so that you can run the application from the command line and pass those files as a parameter. I tried to do this:
java -jar CTester.jar -cl file.txt
public static void main(String[] args)
{
if (Arrays.asList(args).contains("-cl"))
{
foo();
}
else
{
launch(args);
}
}
The main is executed and the argument is correct, but this still creates the GUI.
From the docs:
The entry point for JavaFX applications is the Application class. The
JavaFX runtime does the following, in order, whenever an application
is launched:
Constructs an instance of the specified Application class
Calls the init() method
Calls the start(javafx.stage.Stage) method
Waits for the application to finish, which happens when either of the following
occur:
the application calls Platform.exit()
the last window has been closed and the implicitExit attribute on Platform is true
Calls the stop() method
So if I cannot use the main method, how can I create this "alterantive" flow? I thought about creating a normal java application as a wrapper but that seems a little bit overkill for such a simple task. Is there a more elegant way of doing this?
Simply exit the application after calling your foo() method:
Platform.exit();
Here is a quick sample application to demonstrate:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class CLSample extends Application {
public static void main(String[] args) {
if (Arrays.asList(args).contains("-cl")) {
commandLine();
Platform.exit();
} else {
launch(args);
}
}
public static void commandLine() {
System.out.println("Running only command line version...");
}
#Override
public void start(Stage primaryStage) {
// Simple Interface
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
root.getChildren().add(new Label("GUI Loaded!"));
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("CLSample Sample");
primaryStage.show();
}
}
If you pass -cl, then only the commandLine() method gets called.

Open External Application From JavaFX

I found a way to open a link on default browser using HostServices.
getHostServices().showDocument("http://www.google.com");
Is there any way to open a media in default media player?
Is there any way to launch a specific File or Application?
Generally speaking, you can use Desktop#open(file) to open a file natively as next:
final Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
if (desktop != null && desktop.isSupported(Desktop.Action.OPEN)) {
desktop.open(file);
} else {
throw new UnsupportedOperationException("Open action not supported");
}
Launches the associated application to open the file. If the specified
file is a directory, the file manager of the current platform is
launched to open it.
More specifically, in case of a browser you can use directly Desktop#browse(uri), as next:
final Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {
desktop.browse(uri);
} else {
throw new UnsupportedOperationException("Browse action not supported");
}
Launches the default browser to display a URI. If the default browser
is not able to handle the specified URI, the application registered
for handling URIs of the specified type is invoked. The application is
determined from the protocol and path of the URI, as defined by the
URI class. If the calling thread does not have the necessary
permissions, and this is invoked from within an applet,
AppletContext.showDocument() is used. Similarly, if the calling does
not have the necessary permissions, and this is invoked from within a
Java Web Started application, BasicService.showDocument() is used.
If you want to either open a URL which has an http: scheme in the browser, or open a file using the default application for that file type, the HostServices.showDocument(...) method you referenced provides a "pure JavaFX" way to do this. Note that you can't use this (as far as I can tell) to download a file from a web server and open it with the default application.
To open a file with the default application, you must convert the file to the string representation of the file: URL. Here is a simple example:
import java.io.File;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
public class OpenResourceNatively extends Application {
#Override
public void start(Stage primaryStage) {
TextField textField = new TextField("http://stackoverflow.com/questions/39898704");
Button openURLButton = new Button("Open URL");
EventHandler<ActionEvent> handler = e -> open(textField.getText());
textField.setOnAction(handler);
openURLButton.setOnAction(handler);
FileChooser fileChooser = new FileChooser();
Button openFileButton = new Button("Open File...");
openFileButton.setOnAction(e -> {
File file = fileChooser.showOpenDialog(primaryStage);
if (file != null) {
open(file.toURI().toString());
}
});
VBox root = new VBox(5,
new HBox(new Label("URL:"), textField, openURLButton),
new HBox(openFileButton)
);
root.setPadding(new Insets(20));
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
private void open(String resource) {
getHostServices().showDocument(resource);
}
public static void main(String[] args) {
launch(args);
}
}
Only the solution with java.awt.Desktop worked for me to open a file from JavaFX.
However, at first, my application got stuck and I had to figure out that it is necessary to call Desktop#open(File file) from a new thread. Calling the method from the current thread or the JavaFX application thread Platform#runLater(Runnable runnable) resulted in the application to hang indefinitely without an exception being thrown.
This is a small sample JavaFX application with the working file open solution:
import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
public class FileOpenDemo extends Application {
#Override
public void start(Stage primaryStage) {
final Button button = new Button("Open file");
button.setOnAction(event -> {
final FileChooser fileChooser = new FileChooser();
final File file = fileChooser.showOpenDialog(primaryStage.getOwner());
if (file == null)
return;
System.out.println("File selected: " + file.getName());
if (!Desktop.isDesktopSupported()) {
System.out.println("Desktop not supported");
return;
}
if (!Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) {
System.out.println("File opening not supported");
return;
}
final Task<Void> task = new Task<Void>() {
#Override
public Void call() throws Exception {
try {
Desktop.getDesktop().open(file);
} catch (IOException e) {
System.err.println(e.toString());
}
return null;
}
};
final Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
});
primaryStage.setScene(new Scene(button));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The other proposed solution with javafx.application.HostServices did not work at all. I am using OpenJFX 8u141 on Ubuntu 17.10 amd64 and I got the following exception when invoking HostServices#showDocument(String uri):
java.lang.ClassNotFoundException: com.sun.deploy.uitoolkit.impl.fx.HostServicesFactory
Obviously, JavaFX HostServices is not yet properly implemented on all platforms. On this topic see also: https://github.com/Qabel/qabel-desktop/issues/420

Unable to run the javafx project using command line

I recently had created the javafx project in eclipse, I successfully ran in eclipse and it just works fine.
I wanted to run my project using the command line interface.
I am able to compile successfully but not able to run and it keeps on giving "Error: could not find or load main class Main"
Compile command (Works fine):
javac -cp "C:\Program Files (x86)\Oracle\JavaFX 2.2 Runtime\lib\jfxrt.jar" Main.java
Main.class is created after the above command.
Run Command (doesn't work):
java -cp "C:\Program Files (x86)\Oracle\JavaFX 2.2 Runtime\lib\jfxrt.jar;." Main
I would like to know what am I missing here in order to successfully run it.
Adding Main.java
package application;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
public class Main extends Application {
private Stage primaryStage;
private BorderPane root;
#Override
public void start(Stage primaryStage) {
try {
this.primaryStage = primaryStage;
primaryStage.setTitle("Mojo");
initRootLayout();
showMapLayout();
} catch(Exception e) {
e.printStackTrace();
}
}
private void showMapLayout() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("/application/viewControllers/mapView.fxml"));
AnchorPane mapPane = (AnchorPane)loader.load();
root.setCenter(mapPane);
} catch (IOException e) {
e.printStackTrace();
}
}
private void initRootLayout() {
try{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("/application/viewControllers/RootLayout.fxml"));
root = (BorderPane)loader.load();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}catch(Exception e){
System.out.println(e.getMessage());
}
}
public static void main(String[] args) {
launch(args);
}
}
Your class is declared to be in the package application. So, to compile it so that you get the correct folder structure you should compile with the -d option:
javac -cp "C:\Program Files (x86)\Oracle\JavaFX 2.2 Runtime\lib\jfxrt.jar" -d . Main.java
That should create a directory called application, and Main.class will be in there.
Then execute (from the same folder, not from the application folder) with
java -cp "C:\Program Files (x86)\Oracle\JavaFX 2.2 Runtime\lib\jfxrt.jar";. application.Main

Java choose correct SWT at runtime

I am writing a cross-platform program, and SWT is part of it.
as far as i know i have to select the right SWT library to the right platform and architecture(32 or 64 bit)
so, i need to load SWT jar file at runtime to achieve this. Actually i found a way to do this, but i haven't accomplished it yet.
I take steps following this site
https://www.chrisnewland.com/select-correct-swt-jar-for-your-os-and-jvm-at-runtime-191
but when i run(in eclipse) i get this error
Exception in thread "main" java.lang.Error: Unresolved compilation problems:
Display cannot be resolved to a type
Display cannot be resolved to a type
Shell cannot be resolved to a type
Shell cannot be resolved to a type
Actually, here is the code that i use to test if dynamic loading is success.
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class AddJarAtRuntime {
public static void main(String[] args) {
File swtJar = null;
swtJar = new File("lib/swt-win-x64.jar");
if(swtJar == null)
{
return;
}
addJarToClasspath(swtJar);
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("Hello, world!");
shell.open();
// Set up the event loop.
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
// If no more entries in the event queue
display.sleep();
}
}
display.dispose();
}
public static void addJarToClasspath(File jarFile)
{
try
{
URL url = jarFile.toURI().toURL();
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class<?> urlClass = URLClassLoader.class;
Method method = urlClass.getDeclaredMethod("addURL", new Class<?>[] { URL.class });
method.setAccessible(true);
method.invoke(urlClassLoader, new Object[] { url });
}
catch (Throwable t)
{
t.printStackTrace();
}
}
}
when i compile i get an the errors on lines
Display display = new Display();
and
Shell shell = new Shell(display);
seems like it can't detect the jar file.
i tried to export this project out but it doesn't work too.
Does anybody have idea how i can export the project or make it run correctly?
PS. It would be helpful if somebody provides me the step by step solution.
thanks

Categories

Resources