Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Efxclipse/Runtime/Recipes"

Line 1: Line 1:
 +
<css>
 +
      .mw-code { background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto; }
 +
</css>
 +
 
This page holds best practice recipes when writing JavaFX application using e(fx)clipse
 
This page holds best practice recipes when writing JavaFX application using e(fx)clipse
  
Line 18: Line 22:
 
If you are running on OSGi you can add the <code>org.eclipse.fx.osgi.util</code> bundle which provides access to the <code>LoggerCreator</code> factory class
 
If you are running on OSGi you can add the <code>org.eclipse.fx.osgi.util</code> bundle which provides access to the <code>LoggerCreator</code> factory class
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
import org.eclipse.fx.core.log.Logger;
 
import org.eclipse.fx.core.log.Logger;
Line 29: Line 32:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
=== LoggerFactory Service ===
 
=== LoggerFactory Service ===
Line 35: Line 37:
 
The different logger bundles contribute their <code>LoggerFactory</code> implementation into the OSGi-Registry. In case you are e.g. contributing a service via DS you can get simple add a service reference and you'll get the <code>LoggerFactory</code> with the highest rank injected.
 
The different logger bundles contribute their <code>LoggerFactory</code> implementation into the OSGi-Registry. In case you are e.g. contributing a service via DS you can get simple add a service reference and you'll get the <code>LoggerFactory</code> with the highest rank injected.
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
import org.eclipse.fx.core.log.LoggerFactory;
 
import org.eclipse.fx.core.log.LoggerFactory;
Line 52: Line 53:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; margin-top: 10px; overflow-x: auto;">
 
 
<source lang="xml">
 
<source lang="xml">
 
<?xml version="1.0" encoding="UTF-8"?>
 
<?xml version="1.0" encoding="UTF-8"?>
Line 67: Line 66:
 
</scr:component>
 
</scr:component>
 
</source>
 
</source>
</div>
 
  
 
=== Eclipse DI &amp; @Log annotation ===
 
=== Eclipse DI &amp; @Log annotation ===
Line 73: Line 71:
 
If you make use of Eclipse DI in your code you can get a <code>Logger</code> instance injected with:
 
If you make use of Eclipse DI in your code you can get a <code>Logger</code> instance injected with:
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
import org.eclipse.fx.core.log.Log;
 
import org.eclipse.fx.core.log.Log;
Line 86: Line 83:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
=== Google Guice &amp; @Log annotation ===
 
=== Google Guice &amp; @Log annotation ===
Line 92: Line 88:
 
If you use Guice as the DI container you use the <code>org.eclipse.fx.core.guice</code> bundle to get a Logger injected in your component with:
 
If you use Guice as the DI container you use the <code>org.eclipse.fx.core.guice</code> bundle to get a Logger injected in your component with:
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
import org.eclipse.fx.core.log.Log;
 
import org.eclipse.fx.core.log.Log;
Line 104: Line 99:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
if you have configured your Guice-Module with:
 
if you have configured your Guice-Module with:
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
import com.google.inject.Module;
 
import com.google.inject.Module;
Line 123: Line 116:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
Instead of directly binding to a logger factory you can delegate to the OSGi-Service registry by using <code>OSGiLoggerFactoryProvider</code>:
 
Instead of directly binding to a logger factory you can delegate to the OSGi-Service registry by using <code>OSGiLoggerFactoryProvider</code>:
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
import com.google.inject.Module;
 
import com.google.inject.Module;
Line 142: Line 133:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
== Extending ==
 
== Extending ==
Line 148: Line 138:
 
Like outlined above there are 2 logger implementations available from the e(fx)clipse p2 repository. If you want to use a different logging framework you are able to plug-in your own by implementing <code>LoggerFactory</code> and contributing it to the OSGi-Service-Registry.
 
Like outlined above there are 2 logger implementations available from the e(fx)clipse p2 repository. If you want to use a different logging framework you are able to plug-in your own by implementing <code>LoggerFactory</code> and contributing it to the OSGi-Service-Registry.
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
import javax.inject.Provider;
 
import javax.inject.Provider;
Line 176: Line 165:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
You contribute it to the OSGi-Service registry e.g. by using DS. You should give the service a higher ranking than 1 (which is the ranking of the log4j service) to ensure it is picked when a logger is requested.
 
You contribute it to the OSGi-Service registry e.g. by using DS. You should give the service a higher ranking than 1 (which is the ranking of the log4j service) to ensure it is picked when a logger is requested.
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="xml">
 
<source lang="xml">
 
<?xml version="1.0" encoding="UTF-8"?>
 
<?xml version="1.0" encoding="UTF-8"?>
Line 192: Line 179:
 
</scr:component>
 
</scr:component>
 
</source>
 
</source>
</div>
 
  
 
= Eclipse DI =
 
= Eclipse DI =
Line 202: Line 188:
 
The opposite - publishing a value into the context - is not as easy because your java component will get a dependency on the DI-Container because it needs to access the <code>IEclipseContext</code> directly. e(fx)clipse provides you the possibility to get around this architectual problem by defining an annotation named <code>@ContextValue</code> which marks a slot in <code>IEclipseContext</code> instance which can be used to observe the value and modified.
 
The opposite - publishing a value into the context - is not as easy because your java component will get a dependency on the DI-Container because it needs to access the <code>IEclipseContext</code> directly. e(fx)clipse provides you the possibility to get around this architectual problem by defining an annotation named <code>@ContextValue</code> which marks a slot in <code>IEclipseContext</code> instance which can be used to observe the value and modified.
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
import org.eclipse.fx.core.di.ContextBoundValue;
 
import org.eclipse.fx.core.di.ContextBoundValue;
Line 218: Line 203:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
To make use of this your bundle needs to have a dependency on <code>org.eclipse.fx.core.di</code> and your runtime has to include <code>org.eclipse.fx.core.di.context</code>.
 
To make use of this your bundle needs to have a dependency on <code>org.eclipse.fx.core.di</code> and your runtime has to include <code>org.eclipse.fx.core.di.context</code>.
Line 226: Line 210:
 
A reoccuring pattern when developing with Eclipse Databinding and Dependency Injection is that the injected value is used as the master in master-detail binding scenario.
 
A reoccuring pattern when developing with Eclipse Databinding and Dependency Injection is that the injected value is used as the master in master-detail binding scenario.
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
import org.eclipse.core.databinding.observable.value.IObservableValue;
 
import org.eclipse.core.databinding.observable.value.IObservableValue;
Line 246: Line 229:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
The <code>@ContextValue</code> framework introduce in the recipe above is able to reduce the code to
 
The <code>@ContextValue</code> framework introduce in the recipe above is able to reduce the code to
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
import org.eclipse.core.databinding.observable.value.IObservableValue;
 
import org.eclipse.core.databinding.observable.value.IObservableValue;
Line 262: Line 243:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
If you prefer the JavaFX observable system you get let the system inject you this type as well
 
If you prefer the JavaFX observable system you get let the system inject you this type as well
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
import javafx.beans.property.Property;
 
import javafx.beans.property.Property;
Line 278: Line 257:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
Generally speaking you can use any type for the value injected by <code>@ContextValue</code> as long as there's an adapter registered which is able to convert from <code>ContextBoundValue</code> to it.
 
Generally speaking you can use any type for the value injected by <code>@ContextValue</code> as long as there's an adapter registered which is able to convert from <code>ContextBoundValue</code> to it.
Line 284: Line 262:
 
An observable value is not a one way street so it also allows publish through it:
 
An observable value is not a one way street so it also allows publish through it:
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
public class ListView {
 
public class ListView {
Line 295: Line 272:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
== @FXMLLoader ==
 
== @FXMLLoader ==
Line 301: Line 277:
 
If you want to use DI in the controller attached to an FXML-File you'd normally use an <code>org.eclipse.fx.ui.di.InjectingFXMLLoader</code> to free you from configuring the loader with information the DI container already knows you can make use of the <code>@org.eclipse.fx.ui.di.FXMLLoader</code> in your components which provides you an <code>org.eclipse.fx.ui.di.FXMLLoaderFactory</code> instance.
 
If you want to use DI in the controller attached to an FXML-File you'd normally use an <code>org.eclipse.fx.ui.di.InjectingFXMLLoader</code> to free you from configuring the loader with information the DI container already knows you can make use of the <code>@org.eclipse.fx.ui.di.FXMLLoader</code> in your components which provides you an <code>org.eclipse.fx.ui.di.FXMLLoaderFactory</code> instance.
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
public class MyLoginView {
 
public class MyLoginView {
Line 311: Line 286:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
and now you can use <code>@Inject</code> in your controller:
 
and now you can use <code>@Inject</code> in your controller:
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
public class MyLoginController {
 
public class MyLoginController {
Line 328: Line 301:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
= FXML =
 
= FXML =
Line 336: Line 308:
 
Loading FXML-Files in OSGi is a bit harder than doing it in a standard java application and one can not use the static <code>FXMLLoader.load()</code> so we provide an extra class named <code>org.eclipse.fx.osgi.utilOSGiFXMLLoader</code>
 
Loading FXML-Files in OSGi is a bit harder than doing it in a standard java application and one can not use the static <code>FXMLLoader.load()</code> so we provide an extra class named <code>org.eclipse.fx.osgi.utilOSGiFXMLLoader</code>
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
public class MyView {
 
public class MyView {
Line 345: Line 316:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
== DI in FXML controller ==
 
== DI in FXML controller ==
Line 353: Line 323:
 
If you are running in OSGi and with Eclipse DI (e.g. when you are writing an Eclipse 4 Application) you can make use of <code>org.eclipse.fx.ui.di.InjectingFXMLLoader</code>.
 
If you are running in OSGi and with Eclipse DI (e.g. when you are writing an Eclipse 4 Application) you can make use of <code>org.eclipse.fx.ui.di.InjectingFXMLLoader</code>.
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
public class MyView {
 
public class MyView {
Line 366: Line 335:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
The above code is perfectly ok although you can get rid of the direct <code>IEclipseContext</code> dependency by using the @FXMLLoader annotation.
 
The above code is perfectly ok although you can get rid of the direct <code>IEclipseContext</code> dependency by using the @FXMLLoader annotation.
Line 374: Line 342:
 
If you are not running on OSGi and Eclipse DI but develop a standard java application which uses Google Guice as the DI container you can make use of <code>org.eclipse.fx.core.guice.InjectingFXMLLoader</code>
 
If you are not running on OSGi and Eclipse DI but develop a standard java application which uses Google Guice as the DI container you can make use of <code>org.eclipse.fx.core.guice.InjectingFXMLLoader</code>
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
public class MyGuiceComponent {
 
public class MyGuiceComponent {
Line 383: Line 350:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
= Adapter System =
 
= Adapter System =
Line 396: Line 362:
 
The simplest usage is if the source-object implements the <code>Adaptable</code> interface
 
The simplest usage is if the source-object implements the <code>Adaptable</code> interface
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
public class Sample {
 
public class Sample {
Line 405: Line 370:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
If the source-object itself does not implement the <code>Adaptable</code> interface or you are the one who has to implement a class which implements <code>Adaptable</code> you have to use the <code>AdapterService</code> whicn is provided through the OSGi-Service-Registry if the object is managed by Eclipse DI it will look like this:
 
If the source-object itself does not implement the <code>Adaptable</code> interface or you are the one who has to implement a class which implements <code>Adaptable</code> you have to use the <code>AdapterService</code> whicn is provided through the OSGi-Service-Registry if the object is managed by Eclipse DI it will look like this:
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
public class Sample {
 
public class Sample {
Line 421: Line 384:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
Otherwise you need to query the OSGi-Service-Registry to get access to the <code>AdapterService</code>.
 
Otherwise you need to query the OSGi-Service-Registry to get access to the <code>AdapterService</code>.
Line 429: Line 391:
 
To enhance the adapter system it is possible to register <code>AdapterProvider</code> as OSGi-Services. All you need to do is to implement the <code>AdapterProvider</code> likes this:
 
To enhance the adapter system it is possible to register <code>AdapterProvider</code> as OSGi-Services. All you need to do is to implement the <code>AdapterProvider</code> likes this:
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="java">
 
<source lang="java">
 
public class CustomerAdapterProvider implements AdapterProvider<Person, Customer> {
 
public class CustomerAdapterProvider implements AdapterProvider<Person, Customer> {
Line 453: Line 414:
 
}
 
}
 
</source>
 
</source>
</div>
 
  
 
and register it e.g. through DS with
 
and register it e.g. through DS with
  
<div style="background-color: #fafafa; padding: 20px; border-color: #ddd; border-width: 3px; border-style: solid; border-radius: 5px; margin-left: 10px; margin-right: 10px; overflow-x: auto;">
 
 
<source lang="xml">
 
<source lang="xml">
 
<?xml version="1.0" encoding="UTF-8"?>
 
<?xml version="1.0" encoding="UTF-8"?>
Line 467: Line 426:
 
</scr:component>
 
</scr:component>
 
</source>
 
</source>
</div>
 

Revision as of 07:59, 7 December 2013


This page holds best practice recipes when writing JavaFX application using e(fx)clipse

Logging

e(fx)clipse has its own logging facade org.eclipse.fx.core.log.Logger which allows to plug-in different log frameworks.

Currently available are:

  • java.util.Logging (default)
  • log4j by adding org.eclipse.fx.core.log4j bundle to your OSGi-Runtime

Usage

There are different ways to use get a logger.

LoggerCreator

If you are running on OSGi you can add the org.eclipse.fx.osgi.util bundle which provides access to the LoggerCreator factory class

import org.eclipse.fx.core.log.Logger;
import org.eclipse.fx.osgi.util.LoggerCreator;
 
public class MyClass {
  private static Logger LOGGER = LoggerCreator.createLogger(MyClass.class);
 
  // ....
}

LoggerFactory Service

The different logger bundles contribute their LoggerFactory implementation into the OSGi-Registry. In case you are e.g. contributing a service via DS you can get simple add a service reference and you'll get the LoggerFactory with the highest rank injected.

import org.eclipse.fx.core.log.LoggerFactory;
import org.eclipse.fx.core.log.Logger;
 
public class MyServiceImpl implements MyService {
  private Logger logger;
 
  public void setLoggerFactory(LoggerFactory factory) {
    this.logger = factory.createLogger(MyService.class.getName());
  }
 
  public void unsetLoggerFactory(LoggerFactory factory) {
 
  }
}
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="myservice">
   <implementation class="impl.MyServiceImpl"/>
   <service>
      <provide interface="service.MyService"/>
   </service>
   <reference bind="setLoggerFactory" cardinality="1..1" 
              interface="org.eclipse.fx.core.log.LoggerFactory" 
              name="LoggerFactory" policy="static" unbind="unsetLoggerFactory"/>
</scr:component>

Eclipse DI & @Log annotation

If you make use of Eclipse DI in your code you can get a Logger instance injected with:

import org.eclipse.fx.core.log.Log;
import org.eclipse.fx.core.log.Logger;
import javax.inject.Inject;
 
public class MyDIComponent {
 
  @Inject
  @Log
  Logger logger;
}

Google Guice & @Log annotation

If you use Guice as the DI container you use the org.eclipse.fx.core.guice bundle to get a Logger injected in your component with:

import org.eclipse.fx.core.log.Log;
import org.eclipse.fx.core.log.Logger;
import javax.inject.Inject;
 
public class MyDIComponent {
 
  @Log
  Logger logger;
}

if you have configured your Guice-Module with:

import com.google.inject.Module;
import com.google.inject.Binder;
import org.eclipse.fx.core.log.LoggerFactory;
import org.eclipse.fx.core.log4j.Log4JLoggerFactory;
import org.eclipse.fx.core.guice.FXLoggerListener;
 
public class MyModule implements Module {
  public void configure(Binder binder) {
    binder.bind(LoggerFactory.class).toProvider(Log4JLoggerFactory.class); // or JUtilLoggerFactory
    binder.bindListener(Matchers.any(), new FXLoggerListener());
  }
}

Instead of directly binding to a logger factory you can delegate to the OSGi-Service registry by using OSGiLoggerFactoryProvider:

import com.google.inject.Module;
import com.google.inject.Binder;
import org.eclipse.fx.core.log.LoggerFactory;
import org.eclipse.fx.core.guice.FXLoggerListener;
import org.eclipse.fx.core.guice.OSGiLoggerFactoryProvider;
 
public class MyModule implements Module {
  public void configure(Binder binder) {
    binder.bind(LoggerFactory.class).toProvider(OSGiLoggerFactoryProvider.class);
    binder.bindListener(Matchers.any(), new FXLoggerListener());
  }
}

Extending

Like outlined above there are 2 logger implementations available from the e(fx)clipse p2 repository. If you want to use a different logging framework you are able to plug-in your own by implementing LoggerFactory and contributing it to the OSGi-Service-Registry.

import javax.inject.Provider;
import org.eclipse.fx.core.log.LoggerFactory;
import org.eclipse.fx.core.log.Logger;
 
public class MyLoggerFactory implements LoggerFactory, Provider<LoggerFactory> {
  @Override
  public LoggerFactory get() {
    return this;
  }
 
  @Override
  public Logger createLogger(String name) {
    return new LoggerImpl(name);
  }
 
  static class LoggerImpl implements Logger {
    private String name;
 
    public LoggerImpl(String name) {
      this.name = name;
    }
 
    // ....
  }
}

You contribute it to the OSGi-Service registry e.g. by using DS. You should give the service a higher ranking than 1 (which is the ranking of the log4j service) to ensure it is picked when a logger is requested.

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" 
               name="my.logger.framework.factory">
   <implementation class="my.logger.framework.MyLoggerFactory"/>
   <property name="service.ranking" type="Integer" value="2"/>
   <service>
      <provide interface="org.eclipse.fx.core.log.LoggerFactory"/>
   </service>
</scr:component>

Eclipse DI

Publishing to the IEclipseContext

The IEclipseContext is the central component of the Eclipse DI container. Retrieving values from it is as easy as writing @Inject in your java class and the DI container will fill it with a value and keep it up-to-date if you used field or method injection.

The opposite - publishing a value into the context - is not as easy because your java component will get a dependency on the DI-Container because it needs to access the IEclipseContext directly. e(fx)clipse provides you the possibility to get around this architectual problem by defining an annotation named @ContextValue which marks a slot in IEclipseContext instance which can be used to observe the value and modified.

import org.eclipse.fx.core.di.ContextBoundValue;
import org.eclipse.fx.core.di.ContextValue;
import javax.inject.Inject;
 
public static class SimpleInject {
  @Inject
  @ContextValue(contextKey="user")
  public ContextBoundValue<String> value;
 
  private void updateValue() {
    value.publish("tomschindl");
  }
}

To make use of this your bundle needs to have a dependency on org.eclipse.fx.core.di and your runtime has to include org.eclipse.fx.core.di.context.

Injected value as an observable

A reoccuring pattern when developing with Eclipse Databinding and Dependency Injection is that the injected value is used as the master in master-detail binding scenario.

import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.WritableValue;
import org.eclipse.fx.core.di.ContextBoundValue;
import org.eclipse.fx.core.di.ContextValue;
 
public class DetailView {
  private IObservableValue master = new WritableValue();
 
  @Inject
  public void update(Person p) {
    master.getRealm().exec(new Runnable() {
       public void run() {
         master.setValue(p);
       }
    }); 
  }
}

The @ContextValue framework introduce in the recipe above is able to reduce the code to

import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.fx.core.di.ContextBoundValue;
import org.eclipse.fx.core.di.ContextValue;
 
public class DetailView {
  @Inject
  @ContextValue("my.domain.Person")
  IObservableValue master; 
}

If you prefer the JavaFX observable system you get let the system inject you this type as well

import javafx.beans.property.Property;
import org.eclipse.fx.core.di.ContextBoundValue;
import org.eclipse.fx.core.di.ContextValue;
 
public class DetailView {
  @Inject
  @ContextValue("my.domain.Person")
  Property<Person> property;
}

Generally speaking you can use any type for the value injected by @ContextValue as long as there's an adapter registered which is able to convert from ContextBoundValue to it.

An observable value is not a one way street so it also allows publish through it:

public class ListView {
  @PostConstruct
  public void initUI(@ContextValue("my.domain.Person") Property<Person> property) {
    ListView<Person> v = new ListView<>();
    // ...
    property.bind(v.getSelectionModel().selectedItemProperty());
  }
}

@FXMLLoader

If you want to use DI in the controller attached to an FXML-File you'd normally use an org.eclipse.fx.ui.di.InjectingFXMLLoader to free you from configuring the loader with information the DI container already knows you can make use of the @org.eclipse.fx.ui.di.FXMLLoader in your components which provides you an org.eclipse.fx.ui.di.FXMLLoaderFactory instance.

public class MyLoginView {
  @PostConstruct
  void init(BorderPane p, @FXMLLoader FXMLLoaderFactory factory) {
    GridPane p = factory.loadRequestorRelative("myLoginView.fxml").build();
    // ...
  }
}

and now you can use @Inject in your controller:

public class MyLoginController {
  @Inject
  MyLoginService loginService;
 
  @FXML
  TextField username;
 
  @FXML
  TextField password;
}

FXML

FXML in OSGi

Loading FXML-Files in OSGi is a bit harder than doing it in a standard java application and one can not use the static FXMLLoader.load() so we provide an extra class named org.eclipse.fx.osgi.utilOSGiFXMLLoader

public class MyView {
  @PostConstruct
  public void init(BorderPane p) {
    Node n = OSGiFXMLLoader.load(getClass(), "myView.fxml", null, null);
  }
}

DI in FXML controller

Eclipse DI

If you are running in OSGi and with Eclipse DI (e.g. when you are writing an Eclipse 4 Application) you can make use of org.eclipse.fx.ui.di.InjectingFXMLLoader.

public class MyView {
  @PostConstruct
  public void initUI(BorderPane p, IEclipseContext context) {
    InjectingFXMLLoader<Node> iFXMLLoader = InjectingFXMLLoader.create(
      context, getClass(), "myView.fxml"
    );
    Node n = iFXMLLoader.load();
    // ...
  }
}

The above code is perfectly ok although you can get rid of the direct IEclipseContext dependency by using the @FXMLLoader annotation.

Google Guice

If you are not running on OSGi and Eclipse DI but develop a standard java application which uses Google Guice as the DI container you can make use of org.eclipse.fx.core.guice.InjectingFXMLLoader

public class MyGuiceComponent {
   public void init(Injector injector) {
     Node n = InjectingFXMLLoader.loadFXML(injector,getClass().getResource("myui.fxml"));
     // ...
   }
}

Adapter System

The @ContextValue support introduce above makes use of the adapter system provided by e(fx)clipse core runtime bundle (org.eclipse.fx.core) which constits of the 3 main interfaces:

  • Adaptable
  • AdapterService
  • AdapterProvider

Usage

The simplest usage is if the source-object implements the Adaptable interface

public class Sample {
  private void sellProduct(Person person) {
     Customer customer = person.adapt(Customer.class);
     // ...
  }
}

If the source-object itself does not implement the Adaptable interface or you are the one who has to implement a class which implements Adaptable you have to use the AdapterService whicn is provided through the OSGi-Service-Registry if the object is managed by Eclipse DI it will look like this:

public class Sample {
  @Inject
  AdapterService adapterService;
 
  private void sellProduct(Person person) {
    Customer customer = adapterService.adapt(person,Customer.class);
    // ...
  }
}

Otherwise you need to query the OSGi-Service-Registry to get access to the AdapterService.

Extending

To enhance the adapter system it is possible to register AdapterProvider as OSGi-Services. All you need to do is to implement the AdapterProvider likes this:

public class CustomerAdapterProvider implements AdapterProvider<Person, Customer> {
  @Override
  public Class<Person> getSourceType() {
    return Person.class;
  }
 
  @Override
  public Class<Customer> getTargetType() {
    return Customer.class;
  }
 
  @Override
  public boolean canAdapt(Person sourceObject, Class<Customer> targetType) {
    return true;
  }
 
  @Override
  public Customer adapt(final Person sourceObject, Class<Customer> targetType, ValueAccess... valueAccess) {
    // ...
  }
}

and register it e.g. through DS with

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="my.adapter.sample.customer">
   <implementation class="my.adapter.sample.CustomerAdapterProvider"/>
   <service>
      <provide interface="org.eclipse.fx.core.adapter.AdapterProvider"/>
   </service>
</scr:component>

Back to the top