The Equinox p2 project provides a provisioning infrastructure that can be used to update or install features into an OSGi application.
The previous recipe showed how to add p2 update functionality to an Eclipse 4 application in general. This recipe focuses on how to add an update mechanism to a JavaFX Eclipse 4 application using the e(fx)clipse 2.0.0 Runtime.
Cookware
- JDK 8 Update 40 (minimum)
- This is necessary because that update introduces the JavaFX dialogs API!
- http://www.oracle.com/technetwork/java/javase/downloads/index.html
- Simply run the executable and follow the installation instructions
- e(fx)clipse IDE 2.0.0 (nightly build based on Eclipse Mars at the time of writing this blog post)
- JavaFX tooling and runtime for Eclipse and OSGi
- http://downloads.efxclipse.bestsolution.at/downloads/nightly/sdk/
- Ensure the IDE is started with Java 8 if you have multiple Java versions installed
- Edit the file eclipse.ini which is located in the e(fx)clipse installation directory
- Add the following lines (typically before -showsplash)
-vm C:/Program Files/Java/jre8/bin/javaw.exe
See https://wiki.eclipse.org/Eclipse.ini#Specifying_the_JVM for further information.
- After starting the IDE and choosing a workspace, update the IDE to ensure the latest service release is installed. This is necessary to get the latest bugfixes and security patches.
- Main Menu → Help → Check for Updates
Ingredients
To get started fast with this recipe, you can use the JavaFX version of the Eclipse RCP Cookbook – Basic Recipe we have prepared for you on GitHub .
To use the prepared basic recipe, import the project by cloning the Git repository:
- File → Import → Git → Projects from Git
- Click Next
- Select Clone URI
- Enter URI https://github.com/fipro78/e4-cookbook-basic-recipe-fx.git
- Click Next
- Select the master branch (or the services branch which adds OSGi declarative services)
- Click Next
- Choose a directory where you want to store the checked out sources
- Click Next
- Select Import existing projects
- Click Next
- Click Finish
Preparation
Step 1: Update the Target Platform
- Open the target definition de.codecentric.eclipse.tutorial.target.target in the project de.codecentric.eclipse.tutorial.target
- Remove the Software Site http://download.eclipse.org/efxclipse/runtime-released/1.2.0/site by selecting it in the Locations section and then clicking Remove
- Add a new Software Site by clicking Add… in the Locations section
- Select Software Site
- Software Site for the e(fx)clipse 2.0.0 nightly build
http://download.eclipse.org/efxclipse/runtime-nightly/site - Expand FX Target and check Target Platform Feature
- Uncheck Include required software
- Click Finish
- Activate the target platform by clicking Set as Target Platform in the upper right corner of the Target Definition Editor
Step 2: Prepare the application plug-in
To keep it simple, an update handler will be added to the application plug-in.
- Update the bundle dependencies
- Open the file META-INF/MANIFEST.MF in the project de.codecentric.eclipse.tutorial.app
- Switch to the Dependencies tab
- Add the following bundles so we can make use of the extended e(fx)clipse services
- org.eclipse.core.runtime
- org.eclipse.fx.core
- org.eclipse.fx.core.p2
- Update the application model
- Open the file Application.e4xmi in the project de.codecentric.eclipse.tutorial.app
- Add a command
- Application → Commands → Add
- Set Name to Update EFX
- Set ID to de.codecentric.eclipse.tutorial.app.command.updateEfx
- Add a handler
- Application → Handlers → Add
- Set ID to de.codecentric.eclipse.tutorial.app.handler.updateEfx
- Set the Command reference to de.codecentric.eclipse.tutorial.app.command.updateEfx via Find… dialog
- Create a handler implementation by clicking on the Class URI link
- Set Package to de.codecentric.eclipse.tutorial.app.handler
- Set Name to FXUpdateHandler
- Add a main menu to the application for making the update command accessible for a user
- Application → Windows and Dialogs → Trimmed Window
- Check Main Menu in the details view of the Trimmed Window
- Select the now visible Main Menu in the Application Model tree below the Trimmed Window
- Set ID to org.eclipse.ui.main.menu
- Add a Menu
- Set ID to org.eclipse.ui.file.menu
- Set Label to File
- Add a Handled Menu Item to the File Menu
- Set the label to Update EFX
- Set the Command reference to the Update EFX command via Find … dialog
Step 3: Update the Product Configuration
- Open the file de.codecentric.eclipse.tutorial.app.product in the project de.codecentric.eclipse.tutorial.product
- Switch to the Dependencies tab
- Add
- org.eclipse.equinox.p2.core.feature
- Click Add Required to add the ECF features required by the p2 core feature
- Add
- Switch to the Overview tab
- Ensure that a proper version is set to the Version field, e.g. 1.0.0
Step 4: Implement the update handler
At this point we start making use of the extended OSGi declarative services that are provided by the e(fx)clipse runtime. Since we will use the org.eclipse.fx.core.update.UpdateService which abstracts out the p2 API, performing a p2 update basically consists of two steps (instead of three):
- Check if there are available updates for the specified update operation
- Perform the update by executing a provisioning job if updates are available
For a good user experience it is best practice to execute those operations in background threads to keep the UI responsive. To keep the focus on p2 updates in this recipe, I will not go into the details of background processing here. But we will show the usage of the extended org.eclipse.fx.ui.services.sync.UISynchronize
implementation, which will grant us a really nice opportunity for using a Callable
.
You should also consider giving feedback to the user about the update operation results between the steps. For example, if updates are available, the user should be asked whether to perform the update or not. For this the JavaFX dialogs API is used.
4.1 Update handler preparation
- Get the following values injected in
FXUpdateHandler#execute()
org.eclipse.fx.core.update.UpdateService
OSGi service that is used to perform application updates via p2.org.eclipse.fx.ui.services.sync.UISynchronize
Extended helper class for executing code in the UI thread.org.eclipse.fx.ui.services.restart.RestartService
OSGi service that allows to restart the application by granting the ability to clear the persisted state.
1public class FXUpdateHandler {
2
3 @Execute
4 public void execute(
5 UpdateService updateService,
6 UISynchronize sync,
7 RestartService restartService) {
8 ...
9 }
10
11}
4.2 Check if there are updates available
Via UpdateService#checkUpdate(ProgressReporter)
you can check whether updates for the installable units are available or not. It will return a org.eclipse.fx.core.operation.CancelableOperation
on which “handlers” can be registered to be able to react on different results. For example, to give the user feedback if the operation is cancelled, a Runnable
can be registered via CancelableOperation#onCancel(Runnable)
. If an error occurs, feedback can be shown by registering a Consumer
via CancelableOperation#onException(Consumer)
.
1CancelableOperation<Optional<UpdatePlan>> check =
2 updateService.checkUpdate(ProgressReporter.NULLPROGRESS_REPORTER);
3check.onCancel(() -> showMessage(sync, "Operation cancelled"));
4check.onException(t -> {
5 String message = t.getStatus().getMessage();
6 showError(sync, message);
7});
4.3 Check if an update can be performed
When the update check completes, it is possible to check if there are updates available and therefore an update can be performed. This can be done via CancelableOperation#onComplete(Consumer)
, where T is typically of type org.eclipse.fx.core.update.UpdateService.UpdatePlan
, which is the result of the update check. The UpdatePlan
result is wrapped in a java.util.Optional
, so if it is present, an update can be performed.
1check.onComplete((updatePlan) -> { 2 if (!updatePlan.isPresent()) { 3 showMessage(sync, "Nothing to update"); 4 } 5 else { 6 ... 7 } 8});
4.4 Ask the user if an update should be performed
While with the default Eclipse 4 UISynchronize
implementation it is only possible to use Runnable
s that are executed in the UI thread, the extended UISynchronize
of the e(fx)clipse runtime also supports the usage of Callable
s. This way it is possible to show a confirmation dialog in the UI thread, while the update operation in the background thread is not disturbed. Compared with the usage of the Eclipse 4 UISynchronize
, this means there is no need for additional nesting of Runnable
s.
1private boolean showConfirmation(
2 UISynchronize sync, final String title, final String message) {
3 return sync.syncExec(() -> {
4 Alert alert = new Alert(AlertType.CONFIRMATION);
5 alert.setTitle(title);
6 alert.setContentText(message);
7 Optional result = alert.showAndWait();
8 return (result.get() == ButtonType.OK);
9 }, false);
10}
This method can simply be used like the showMessage(UISynchronize, String)
or showError(UISynchronize, String)
methods for simple dialogs.
1if (showConfirmation( 2 sync, 3 "Updates available", 4 "There are updates available. Do you want to install them now?")) { 5 ... 6}
4.5 Perform an update
The update itself can be performed by executing UpdatePlan#runUpdate(ProgressReporter)
, which again returns a CancelableOperation
to be able to react on different operation results.
1CancelableOperation result = 2 updatePlan.get().runUpdate(ProgressReporter.NULLPROGRESS_REPORTER);
4.6 Restart the application after the update is finished
After an update was performed, it is good practice to restart the application so the updates are applied correctly. This can be done using org.eclipse.fx.ui.services.restart.RestartService
. Using this service it is possible to specify whether the persisted state should be cleared, so changes to the application model are visible after the restart.
1result.onComplete((r) -> { 2 if (showConfirmation( 3 sync, 4 "Updates installed, restart?", 5 "Updates have been installed successfully, do you want to restart?")) { 6 7 sync.syncExec(() -> restartService.restart(true)); 8 } 9});
Note: RestartService#restart(boolean)
needs to be executed in the UI thread.
The complete FXUpdateHandler
can be found on GitHub .
Step 5: Configure the repository location
To perform update operations, it is necessary to configure the repositories to check against. You need to specify the artifact repositories, which contain the actual content being installed or managed, and the metadata repositories, which contain the installable units (IUs) that describe things that can be installed, the capabilities they provide, and the requirements they have.
Note: The artifact and metadata repositories don’t need to be at the same locations, but typically they are.
It is possible to configure the repositories programmatically via UpdateOperation#getProvisioningContext().setArtifactRepositories(URI[])
and UpdateOperation#getProvisioningContext().setMetadataRepositories(URI[])
. But the best practice is to configure them via p2.inf configuration files.
- Create the file p2.inf in the project de.codecentric.eclipse.tutorial.product
- Add the following lines to configure the repository locations (e.g. C:/Development/tmp/repository)
- ${#58} is the variable for ‘:’
1instructions.configure=\ 2 addRepository(type:0,location:file${#58}/C${#58}/Development/tmp/repository);\ 3 addRepository(type:1,location:file${#58}/C${#58}/Development/tmp/repository/);
Further information on how to create and configure a p2.inf file can be found here:
Step 6: Export the product
- Open the Product Configuration in the de.codecentric.eclipse.tutorial.product project
- Select the Overview tab
- Click Eclipse Product export wizard in the Exporting section
- Select a directory to export to in the Destination section of the export wizard (e.g. C:/Development/tmp/base_export)
- Ensure Generate p2 repository is checked
- This also creates the p2 cache that is necessary to make updates work
- Copy the export to another directory (e.g. C:/Development/tmp/app)
- Leave the other options unchanged
- Click Finish
Note: If you face issues on exporting regarding a missing plug-in dependency, try to add org.eclipse.equinox.concurrent to the Plug-ins section of de.codecentric.eclipse.tutorial.feature. This is due to platform changes explained here .
Note: The export works only for Windows and Linux if the Eclipse Platform Launcher Executables are added to the Target Definition. Mac users should stick to exporting without native launchers and start the application via command line or create a deliverable using the JavaFX Packager Tool. Futher information on both ways can be found in the SWT to JavaFX migration recipe .
Step 7: Create an application update
- Open the file Application.e4xmi in the project de.codecentric.eclipse.tutorial.app
- Add an exit command
- Application → Commands → Add
- Set Name to Exit
- Set ID to de.codecentric.eclipse.tutorial.app.command.exit
- Add a handler
- Application → Handlers → Add
- Set ID to de.codecentric.eclipse.tutorial.app.handler.exit
- Set the Command reference to de.codecentric.eclipse.tutorial.app.command.exit via Find… dialog
- Create a handler implementation by clicking on the Class URI link
- Set Package to de.codecentric.eclipse.tutorial.app.handler
- Set Name to ExitHandler
1@Execute 2public void execute(IWorkbench workbench) { 3 workbench.close(); 4}
- Add a Handled Menu Item to the File Menu
- Set the label to Exit
- Set the Command reference to the Exit command via Find … dialog
- Increase the version of the plug-in de.codecentric.eclipse.tutorial.app
- 1.0.1.qualifier
- Increase the version of the feature de.codecentric.eclipse.tutorial.feature
- 1.0.1.qualifier
- Update the product configuration in de.codecentric.eclipse.tutorial.product
- Increase the version of the product to 1.0.1
- Update product feature dependencies
- Edit Properties of the feature de.codecentric.eclipse.tutorial.feature and set the version to 1.0.1.qualifier
- Export the updated product
- Open the Product Configuration in the de.codecentric.eclipse.tutorial.product project
- Select the Overview tab
- Click Eclipse Product export wizard in the Exporting section
- Select a directory to export to in the Destination section of the export wizard (e.g. C:/Development/tmp/update_export)
- Ensure Generate p2 repository is checked
- Click Finish
- Copy the generated p2 repository located at C:/Development/tmp/update_export/repository to (e.g. C:/Development/tmp/repository)
Step 8: Taste
- Start the application that was exported first via C:/Development/tmp/app/eclipse/eclipse.exe
- Execute the update via File → FXUpdate
- Check that an update is performed and that after the restart an Exit menu item is available in the File menu
This recipe showed how to make use of the extended e(fx)clipse services to perform a p2 update of an e(fx)clipse application. Using those services makes it a lot easier to perform an application update using p2. And it also adds great features like restarting with clearing the workspace and executing Callable
s on the UI thread.
You can find the full source code of this recipe on GitHub . The p2 branch contains the sources to create the base export with the p2 update handler. The p2_update branch contains the sources with a simple modification to verify the update.
Appendix:
It is also possible to use the above recipe in an SWT based application with some slight modifications. As explained in Add JavaFX controls to a SWT Eclipse 4 application , you need to add the RCP e4 Target Platform Feature of the e(fx)clipse software site to the target definition. The UpdateService
is UI toolkit neutral so it can simply be used as described above. The extended UISynchronize
is not UI toolkit neutral, but the e(fx)clipse 2.0.0 runtime offers the ThreadSynchronize
service which is implemented in the org.eclipse.fx.ui.di.interopt plug-in. To use a Callable
in an SWT based Eclipse 4 application, you can therefore use ThreadSynchronize
instead of UISynchronize
.
The RestartService
is not simply usable in an SWT based Eclipse 4 application, and there is no interopt implementation because of the tight startup integration. You can, however, make use of a similar RestartService
created and provided by Tom Schindl .
- Add a new Software Site to the target definition
- http://downloads.foss.bestsolution.at/e4-extensions/nightly/site
- Add the following feature to the product dependencies
- at.bestsolution.e4.extensions.feature
- Add the following plug-in to the dependencies of the de.codecentric.eclipse.tutorial.app plug-in
- at.bestsolution.e4.extensions.core.services
- Modify the
FXUpdateHandler
to import and useat.bestsolution.e4.extensions.core.services.RestartService
1import java.util.Optional;
2
3import org.eclipse.e4.core.di.annotations.Execute;
4import org.eclipse.fx.core.ProgressReporter;
5import org.eclipse.fx.core.ThreadSynchronize;
6import org.eclipse.fx.core.operation.CancelableOperation;
7import org.eclipse.fx.core.update.UpdateService;
8import org.eclipse.fx.core.update.UpdateService.UpdatePlan;
9import org.eclipse.fx.core.update.UpdateService.UpdateResult;
10import org.eclipse.jface.dialogs.MessageDialog;
11
12import at.bestsolution.e4.extensions.core.services.RestartService;
13
14public class FXUpdateHandler {
15
16 @Execute
17 public void execute(
18 UpdateService updateService,
19 ThreadSynchronize sync,
20 RestartService restartService) {
21 ...
22}
- Register a lifecycle handler or addon to be able to clear on restart
- e.g. add a property to the org.eclipse.core.runtime.products extension point in the de.codecentric.eclipse.tutorial.app plug-in
- name: lifeCycleURI
- value: bundleclass://at.bestsolution.e4.extensions.lifecycle/at.bestsolution.e4.extensions.lifecycle.ExtensibleLifecycle
- e.g. add a property to the org.eclipse.core.runtime.products extension point in the de.codecentric.eclipse.tutorial.app plug-in
The appendix sources are also available on GitHub .
Hopefully the above and even more of the general services will soon be ported to the Eclipse platform itself, so even more people will get to use those really valuable services!
More articles
fromDirk Fauth
Your job at codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog author
Dirk Fauth
Do you still have questions? Just send me a message.
Do you still have questions? Just send me a message.