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.

Advertisements
Posted in Uncategorized | 5 Comments

Fluent-Log API landed in e(fx)clipse

Last week I came a cross Googles FLogger-API and I really liked it.

Back in e(fx)clipse land I started to miss it but because introducing a dependency to other log-frameworks is not possible – I implemented our own fluent log API inspired by Flogger.

So how do you use it:


// if you have a logger
Logger logger = LoggerCreator.createLogger( Sample.class );
FluentLogger flogger = FluentLogger.of( logger );

// if you use @Log
@Inject
@Log
FluentLogger flogger;

// Log something
FluentLogContext debug = flogger.atDebug();
debug.log( "Hello World" );
debug.log( "Hello World with format %s", 10 );
debug.log( () -> "Lazy Hello World" );
debug.log( t -> "Lazy Hello World with Context" + t, o );

// Log with exception
try {
   // ...
} catch( Throwable l ) {
  flogger.atInfo().withException( t ).log( "Hello World" );
}

// Throttle: Only log every 100 log statement
flogger.atInfo().throttleByCount(100)
  .log( "Log every 100 time" );

// Throttle: Only log every minute
flogger.atInfo().throttleByTime(1, TimeUnit.MINUTES)
  .log( "Log every minute" );

// Build your own condition fluent addition
logger.atInfo().with( Throttle::new ).every( 100 )
  .log( "Log every 100 time" )
Posted in e(fx)clipse | 6 Comments

Proper OSGi access restriction warnings for e(fx)clipse maven projects

In the last few days I worked on the last required feature to abandon PDE and use maven (with the bnd-maven-plugin) and m2e.

Does anyone reading my blog know if IntelliJ and Netbeans also have a feature like this?

Posted in e(fx)clipse, Uncategorized | 4 Comments

Cross IDE Dev-Support for “e4 on JavaFX” –¬†We are almost there

In the last few days I made a major step towards supporting all major Java-IDEs (Eclipse, Netbeans, IntelliJ IDEA) to develop “e4 on JavaFX” applications.

The key to this multi-IDE support is that we don’t use the MANIFEST-First approach from PDE but we use:

  • Maven 3.5
  • bnd-maven-plugin
  • A custom maven-plugin for launching in Netbeans and IntelliJ

Eclipse

For every member on your team who likes the Eclipse IDE.

In Eclipse we use a custom launcher who uses the maven-project dependency information to launch OSGi and install all necessary bundles.

Netbeans

For every member on your team who likes Netbeans

As you notice hot-code replacement is not yet working (or maybe I’m using Netbeans wrong?) Is there anything i need to do after having changed the file to hot-swap it?

IntelliJ IDEA

For every member on your team who likes IntelliJ

As you notice hot-code replacement is not yet working (or maybe I’m using IntelliJ IDEA wrong?) Is there anything i need to do after having changed the file to hot-swap it?

Posted in e(fx)clipse, Uncategorized | 10 Comments

Introducing (another/additional) JavaFX TestFramework

Let me start with the statement that TestFX is somewhat the default JUnit-Testframework for JavaFX application and is what we proposed to use to all our customers and our projects until today where we introduce our own one.

There are 2 different problem vectors we have:

  • A licensing issue we have with it at Eclipse.org: TestFX is licensed under EUPL which to me as a software developer looks ok but it looks like the IP-Department at Eclipse.org is not happy about it and refused me to use it. I don’t blame anyone but need to cope with the situation as is!
  • Implementing JUnit-Tests for OSGi-JavaFX applications: This is a pure technical issue and something we could have solved (although it would have meant to change the way TestFX works) but after having hit the licensing problem my motivation to resolve that problem was not really there

Anyways let’s look at what I’ve started to implement as a replacement for TestFX.

BestSolution FX-Test

First of all we decided for now that we don’t integrate the test-framework into e(fx)clipse but treat it as an independent entity at github and release it under EPL from there.

Writing JUnit-Tests is done by subclassing a base class (current only one available is FXComponentTest) and implementing your JUnit-Tests might look like this:

@Test
public void sample() {
  // Search with a css-selector query
  // and generated a click on the button
  rcontroller().cssFirst(".button").get().click();
}

In contrast to TestFX, tests written with our API can not run directly but you need to decided if you want to:

  • Use a specific runner using @RunWith(FXRunner.class)
  • Use a Rule @FXTest and annotate all UI-Test methods with it

which on the plus-side means that the @Test-methods are executed on the JavaFX-UI-Thread (unlike TestFX where they are executed on another thread most like the main-thread)

For more information that a look at the project README.md.

Let me close this post saying that we are very early in the development and things are still in the flux so we are happy for any feedback we get.

Posted in e(fx)clipse | 6 Comments

e(fx)clipse 3.0.0 is released

e(fx)clipse has been released on June 6th but because of the high load of JavaFX projects we are working on I did not have time to write the public release announcement.

In total we have worked on ~100 tickets most of them adding new features. We also moved our dev infrastructure to github.com and while doing that we’ve split the project into 2 repositories:

We believe that moving to github and the upcoming pure maven-build-story we currently work on in an extra branch will make it easier for others to consume our libraries in none OSGi/Eclipse/e4-Projects.

While we are switching our project structure to pure maven you can already consume our libraries from a maven-repository we host ourselves (see http://maven.bestsolution.at/)

Let’s take a short tour through some of the 3.0 highlights.

Support for Java 9

3.0 is the first release who is fully compatible with Java9 and JPMS. Historically we used non-public APIs and even used reflection to eg “hack” DnD in TabFolder. All code that would be broken in Java9 has been reworked to run on Java8 and Java9.

Some of the none public APIs we used in Java8 have been promoted to public API in Java9 and to support both Java 8 and 9 in the same codebase we extracted those into utility class org.eclipse.fx.ui.controls.JavaFXCompatUtil:

Java 8 Java 9 JavaFXCompatUtil
Window#impl_getWindows() Window#getWindows() getAllWindows()
KeyCode#impl_getChar() KeyCode#getChar() getChar(KeyCode)
KeyCode#impl_getCode() KeyCode#getCode() getCode(KeyCode)

Utilities to work with JavaFX Properties

FXBindings

JavaFX has a Bindings class to setup bindings between different Observables but we missed some features we frequently need in our application code hence we added an org.eclipse.fx.core.bindings.FXBindings who eg has:

  • tenaryBinding(ObservableBooleanValue, ObservableValue<T>, ObservableValue<T>) : Binding<T>
    allowing you to defined if-else in a JavaFX binding way
  • concat(ObservableList<? extends A>...) : ListBinding<A>
    to concat the lists to one and keep the target updated when one of the source lists change
  • concat(String, ObservableValue<T>...) : StringBinding
    concat the lists and concat the items with the given delimiter and keep the binding updated
  • bindContent(List<T>, ObservableList<E>, Function<E, T>) : Subscription
    similar to Bindings.bindContent but allows to use a converter function

A very special feature is a BindingStream you can create with bindStream(ObservableValue<T>) who is similar to Bindings.select but is:

  • Typesafe
  • Provide a Property as the leaf

Let’s look at a concrete example.

class Person {
  public ObjectProperty<Address> address();
}

class Address {
  public StringProperty street() { /* ... */ }
}

class UI {

  ObjectProperty<Person> currentPerson = /* ... */;

  TextField street;

  bindUI() {
     street.textProperty().bindBidirectional( 
      FXBindings.bindStream( currentPerson )
        .map( Person::address )
        .collect( 
           FXCollectors.toProperty( Address::street ) 
        )
     );
  }

}

@ContextValue improvements

@ContextValue is IMHO one of the coolest concepts we introduced in e(fx)clipse to make reuseable components. In 3.0 we added a scope-Property who allows you to fix a publishing scope.

As of 3.0 we support:

  • APPLICATION: Publish the value in the IEclipseContext of the application
  • LOCAL: Publish the value in the local IEclipseContext
  • DYNAMIC: Publish the value using IEclipsContext#modify and the application container is responsible to mark the target context

with DYNAMIC as the default.

ThreadSynchronize improvements

Halt program flow

We added a new API to halt the program flow (NOT the event loop) like this:


ThreadSynchronize t = ...;
TextField username = new TextField();

BlockCondition<String> w = new BlockCondition<>();
username.setOnAction( 
  e -> w.release( username.getText() ) );

System.out.println(t.block( w ));

Headless implementation

We at BestSolution use the MVVM-Pattern for our JavaFX applications and there we fetch data from backend-services in none-ui-threads and so we need to synchronize from a Background- to the UI-thread using ThreadSynchronize.

While things work perfectly fine in the real application because the JavaFX Framework is up and running things fall apart when you test your ViewModel in a headless JUnit-Tests. For that purpose there’s now the possibility to create a ThreadSynchronize instance like this:

ThreadSynchronize t = 
  ThreadSynchronize.createBasicThreadSyncronize(
    new EventLoop()
  );
Posted in e(fx)clipse, Uncategorized | 1 Comment

e(fx)clipse runtime library – Working with the (JavaFX) UI-Thread

When developing UI-Application a very frequent task is

  • to synchronizes yourself back from an worker-thread to the UI-Thread
  • schedule task to run in the future
  • Block the program flow and wait for a condition being met (in SWT called spinning the event loop)

JavaFX 8 has a public API for most of those building blocks (only exception is Event-Loop-Spinning who was an internal API in Java8 and is public API in 9) but using higher level API reduces the boilerplate code you need to implement.

Before we start let’s see how you can access to it in your maven-driven projects (I’m not gradle safey enough to show how it works there) you need to

  • add the efxclipse maven repository (at the time of this writing you need to nightly repository because you need at least 3.0.0)
  • add the following dependency
    <dependency>
      <groupId>at.bestsolution.efxclipse.rt</groupId>
      <artifactId>org.eclipse.fx.ui.controls</artifactId>
      <version>3.0.0-SNAPSHOT</version>
    </dependency>
    

Let’s take a look at some of those APIs and how they can help you write better code:

Writing back from worker thread

Let’s suppose we have a service who returns a search result as a CompletableFuture and we want to push the result – once available – to JavaFX ObjectProperty:

import org.eclipse.fx.core.ServiceUtils;

// Lookup the domain service in the service registry
PersonSearchService service = 
  ServiceUtils.getService(PersonSearchService.class);

ObjectProperty<Person> person = ...;

CompletableFuture<Person> result = 
  service.findByName("Tom","Schindl");

Let’s for a second suppose we can interact with our UI-Toolkit on ANY-Thread we could simply write:

result.thenAccept( person::set );

Unfortunately none of the UI-Toolkits I know would support this because they require you to sync on a special thread called the UI-Thread. JavaFX is not different in this aspect.

Plain JavaFX APIs:

result.thenAccept( p -> {
   Platform.runLater( () ->
     person.set(p);
   );
} );

Using e(fx)clipse’ ThreadSynchronize#asyncExec(T,Consumer<T>):

import org.eclipse.fx.core.ThreadSynchronize;

// ...

// Lookup the thready service in the service registry
ThreadSynchronize threadSync = 
  ServiceUtils.getService(ThreadSynchronize.class).get();

result.thenAccept( p -> {
   threadSync.asyncExec( p, person::set );
});

We got rid of the inner lambda and replaced it with a method reference, so the code got more readable but

Using e(fx)clipse’ ThreadSynchronize#wrap(Consumer<T>):

// ...
result.thenAccept( threadSync.wrap(person::set) );

we can get rid of all outer lambda as well and are back to fairly the same code as if we’d not had to worry about thread-synchronization at all.

Reading from a worker thread

Let’s suppose you have a Timer running who wants to read a JavaFX-Property from a TextField in 1 second from now you’d:

  • You’d better rewrite it to use a Timeline so that no thread sync is needed
  • You’d write the following magic lines of code:
TextField textField = ...

Timer t = new Timer();
t.schedule( new TimerTask() {
  @Override
  public void run() {
    CountDownLatch ll = new CountDownLatch(1);
    AtomicReference<String> data = new AtomicReference<>();
    Platform.runLater( () -> {
      data.set( textField.getText() );
      ll.countDown();
    });
    ll.await();
    String d = data.get();
    // further process the data 
  }
}, 1000 );

Let’s for a moment forget that this code fairly dangerous because it might create a dead lock situation (hence JavaFX only provides Platform.runLater(Runnable)) this is a huge amount of code to write! Let’s see what APIs e(fx)clipse has to improve the situation.

Using ThreadSynchronize#syncExec(Runnable):

ThreadSynchronize threadSync = 
  ServiceUtils.getService(ThreadSynchronize.class).get();

public void run() {
  AtomicReference<String> data = new AtomicReference<>();
  threadSync.syncExec( () -> {
    data.set( textField.getText() );
  } );
  String d = data.get();
  // further process the data 
}

Removes the need for the CountDownLatch

Using ThreadSynchronize#syncExec(Callable<V>, V):

public void run() {
  String d = threadSync.syncExec( textField::getText, "" );
}

Removes the need for the AtomicReference

Using ThreadSynchronize#scheduleExecution(long, Runnable):

ExecutorService s = ...;
threadSync.scheduleExecution( 1000, () -> {
   String data = textField.getText();
   s.submit( () -> ... );
} );

Removes the thread synchronization problems arising from Platform.runLater() call

Using ThreadSynchronize#scheduleExecution(long, Callable<T>) : CompletableFuture<T> :

threadSync.scheduleExecution( 1000, textField::getText )
  .thenAcceptAsync( d -> ... );

Removes the lambda and gets you to the wonderful CompletableFuture-API.

Block program flow

Generally speaking halting the program flow is a discouraged software pattern and you’d better work with Future and callbacks like Consumer but there might be (existing) API you have to support who requires you to halt the program flow and continue after a certain condition has been met.

To support such a usecase e(fx)clipse has org.eclipse.fx.ui.controls.Util#waitUntil( BlockCondition blockCondition ) you can use like this


Pane p = ...;

// Ask for name in an overlay
String askForName() {
  BlockCondition<String> condition = new BlockCondition<>();

  TextField t = new TextField();
  Button b = new Button("Proceed");
  b.setOnAction( e -> { condition.release(t.getText()); } );
  HBox box = new HBox(
    new Label("Name:"),
    t,
    b);
  box.setManaged(false);
  box.autosize();
  box.relocate( 
    p.getWidth() / 2 - box.getWidth() / 2, 
    p.getHeight() / 2 - box.getHeight() / 2 );
  p.getChildren().add( box );
  return Util.waitUntil( condition );
}
Posted in e(fx)clipse | 1 Comment