Riena - A New Adventure In Eclipse
Join the DZone community and get the full member experience.
Join For FreeThe Riena project has just released it's 1.0 version recently. In this article, I'll show you how to get going with Riena by walking you through my own experiences using the project. As well as providing a little background on the project I will show how to create a UI with Riena, and run through an example of remote services.
Some Background
First, what is Riena? I wrote an about Riena last August, discussing how it adds a new dimension to Eclipse RCP applications by providing a more "friendly" user interface to non-engineers. But the main concept behind Riena is to provide a foundation for building multi-tier enterprise client/server application. Riena makes the usage of OSGi (Equinox) services easier by providing access to both the local and remote services in a transparent way. So, no matter where your OSGi bundle is, whether on the client or the server, you can access it the same way through Riena.
The second release of Riena is already in planning and will include enhanced features and components for the development of user interfaces and navigation.
The complete list of concepts and features in Riena are pretty complete. They include:
- Injecting Services and Extensions
- Remote Services
- Security
- ObjectTransaction
- Stages
- Client Monitoring
- Navigation
- Look and Feel
- UI Filters
- Login support
- Custom Ridgets
Getting Started
Now that we know a bit about the project, it's time to take it for a test drive. For this example I am using Eclipse 3.5M6. To get started there are two approaches that you can take. One is to download the full Riena target platform including Equinox and SDK from http://www.eclipse.org/downloads/download.php?file=/rt/riena/Riena-1.1.0.M5-platform-win32.win32.x86.zip.
I did that to keep my current Eclipse installation lighter. (Aside: I do that quite often, and have lots of Eclipse installations around my machine). If you just want to add Riena to your current application, you can just add the Riena specific stuff from http://www.eclipse.org/downloads/download.php?file=/rt/riena/Riena-1.0.0.zip
There is an update site available for the UI wizard.The wiki page has the wrong URL for the update site - you'll need to point to http://download.eclipse.org/rt/riena/updatesites/org.eclipse.riena.ui.templates.updatesite/
I'll be using this throughout this tutorial, so it's worth getting this included.
Full, and official, getting started instructions are available from the Riena wiki page.
Setting Up The Target Platform
First we need to specify our target platform for our application to be where Riena is "installed".This step does not apply if you have downloaded the Riena components for your current installation.
Here I have used C:\Riena as my install location:
Clicking Finish here will set up a target definition. Now you can give the target a more friendly name
All you need to do now is to set this target as active for your current plug-in development:
Creating a UI Using Riena
Now that we have installed the Riena UI Wizard, we can see some example code quickly. Here's a step by step to get to one of the Riena project templates:
- Create a new Plug-in Project, but make sure to base it on Eclipse 3.4
- Check "This plug-in will make contributions to the UI" and select Yes to create a rich client application
- You will get four different Riena options in the template list. Here we'll select Riena Mail Template
Once you click Finish you will be presented with your Riena mail project. Let's take a quick look at how the application looks, before we start to investigate the code.
To run the application, simply open the plugin.xml and chooose Launch an Eclipse Application
The following is the screen that you will be presented with - I'm sure you'll agree it's pretty slick, and different to any RCP application that you probably developed so far.
A Different User Interface
The following are the key points in the plugin definition that make Riena UIs different to standard RCP UIs.
- In the plugin manifest, you'll see that as well as including org.eclipse.ui, there is also a dependency on org.eclipse.riena.client
Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime, org.eclipse.riena.client
- The main class of the Riena application is created as an org.eclipse.core.runtime.applications extension in the plugin.xml
<extension id="application" point="org.eclipse.core.runtime.applications"> <application> <run class="com.dzone.riena.tests.Application"> </run> </application> </extension>
We will look at this class in more detail shortly. - An instance of org.eclipse.riena.navigation.ui.swt.views.SubApplicationView is created as a perspective. This will be called later from the main Application class.
Everything else is as in any standard RCP application. The views, menus and handlers are all written as normal. So, let's take a closer look at the Application class.
The class extends org.eclipse.riena.navigation.ui.swt.application.SwtApplication. The key part of this class is the overridden createModel method.
protected IApplicationNode createModel() { ApplicationNode app = new ApplicationNode("Riena Mail"); //NON-NLS-1 ISubApplicationNode subApp = new SubApplicationNode("Your Mail"); //NON-NLS-1 app.addChild(subApp); WorkareaManager.getInstance().registerDefinition(subApp, "rcp.mail.perspective"); //NON-NLS-1 IModuleGroupNode groupMailboxes = new ModuleGroupNode(new NavigationNodeId(Application.ID_GROUP_MBOXES)); subApp.addChild(groupMailboxes); IModuleNode moduleAccount1 = new ModuleNode("me@this.com"); //NON-NLS-1 groupMailboxes.addChild(moduleAccount1); moduleAccount1.setClosable(false); ....... return app; }
The ApplicationNode is represented by the entire application's shell, while the current tab that is open is an ISubApplicationNode. The best way to illustrate this is to add a new node, which we'll name "Search".
ISubApplicationNode searchApp = new SubApplicationNode("Search"); //NON-NLS-1 app.addChild(searchApp);
As I haven't associated a perpective with the sub application, all this will do right now is contribute a tab.
Adding Content To The Search Tab
To add some simple UI content into the search tab, we can take the following short cut:
- In plugin.xml, copy the Riena Perspective and create your own perspective. Just change the name and id of this new perspective. Note, we still use the same class.
<perspective class="org.eclipse.riena.navigation.ui.swt.views.SubApplicationView" id="rcp.search.perspective" name="Search Perspective"> </perspective>
Now we we click on our Search tab, we will see an empty view. As you would expect, the menu and toolbar items remain the same when you click into the Search tab. - Next we need to provide a ViewPart for the Search tab. Again, let's take the quick way out of this and copy the existing view, changing a few of the parameters.
<view allowMultiple="true" class="com.dzone.riena.tests.SearchView" icon="icons/sample2.gif" id="rcp.search.view" name="Search"> </view>
The view class itself extends ViewPart as in any standard RCP application. All we'll do in this class is define the ID as a constant and create some content for the view in the createPartControl method. - Modify the Application class to include this new view as follows
//create a group node for the search content IModuleGroupNode searchGroupNode = new ModuleGroupNode(); searchApp.addChild(searchGroupNode); //create a module note for searching IModuleNode searchScreen = new ModuleNode("Search Screen"); //NON-NLS-1 searchGroupNode.addChild(searchScreen); createSubMobule("Search", searchScreen, SearchView.ID); //NON-NLS-1
First we create a group node to act as a container for our Search screen. Then we provide an IModuleNode. This will create a navigation item on the left hand side. We then add the SearchView as the submodule for this navigation node
I would be interested to find out if there's any way of avoiding having those nodes appear on the left hand side.
The createSubMobule method is provided along with the example, so I won't go into it here.
Here's the resulting screen. As you can see, putting together the pieces for a UI in Riena is pretty easy.
A Custom Look & Feel For Your Application
So far we have seen that a Riena UI can look dramatically different a standard RCP UI. The final UI aspect that I will go through here is how to change the Look & Feel of the application.
First, you will need to create a theme, by implementing the ILnfTheme from Riena. This allows you to add custom colors, fonts, images and settings. An easier way to do this is to extend the RienaDefaultTheme, as this will have all the necessary parts set up.
For this example we'll just change the background colour of submodules, and the coolbar
public class NewTheme extends RienaDefaultTheme { public void addCustomColors(Map<String, ILnfResource> table) { super.addCustomColors(table); table.put(LnfKeyConstants.SUB_MODULE_BACKGROUND, new ColorLnfResource(186, 193, 225)); table.put(LnfKeyConstants.COOLBAR_BACKGROUND, new ColorLnfResource(186, 193, 225)); } }
Next, create a class to extend RienaDefaultLnf that can instantiate this new theme
public NewLnf() { super(); setTheme(new NewTheme()); } protected String getLnfId() { return "newLnf"; }
In the constructor of the Application, just call the LnfManager to make the change to the look and feel of the entire application
public Application() { super(); LnfManager.setLnf(new NewLnf()); }
We now have a complete change in our entire UI. I really like this look and feel concept in Riena, which is not that far from the Swing concept.
Remote Services
Another big part of Riena is the way it handles remote services. In summary, Riena allows OSGi services to be accessible from outside of the current JVM by publishing the service as a web service endpoint. From the client side, remote access to the service is possible through a proxy registered as an OSGi service in the client VM. This proxy transports the request and response between client and server.
This example will provide a foreign exchange service to clients. The code for the "foreign exchange" service is very simple, using a POJO to do the work. Throughout this section of the article, we will be dealing with OSGi bundles, rather than standard Eclipse plugins.
First, we'll create a common OSGi bundle for both the client and server to reference. To create an OSGi bundle in Eclipse, you just create a standard plug-in project, but the target platform for the bundle should be Equinox, rather than an Eclipse version.
The main content here is the interface for our ForeignExchange class.
public interface IForeignExchange { double convertDollarToEuro(double dollarAmount); }
The only other detail we need to complete here is to open the bundle's manifest to add one dependency: org.eclipse.riena.communication.core.
Ensure that you export the package containing this interface so that it is visible to all other bundles.
Riena on the Server Side
In our server bundle, we create an implementation of this class. First, we need to organise the dependencies of this bundle to be as follows:
public class ForeignExchange implements IForeignExchange{ public double convertDollarToEuro(double dollarAmount) { double euro = dollarAmount * 0.7513; return euro; } }
Now let's take a look at the extra properties required to publish the OSGi service as a web service through Riena. To do this we need to go to the Activator of this bundle.
public class Activator implements BundleActivator { private ServiceRegistration foreignExchangeService; public void start(BundleContext context) throws Exception { Hashtable<String, String> properties = new Hashtable<String, String>(3); properties.put(RSDPublisherProperties.PROP_IS_REMOTE, Boolean.TRUE.toString()); properties.put(RSDPublisherProperties.PROP_REMOTE_PROTOCOL, "hessian"); //$NON-NLS-1$ properties.put(RSDPublisherProperties.PROP_REMOTE_PATH, "/currency"); //$NON-NLS-1$ foreignExchangeService = context.registerService(IForeignExchange.class.getName(), new ForeignExchange(), properties); } public void stop(BundleContext context) throws Exception { foreignExchangeService.unregister(); foreignExchangeService = null; } }
So, when the plugin is started, the path to this service will now be http://localhost/hessian/currency. If the plugin is stopped, the web service endpoint will also be unavailable. The following diagram, taken from the Riena Getting Started documentation explains how this works.
Riena on the Client Side
On the client side, we'll need to create a web service proxy, and register that as an OSGi service. This will allow the client to access the foreign exchange service on the server side. It's best practice to create a seperate bundle for this. In the Riena examples this is bundle ends with .client.config.
First, edit the manifest to include the necessary plugins.
You'll also need to manipulate the Activator for this plugin in order to get the remote service registered.
private IRemoteServiceRegistration serviceReg; /** * Creates a RemoteServiceReferences based on Hessian protocol and registers * this as "remote" OSGi Service */ public void start(BundleContext context) throws Exception { // register hessian proxy for riena remote service serviceReg = Register.remoteProxy(IForeignExchange.class).usingUrl("http://${riena.hostname}/hessian/currency") .withProtocol("hessian").andStart(context); } /** * unregister end release the "remote" OSGi Service */ public void stop(BundleContext context) throws Exception { if (serviceReg != null) { serviceReg.unregister(); serviceReg = null; } }
You'll need to export the package containing this activator in the Runtime/Exported Packages section of the manifest also.
A final point on what I have implementing on the the .config bundle, is the use of the ${riena.hostname} variable. I could have simply used localhost:8080, but in the plugin.xml of this bundle, I have included the following value variable, which provides an initial value to the ${riena.hostname} variable.
<plugin> <extension point="org.eclipse.core.variables.valueVariables"> <variable description="Name of the host to connect to" name="riena.hostname" readOnly="true" initialValue="localhost:8080"/> </extension> </plugin>
Now, we should be able to get at the service through this bundle, just the same as calling any local OSGi service. Our fourth bundle, the Client, should only need to be dependent on the common bundle.
The client Activator code goes something like this:
private BundleContext context; public void start(BundleContext context) throws Exception { this.context = context; ServiceReference serviceRef = context.getServiceReference(IForeignExchange.class.getName()); if (serviceRef != null) { IForeignExchange exchangeService = (IForeignExchange)context.getService(serviceRef); System.err.println("Result of exchange: " + exchangeService.convertDollarToEuro(2.0)); } else { context.addServiceListener(new ForeignExchangeClient(), "(objectClass=" + IForeignExchange.class.getName() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } } public void stop(BundleContext context) throws Exception { this.context = null; }
In this case we have added a Service listener, in case the service isn't ready when we first run. The code for the ForeignExchangeClient is quite simple:
class ForeignExchangeClient implements ServiceListener { public void serviceChanged(ServiceEvent event) { ServiceReference serviceReference = event.getServiceReference(); IForeignExchange exchangeService = (IForeignExchange) context.getService(serviceReference); if (exchangeService == null) { return; } System.err.println("Result of exchange: " + exchangeService.convertDollarToEuro(2.0)); } }
Time to test this out - to do this, we'll set up two different Run Configurations. As we are running OSGi bundles, rather than standard Eclipse plugins, your Configurations will appear under OSGi Framework rather than Eclipse Application in the Run Configurations dialog.
The server will run as follows:
A good way of making sure you have the bundles you need here is to just select the server bundle first, and then just choose "Add Required Bundles".
For the client, we'll do the same thing. Make sure you select the client.config bundle also.
Because we'll want to see the console output for this run, add in the -consoleLog parameter in the Arguments tab.
All the source used in this example is available for you to run. The following diagram shows the server bundle dependencies:
And the client bundle dependencies:
Summary
The following is a summary of the main steps behind providing remote services using Riena
- The server can be run anywhere. In the Activator of the service, you will need to provide a URL to the service. Most importantly you will need to register your service.
- The client.config class does the work of registering the remote service for access on the client side.
- The client will get the service reference as if it was a local OSGi bundle.
Riena gives Eclipse a new edge, with it's different approach to user interfaces, and the flexible implemenation of remote services. Over the next few weeks I'll be looking at Riena more closely. As you can see from the list at the start of the article, covering UI and Remote Services only scratches the surface of what Riena can provide for Eclipse developers.
Opinions expressed by DZone contributors are their own.
Comments