e(fx)clipse 3.7.0 is released

We are happy to announce that e(fx)clipse 3.7.0 has been released. This release contains the following repositories/subprojects:

There are almost no new features (eg the new boxshadow) but only bugfixes who are very important if you use OpenJFX in an OSGi-Environment.

For those of you who already use our pom-First approache the new bits have been pushed to https://maven.bestsolution.at/efxclipse-releases/ and the Sample application at https://github.com/BestSolution-at/e4-efxclipse-maven-sample/tree/efxclipse-3.7.0 has been updated to use the latest release.

Posted in Uncategorized | Leave a comment

Browser like BoxShadow for JavaFX coming with e(fx)clipse 3.7.0

Using BoxShadow is a very common thing in modern UIs, so it might not be suprising that designers defining UIs often also use them heavily.

Unfortunately JavaFX has NO 100% compatible effect and even worse one who is closest (DropShadow) leads to a massive performance hit as shown in this video

On the left hand side is a Node who has a DropShadow-Effect applied to it and you notice that once the effect is applied that the animation isn’t smooth any more. On the right hand side you see a new Node we’ll release with e(fx)clipse 3.7.0 who provides a new BoxShadow-Node (named BoxShadow2).

Beside getting a huge performance win, the BoxShadow-Node uses the same semantics the browser counterpart does so you can port CSS definition to your JavaFX-Application.

For completness here’s the code for this demo video.

package org.eclipse.fx.ui.controls.demo;

import org.eclipse.fx.ui.controls.effects.BoxShadow2;

import javafx.animation.Animation;
import javafx.animation.Animation.Status;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.effect.DropShadow;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;

public class InTheShadow extends Application {

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

	@Override
	public void start(Stage primaryStage) throws Exception {
		BorderPane p = new BorderPane();
		p.setPadding(new Insets(20));

		Button shadowMe = new Button("Toggle Shadow");
		p.setTop(shadowMe);

		Region pane;

		if (Boolean.getBoolean("efxclipse-shadow")) {
			BoxShadow2 shadow = new BoxShadow2(createComplexUI());
			shadow.setShadowVisible(false);
			pane = shadow;
		} else {
			pane = new StackPane(createComplexUI());
		}

		p.setCenter(pane);

		shadowMe.setOnAction(evt -> toggleShadow(pane));

		Scene s = new Scene(p, 1200, 800);
		primaryStage.setTitle("efxclipse-shadow: " + Boolean.getBoolean("efxclipse-shadow"));
		primaryStage.setScene(s);
		primaryStage.show();
	}

	private void toggleShadow(Region pane) {
		if (pane instanceof BoxShadow2) {
			BoxShadow2 s = (BoxShadow2) pane;
			s.setShadowVisible(!s.isShadowVisible());
		} else {
			if (pane.getEffect() != null) {
				pane.setEffect(null);
			} else {
				DropShadow dropShadow = new DropShadow();
				dropShadow.setRadius(5.0);
				dropShadow.setOffsetX(3.0);
				dropShadow.setOffsetY(3.0);
				dropShadow.setColor(Color.color(0.4, 0.5, 0.5));
				pane.setEffect(dropShadow);
			}
		}
	}

	private Node createComplexUI() {
		StackPane pane = new StackPane();
		pane.setStyle("-fx-background-color: white");

		for (int i = 0; i < 100; i++) {
			Button b = new Button("Button " + i);
			b.setTranslateX(i % 100);
			pane.getChildren().add(b);
		}

		Button animated = new Button("Animated");
		StackPane.setAlignment(animated, Pos.BOTTOM_CENTER);

		TranslateTransition t = new TranslateTransition(Duration.millis(1000), animated);
		t.setAutoReverse(true);
		t.setFromX(-300);
		t.setToX(300);
		t.setCycleCount(Animation.INDEFINITE);
		animated.setOnAction(evt -> {
			if (t.getStatus() == Status.RUNNING) {
				t.pause();
			} else {
				t.play();
			}
		});

		pane.getChildren().add(animated);

		return pane;
	}

}
Posted in e(fx)clipse, Uncategorized | Leave a comment

JavaFlightRecorder-Event Documentation

So while exploring the JFR-Event-Stream-System I came across the problem that there’s no (human readable) documentation what JFR-Events are available in a given JDK-Version.

Erik Gahlin who works on JavaFlightRecorder pointed me to a command one can use to dump the information

The output you get from such a command is something like:

So this is some structured text (well it’s fairly simple Java-Code). So I crafted a simple parser using Xtext:

wrote an HTML-Generator using Xtend, threw some CSS-Framework (https://getuikit.com/) on it to get

Created a github-repo so that you can browse that yourself – https://bestsolution-at.github.io/jfr-doc/openjdk-14.html

Posted in Uncategorized | Leave a comment

Observing your Quarkus Server with “JFR Event Streaming” via Websockets

Having reviewed thousands lines if code last week I had the urgent need to code something for fun and inspired by Gunnar Morlings tweets on “JFR Event Streaming – JEP 349”

I hacked this afternoon in the sun and I’m now able to observe an Quarkus-Server via Websockets retrieving JFR Events rendering them via https://www.chartjs.org/

This is a very early draft implementation who is only able to observe the “jdk.CPULoad”-Event and just a PoC to see if this is possible at all but I’m quite happy with my afternoon work (well now it is already 10pm 🙂

Posted in Uncategorized | Leave a comment

News from DriftFX: Approaching v1.0

It’s been a long time since we gave an update on DriftFX (we announced our PoC roughly 1.5 years ago) but I’m pleased to share with you that we’ve worked hard on it for the past half year to turn the PoC-Code into something we think will get v1.0 of DriftFX.

While our first implementation was focused on the usecase of the main sponsor (and is used in product already) we took the stuff we learned from it and hopefully came up without repeating mistakes we made in our PoC.

Before going into detail let me repeat what DriftFX is (and what it is not):

  • DriftFX is an OpenJFX Node and an API to draw textures within this Node. (the textures could come from anywhere but the main focus is currently OpenGL)
  • DriftFX provides transfer modes to bypass main memory for Windows and MacOS (the texture never leaves the GPU memory)
  • DriftFX is NOT an OpenGL context creation/management library (use one of your own choice e.g. GLFW, ..)
  • DriftFX is NOT an OpenGL API library (use one of your own choice e.g. LWJGL, JOGL, GLEW, ..)
  • DriftFX is NOT a rendering or game engine, its sole purpose is the transport of rendered textures to OpenJFX

So let’s take a closer look what we worked on for the last couple of months:

  • Rewrote the complete internal structure and moved as much code as possible to Java (formerly a lot stuff was in C++)
  • Rewrote the complete API implementing a Swap-Chain approach allowing us to deal with the fact that the old API had to allocate textures on a per frame basis
  • Provided a Java-API so that people don’t have to write Native-Code
  • Provided an CPP-API which wraps the Java-API. It is distributed in source form, so no more linking against DriftFX binaries is needed.
  • Because of the new Java-API direct support for LWJGL is available (there’s been an experimental LWJGL-Support provided by the LWJGL-maintainers)
  • Moved the demos to its own repo, so that we may include samples which are not EPL.
  • Demo Application running out of the box
  • Building with GitHub-Actions and publishing p2 and maven artifacts

The video below shows a LWJGL-Demo we adapted to use DriftFX

You can find the source code of DriftFX at https://github.com/eclipse-efx/efxclipse-drift/ and our Demo-Project at https://github.com/BestSolution-at/efxclipse-drift-samples.

While we are still fixing some details, write some documentation we’d like to get feedback from the wider OpenSource-Community.

Posted in e(fx)clipse, Uncategorized | 1 Comment

Setting up e(fx)clipse RCP development for Java11+ and PDE

As I’m currently converting a Java-8 project to AdoptJDK-11 and JavaFX-11+ I thought it would be a good idea document the steps involved.

Prequisits

I assume you have installed:

Configure your Eclipse

Java Settings

Make AdoptJDK-11 the default JRE unless it is already the default.

Make sure AdoptJDK-11 is used for the Java-SE-11 EE

e(fx)clipse Settings

Open the JavaFX-Preference Page and point it to your JavaFX-11-SDK

This step is required because JavaFX is not part of AdoptJDK-11 and hence Eclipse won’t find the libraries and your code won’t compile inside the IDE (we’ll revisit this topic below once more)

Setup a target platform

Create your project

Bootstrap your project

Check your project setup

Check if Eclipse correctly recognized the javafx-library and magically added them to your plug-in dependendencies

Implement the UI

Add javax.annotation to your MANIFEST.MF

Before you can write the Java-Code for your UI you need to add javax.annotation-package to your bundle (this used to ship with Java-8 has been removed since then)

Create a Java-Class

package my.app.app;

import javax.annotation.PostConstruct;

import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;

public class SamplePart {
  @PostConstruct
  void init(BorderPane root) {
    root.setCenter(
      new Label(System.getProperty("javafx.version"))
    );
  }
}

Adapt your e4xmi

Running your application

While everything happily compiles running the application would fail because in the initial steps we only satisfied the Eclipse compiler by magically injecting the JavaFX-Libraries in your Plug-in-Dependency (see above).

To run the application we need to decide how we’d like to ship JavaFX:

  • next to your application in a folder
  • as part of your eclipse application inside the the plugins-directory
  • you jlink yourself a JDK

We’ll not take a look at the 3rd solution as part of this blog post!

Running with an external folder

Open the generated launch configuration and append -Defxclipse.java-modules.dir=PATH_TO_YOUR_JAVAFX_LIBS in the VM arguments-field

Running with bundled javafx-modules

We provide OSGi-Bundles who contain the original and unmodified JavaFX-Modules (note you can NOT use them are OSGi-Dependencies!) you can use them by adding http://downloads.efxclipse.bestsolution.at/p2-repos/openjfx-11/repository/

Add them to your launch configuration

Exporting your application

The project wizard already generated the basic infrastructure for you but we need to make some small changes. We assume you’ve chosen to option to ship the JavaFX-modules as part of the plugins-directory to keep it simple.

The wizard already added the JavaFX-Standard-Feature into your product-File

It also added the parts to satisfy the compiler in your releng/pom.xml

While most of the stuff is already in place we need to make 2 small modifications:

  • Update the tycho-version property to 1.5.0
  • Change the export environment to match the operation-system(s) you want to target
    • Windows: os=win32, ws=win32, arch=x86_64
    • Linux: os=linux, ws=gtk, arch=x86_64
    • OS-X: os=macosx, ws=cocoa, arch=x86_64

Producing a native launcher

As we anyway have to produce a platform-dependent we can also add the creation of a native launcher. For that open your .product-File:

  • Tick the “The product includes native launcher artifacts”
  • Change the application to main-thread-application

Posted in Uncategorized | 7 Comments

Slides from JavaFX-Days Zürich on e(fx)clipse APIs

If you could not attend my talk at the JavaFX-Days Zürich yesterday or you did and wanted to recap what you’ve presented. Here are the slides.

I enjoyed the conference and I hope you did as well. See you next year!

Posted in Uncategorized | 1 Comment

Announcing e(fx)clipse DriftFX – Integrating Native Rendering Pipelines into JavaFX

I’ve had the honor to introduce DriftFX at todays JavaFX-Days conference in Zürich.

What is DriftFX

DriftFX is a JavaFX extension allowing you to embed native rendering engines (eg OpenGL) into the JavaFX-Scenegraph.

To embed external rendering engines DriftFX exposes a new Node-Type DriftFXSurface one can put at any place in the SceneGraph and treat it like any other JavaFX-Node – you can apply transformations, effects, … on it like you do with any other JavaFX-Node. In general you can think of it as ImageView on steroids.

A more real world example what one can make happen with DriftFX can be seen in this video provide by Netallied a partner company.

What does DriftFX do?

DriftFX allows you to directly embed content rendered by native pipelines into JavaFX WITHOUT going through the Main-Memory. The rendered artifacts stay at the GPU all time!

What DriftFX does not do?

DriftFX is not a rendering engine itself and it does not provide any abstraction layer to write native rendering engines. Its only purpose is to bring your rendered content directly in JavaFX.

What platforms does DriftFX support?

Our Beta implementation currently supports all 3 major Desktop-Systems supported by OpenJFX – Windows, OS-X and Linux.

We currently targeted JavaFX-8 because this is what our customers are running their applications on. We plan to provide support for OpenJFX-11 and future releases of OpenJFX in the week/months to come.

Is DriftFX opensource?

Yes. We’ve been lucky that the sponsors of the work agreed to make DriftFX opensource. Currently there’s an open Pull-Request at Github for the e(fx)clipse project.

Note that it will take some time until the PR is merged. The reason is that we are going to run this through the Eclipse IP-Process to make sure you can safely embed it into your application.

Does DriftFX use internal “APIs”

Yes. It integrates into the JavaFX-Rendering pipeline so it needs to access internals. We are aware that those can change at any time. We are open to contribute DriftFX in future to OpenJFX but it heavily depends on the OpenJFX community and the stewards of OpenJFX.

Is DriftFX production ready?

Propably not 100% yet, we are just about to integrate it into our customers application and fix problems as they arise. So we are somewhere in between Beta and production readiness.

The reason to opensource it now is that we expect the OpenJFX community to take a look and help us improving it. We know that there are many very clever people in the OpenJFX community who can hopefully help us pushing DriftFX forward.

How can you/your company help?

First of all take a look at it and in case it looks interesting get in touch with us to help fund the ongoing work on this matter.

Acknowledgement

First of all I’d like to thank EclipseSource Munich and Netallied for their on going support on DriftFX. Special thank goes out to my co-worker (and partner in crime) Christoph and all other people at BestSolution.at who made DriftFX happen!

Posted in e(fx)clipse | 4 Comments

Building dynamic Java Module System layers to integrate JavaFX 11 in Equinox

One of the most fundamental features of the e(fx)clipse runtime is to integrate JavaFX into the Equinox OSGi-Container and even a running Eclipse IDE.

We currently support the following setups:

  • JavaFX 8
  • JavaFX 9/10
  • JavaFX 11

and the integration for all those versions is a bit different. I don’t want to go into details but starting with JavaFX-11 we need to spin up a new Java-Module-System-Layer at runtime because we can not assume JavaFX being part of the JRE running your OSGi-Container (Eclipse IDE).

Since JavaFX-9 we spin up a dynamic layer to implement JavaFX-SWT-Integration and we adapted that logic for JavaFX-11 to load all JavaFX-11 modules.

The code we have works like this

package javamodules;
import java.lang.module.Configuration;
import java.lang.module.ModuleFinder;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class SimpleLayerCreation {
private static Path JAVAFX_MODULES = Paths.get("/Users/tomschindl/Downloads/javafx-sdk-11/lib");
@SuppressWarnings("deprecation")
public static void main(String[] args) throws Throwable {
Path[] array = Files.list(JAVAFX_MODULES) //
.filter(p -> {
String name = p.getFileName().toString();
return name.startsWith("javafx.") && name.endsWith(".jar");
}) //
.toArray(i -> new Path[i]);
Set<String> modules = Stream.of(array) //
.map(Path::getFileName) //
.map(Path::toString) //
.map(n -> n.substring(0, n.length() - ".jar".length())) //
.collect(Collectors.toSet());
URL[] urls = Stream.of(array).map( Path::toFile).map( f -> {
try {
return f.toURL();
} catch(Throwable t) {
return null;
}
} ).toArray( i -> new URL[i]);
URLClassLoader c = new URLClassLoader(urls, FeatureRichLayerCreation.class.getClassLoader());
ModuleFinder fxModuleFinder = ModuleFinder.of(array);
ModuleFinder empty = ModuleFinder.of(new Path[0]);
ModuleLayer bootLayer = ModuleLayer.boot();
Configuration configuration = bootLayer.configuration();
Configuration newConfiguration = configuration.resolve(fxModuleFinder, empty, modules);
ModuleLayer moduleLayer = bootLayer.defineModulesWithManyLoaders(newConfiguration, FeatureRichLayerCreation.class.getClassLoader());
ClassLoader loader = moduleLayer.findLoader("javafx.base");
System.err.println(loader.loadClass("javafx.beans.property.SimpleStringProperty").newInstance());
System.err.println(loader.loadClass("com.sun.javafx.runtime.VersionInfo").newInstance());
c.close();
}
}
and it works prefectly fine until someone like ControlsFX comes along and does not play by the rules trying to load classes from unexported packages like com.sun.javafx.runtime.VersionInfo.

The standard answer from ControlsFX to fix that problem temporarily is to force the module-system to export them using –add-exports=javafx.base/com.sun.javafx.runtime=ALL-UNNAMED.

Unfortunately this workaround does not work in our case because the command-line flag only allows to modify modules of the Boot-Layer but not those created in dynamic ones like those we construct inside our JavaFX-OSGi integration.

I was investigating yesterday how one could fix this problem but could not come up with a good solution (one that does not call into internals of the module system) until I tweeted

about it and Tom Watson (one of the maintainers of Equinox) pointed me into the right direction.

So the solution is

/*
* For modules on the boot-layer one can add exports using --add-exports but it looks like there's no
* public API to do the same when constructing a custom layer
*/
package javamodules;
import java.lang.ModuleLayer.Controller;
import java.lang.module.Configuration;
import java.lang.module.ModuleFinder;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class FeatureRichLayerCreation {
private static Path JAVAFX_MODULES = Paths.get("/Users/tomschindl/Downloads/javafx-sdk-11/lib");
@SuppressWarnings("deprecation")
public static void main(String[] args) throws Throwable {
System.err.println("VERSION: " + System.getProperty("java.version"));
Path[] array = Files.list(JAVAFX_MODULES) //
.filter(p -> {
String name = p.getFileName().toString();
return name.startsWith("javafx.") && name.endsWith(".jar");
}) //
.toArray(i -> new Path[i]);
Set<String> modules = Stream.of(array) //
.map(Path::getFileName) //
.map(Path::toString) //
.map(n -> n.substring(0, n.length() - ".jar".length())) //
.collect(Collectors.toSet());
URL[] urls = Stream.of(array).map( Path::toFile).map( f -> {
try {
return f.toURL();
} catch(Throwable t) {
return null;
}
} ).toArray( i -> new URL[i]);
URLClassLoader c = new URLClassLoader(urls, FeatureRichLayerCreation.class.getClassLoader()) {
protected java.lang.Class<?> findClass(String moduleName, String name) {
try {
return findClass(name);
} catch (ClassNotFoundException e) {}
return null;
}
protected URL findResource(String moduleName, String name) throws java.io.IOException {
return findResource(name);
}
};
ModuleFinder fxModuleFinder = ModuleFinder.of(array);
ModuleFinder empty = ModuleFinder.of(new Path[0]);
ModuleLayer bootLayer = ModuleLayer.boot();
Configuration configuration = bootLayer.configuration();
Configuration newConfiguration = configuration.resolve(fxModuleFinder, empty, modules);
Controller moduleLayerController = ModuleLayer.defineModules(newConfiguration, Arrays.asList(bootLayer), s -> c);
ModuleLayer moduleLayer = moduleLayerController.layer();
moduleLayerController.addExports(moduleLayer.findModule("javafx.base").get(), "com.sun.javafx.runtime", FeatureRichLayerCreation.class.getModule());
ClassLoader loader = moduleLayer.findLoader("javafx.base");
System.err.println(loader.loadClass("javafx.beans.property.SimpleStringProperty").newInstance());
System.err.println(loader.loadClass("com.sun.javafx.runtime.VersionInfo").newInstance());
Class<?> loadClass = loader.loadClass("com.sun.glass.utils.NativeLibLoader");
System.err.println(loadClass.getModule());
// we need to overwrite findResource(String,String)
System.err.println(loadClass.getResource("NativeLibLoader.class"));
// we need to overwrite findClass(String,String)
System.err.println(Class.forName(moduleLayer.findModule("javafx.base").get(), "javafx.beans.property.SimpleIntegerProperty"));
c.close();
}
}

and now I have to think how we expose that to in our OSGi-Integration.

Posted in Uncategorized | 6 Comments

Supporting OpenJFX 11 from JDK11 onwards in e(fx)clipse

So starting with JDK-11 OpenJFX is not part of any downloadable distribution. As JavaFX is designed to run on the module-path (and tested only there) you have 2 options to run JavaFX inside OSGi:
* You create your own JDK-Distribution using jlink
* You launch the VM you want to use JavaFX adding the JavaFX-Modules

While the 2nd solution is doable for RCP-Applications it is less than a nice one, and for integrating into external frameworks (like the Eclipse IDE) it is not possible at all. So we need a different solution to satisfy both usecases.

The solution to this problem is that e(fx)clipse installs a classloader hook using the Equinox AdapterHook-Framework (you can do crazy stuff with that) and on the fly spins up a new Java-Module-Layer containing all the JavaFX-Modules and uses the classloader from the Module-Layer to load the JavaFX-Classes.

With this strategy you can supply the JavaFX-Modules (including the native bits) required for your application to run as part of your p2-repository.

Posted in Uncategorized | 6 Comments