Статьи

Риена — новое приключение в затмении

Проект Riena недавно выпустил версию 1.0. В этой статье я покажу вам, как начать работать с Riena, рассказав о моем собственном опыте использования проекта. Помимо краткого описания проекта, я покажу, как создать пользовательский интерфейс в Riena, и на примере удаленных служб. 

Некоторый Фон

Во-первых, что такое Риена? В  августе прошлого года я написал статью о Riena, в которой рассказывалось о том, как она добавляет новое измерение к приложениям Eclipse RCP, предоставляя более «дружественный» пользовательский интерфейс для неинженеров. Но основная концепция Riena заключается в создании основы для создания многоуровневого корпоративного клиент-серверного приложения. Riena упрощает использование служб OSGi (Equinox), обеспечивая прозрачный доступ к локальным и удаленным службам. Таким образом, независимо от того, где находится ваш OSGi-пакет, будь то на клиенте или на сервере, вы можете получить к нему одинаковый доступ через Riena.

Второй выпуск Riena уже находится в стадии планирования и будет включать расширенные функции и компоненты для разработки пользовательских интерфейсов и навигации.

Полный перечень понятий и функций в Riena довольно полно. Они включают: 

  • Инъекционные услуги и расширения
  • Удаленные Услуги
  • Безопасность
  • ObjectTransaction
  • Этапы
  • Мониторинг клиентов
  • навигация
  • Смотреть и чувствовать
  • UI Filters
  • Поддержка входа
  • Пользовательские Риджеты

 

Начиная 

Теперь, когда мы немного узнали о проекте, пришло время взять его на тест-драйв. Для этого примера я использую Eclipse 3.5M6. Для начала есть два подхода, которые вы можете использовать. Одним из них является загрузка полной целевой платформы Riena, включая Equinox и SDK, с http://www.eclipse.org/downloads/download.php?file=/rt/riena/Riena-1.1.0.M5-platform-win32.win32. .x86.zip .

Я сделал это, чтобы облегчить мою текущую установку Eclipse. (Кроме того: я делаю это довольно часто, и вокруг моей машины много инсталляций Eclipse). Если вы просто хотите добавить Riena в ваше текущее приложение, вы можете просто добавить материал, специфичный для Riena, с http://www.eclipse.org/downloads/download.php?file=/rt/riena/Riena-1.0.0. застежка — молния

Для мастера пользовательского интерфейса доступен сайт обновления. На вики-странице указан неправильный URL-адрес сайта обновления — вам нужно указать http://download.eclipse.org/rt/riena/updatesites/org.eclipse. riena.ui.templates.updatesite /

Я буду использовать это в этом уроке, так что стоит включить это.

Полные и официальные инструкции по началу работы доступны на вики-странице Riena .

Настройка целевой платформы

Сначала нам нужно указать целевую платформу для нашего приложения, в которой «Riena» «установлена». Этот шаг не применяется, если вы загрузили компоненты Riena для текущей установки.

Здесь я использовал C: \ Riena в качестве места установки: 

 

Нажав кнопку «Готово», вы получите целевое определение. Теперь вы можете дать цели более дружественное имя

 

Все, что вам нужно сделать сейчас, это установить эту цель как активную для вашей текущей разработки плагинов:

 

Создание пользовательского интерфейса с помощью Riena

Теперь, когда мы установили Riena UI Wizard, мы можем быстро увидеть пример кода. Вот шаг за шагом, чтобы добраться до одного из шаблонов проекта Riena: 

  • Создайте новый проект плагина, но убедитесь, что он основан на Eclipse 3.4
  • Установите флажок «Этот плагин будет вносить вклад в пользовательский интерфейс» и выберите « Да», чтобы создать многофункциональное клиентское приложение.
  • Вы получите четыре различных варианта Riena в списке шаблонов. Здесь мы выберем почтовый шаблон Riena

После того, как вы нажмете кнопку «Готово», вам будет представлен ваш почтовый проект Riena. Давайте кратко рассмотрим, как выглядит приложение, прежде чем мы начнем исследовать код.

Чтобы запустить приложение, просто откройте файл plugin.xml и выберите « Запустить приложение Eclipse».

Ниже приведен экран, на котором вам будет представлен — я уверен, вы согласитесь, что он довольно приятный и отличается от любого приложения RCP, которое вы, вероятно, разрабатывали до сих пор. 

Другой пользовательский интерфейс

Ниже приведены ключевые моменты в определении плагина, которые отличают пользовательские интерфейсы Riena от стандартных пользовательских интерфейсов RCP.

  •  В манифесте плагина вы увидите, что помимо org.eclipse.ui, есть также зависимость от org.eclipse.riena.client
    Require-Bundle: org.eclipse.ui,
    org.eclipse.core.runtime,
    org.eclipse.riena.client

  • Основной класс приложения Riena создается как расширение org.eclipse.core.runtime.applications в plugin.xml. 
    <extension
    id="application"
    point="org.eclipse.core.runtime.applications">
    <application>
    <run
    class="com.dzone.riena.tests.Application">
    </run>
    </application>
    </extension>

    Мы рассмотрим этот класс более подробно в ближайшее время.

  • Экземпляр org.eclipse.riena.navigation.ui.swt.views.SubApplicationView создается в качестве перспективы. Это будет вызвано позже из основного класса Application.

 

Все остальное как в любом стандартном приложении RCP. Представления, меню и обработчики написаны как обычно. Итак, давайте подробнее рассмотрим класс Application

Класс расширяет org.eclipse.riena.navigation.ui.swt.application.SwtApplication. Ключевой частью этого класса является переопределенный метод createModel

 

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("[email protected]"); //NON-NLS-1
groupMailboxes.addChild(moduleAccount1);
moduleAccount1.setClosable(false);
.......
return app;
}

ApplicationNode представлен оболочкой всего приложения, в то время как закладка тока , который является открытым является ISubApplicationNode . Лучший способ проиллюстрировать это — добавить новый узел, который мы назовем «Поиск».

	ISubApplicationNode searchApp  = new SubApplicationNode("Search"); //NON-NLS-1
app.addChild(searchApp);

Поскольку я не связал с этим приложением какую-либо особенность, все, что сейчас нужно сделать, это добавить вкладку.

Добавление контента на вкладку поиска

Чтобы добавить простой контент пользовательского интерфейса на вкладку поиска, мы можем использовать следующий ярлык:

  • В plugin.xml скопируйте перспективу Riena и создайте свою собственную перспективу. Просто измените имя и идентификатор этой новой перспективы. Обратите внимание, мы все еще используем тот же класс.
      <perspective
    class="org.eclipse.riena.navigation.ui.swt.views.SubApplicationView"
    id="rcp.search.perspective"
    name="Search Perspective">
    </perspective>

    Теперь мы нажимаем на нашу вкладку Поиск, мы увидим пустой вид. Как и следовало ожидать, элементы меню и панели инструментов остаются неизменными при нажатии на вкладку Поиск.

  • Далее нам нужно предоставить ViewPart для вкладки Search. Опять же, давайте быстро выберемся из этого и скопируем существующее представление, изменив некоторые параметры.
      <view
    allowMultiple="true"
    class="com.dzone.riena.tests.SearchView"
    icon="icons/sample2.gif"
    id="rcp.search.view"
    name="Search">
    </view>

    Сам класс представления расширяет ViewPart как в любом стандартном приложении RCP. Все, что мы сделаем в этом классе, определим ID как константу и создадим некоторый контент для представления в методе createPartControl .

  • Измените класс Application для включения этого нового представления следующим образом
    //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

    Сначала мы создаем групповой узел, который будет действовать как контейнер для нашего экрана поиска. Затем мы предоставляем IModuleNode. Это создаст элемент навигации с левой стороны. Затем мы добавляем SearchView в качестве подмодуля для этого узла навигации.

    Мне было бы интересно узнать, есть ли способ избежать появления этих узлов с левой стороны.

    Метод createSubMobule предоставляется вместе с примером, поэтому я не буду вдаваться в подробности.

Вот результирующий экран. Как видите, собрать части для пользовательского интерфейса в Риене довольно легко.

Индивидуальный внешний вид для вашего приложения

До сих пор мы видели, что Riena UI может значительно отличаться от стандартного RCP UI. Последний аспект пользовательского интерфейса, который я расскажу здесь, — это как изменить внешний вид приложения. 

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_RemoteService_on_Server.png

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 here 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.