Resource Menu


by Julien Forest - (no comment)
This is a short example that demonstrates how to use Keridwen Core, and in particular its messaging module.

Other examples will be added later.

The source code of this example can be found in Keridwen Core bundles called Sample Hello World and Sample Hello World Caller.

1. Prerequisites

To understand this example, it is required to have at least basic knowledge of the following technologies:

  • Java programming
  • OSGi
  • iPOJO
  • Felix framework
  • Maven
  • Design patterns such as the Command pattern

2. Hello World

2.1 Introduction

The first bundle is called org-keridwen-sample-helloworld. Its purpose is to print the name of the root node of the data model, which is called "HelloWorld".

It is structured as a standard Maven project, with a Project Object Model file (pom.xml) located at the root of the project and that allows you to easily compile the bundle or import it in your favorite IDE (Eclipse, Netbeans, IntellijIdea, etc.).

Java source files are contained in the src/main/java directory.

As in most Keridwen bundles, normalized packages have been created:

  • org.keridwen.sample.helloworld.command: contains the commands of the bundle
  • org.keridwen.sample.helloworld.i18n: should contain the internationalisation dictionary (this package is empty in this example)
  • org.keridwen.sample.helloworld.key: contains the event keys and their association with the commands
  • org.keridwen.sample.helloworld.model: should contain the data model of the bundle (this package is empty in this example)
  • org.keridwen.sample.helloworld.osgi: contains the classes used by the OSGi framework to start and stop the bundle
  • org.keridwen.sample.helloworld.view: contains the graphical user interfaces of the bundle (this package is empty in this example)
The are mainly 3 files that we are going to review here:
  • org/keridwen/sample/helloworld/command/PrintRootNameCommand.java
  • org/keridwen/sample/helloworld/key/HelloWorldEventKeys.java
  • org/keridwen/sample/helloworld/osgi/BundleStarter.java

2.2 Command

In Keridwen Messaging, we have implemented the Command design pattern that aims at encapsulating an action. Bundles can directly reuse it to implement their own functions, as illustrated below.

The most important method here is the run() method that will be called when the command is executed. Here, it retrieves the application hierarchical data model and prints the name of its root node.

/**
 * Project        : Keridwen
 * Web site       : http://www.keridwen.org
 * Copyright      : (c) Artenum SARL, 24 rue Louis Blanc
 *                  75010, Paris, France 2000-2012
 *                  http://www.artenum.com
 * Email          : contact (at) artenum.com
 * License        : cf. LICENSE.txt
 * Developed By   : Artenum SARL
 * Authors        : Benoit Thiebault
 *                  Jeremie Turbet
 *                  Benjamin Jeanty-Ruard
 *                  Pierre Souquet
 *                  Julien Forest
 * Contract       : Internal development
 * Creation Date  : 2011-09-15
 */
package org.keridwen.sample.helloworld.command;

import org.keridwen.core.data.model.DataModel; import org.keridwen.core.data.model.Node; import org.keridwen.core.messaging.Command; import org.keridwen.core.messaging.CommandException; import org.slf4j.Logger; import org.slf4j.LoggerFactory;

/** * This {@link Command} simply prints the name of the root {@link Node} of Keridwen {@link DataModel}. It demonstrates * the basic usage of Keridwen {@link Command}s and how they can access the central application {@link DataModel}. * * @author Benoit Thiebault */ public class PrintRootNameCommand extends AbstractHelloWorldCommand<Boolean, String> {

/** Logger to print information in the log console. */ private static final Logger LOGGER = LoggerFactory.getLogger(PrintRootNameCommand.class.getName());

/** * Default constructor. */ public PrintRootNameCommand() { super("Print root name command", "Prints the name of the root node of the application model", false, false, false, false); }

@Override public final void undo() { throw new UnsupportedOperationException("This command prints a String, it cannot be undone"); }

@Override public final boolean validatePostConditions() { // There are no post conditions, so the result is always true and the Command is in DONE state. // When the run method fails and throws an Exception, the Command is in ERROR state. // When it does not crash but the post conditions are not met (false is returned), the // Command is in WARNING state. return true; }

@Override public final Boolean run(final String message) throws CommandException { // We access the application DataModel (shared by all other Bundles of the sample application), // Retrieve the root Node and prints its name with the Logger. LOGGER.info(getApplicationModel().getRootNode().getName()); return true; }

@Override public void pause() { throw new UnsupportedOperationException("Impossible to pause this command."); }

@Override public void resume() { throw new UnsupportedOperationException("Impossible to resume this command."); } }

2.3 Key

In order to keep command callers loosely coupled with the command itself, an event key mechanism has been introduced in Keridwen messaging. The caller (a button in the user interface for instance) triggers an event with a unique event key that asks the controller to execute the appropriate command. The command can then access the central data model to modify it. The user interface asks for a specific cation to be performed (the what to do, i.e. the event), but does not know how the action will be performed (by which command). This allows to easily replace the business layer of your application without impacting the user interface (or reciprocally, having several user interfaces linked to the same business layer).

This is a direct implementation of the Model View Controller design pattern that favors future evolutions of the software.

The HelloWorldEventKey class purpose is to declare all the event keys and to associate them with the appropriate command. This is the place where you can change what command is triggered by a given event key, without modifying the caller.

/**
 * Project        : Keridwen
 * Web site       : http://www.keridwen.org
 * Copyright      : (c) Artenum SARL, 24 rue Louis Blanc
 *                  75010, Paris, France 2000-2012
 *                  http://www.artenum.com
 * Email          : contact (at) artenum.com
 * License        : cf. LICENSE.txt
 * Developed By   : Artenum SARL
 * Authors        : Benoit Thiebault
 *                  Jeremie Turbet
 *                  Benjamin Jeanty-Ruard
 *                  Pierre Souquet
 *                  Julien Forest
 * Contract       : Internal development
 * Creation Date  : 2011-09-15
 */
package org.keridwen.sample.helloworld.key;

import org.keridwen.core.data.model.DataModel; import org.keridwen.core.data.model.Node; import org.keridwen.core.messaging.BundleController; import org.keridwen.core.messaging.Command; import org.keridwen.core.messaging.DefaultEventKey; import org.keridwen.core.messaging.EventKey; import org.keridwen.sample.helloworld.command.PrintRootNameCommand;

/** * This class stores all the keys and the Keridwen {@link Command}s they are associated with. In order to keep * {@link Command} caller loosely coupled with the {@link Command}s themselves, the caller only uses the * {@link EventKey}s to call the {@link Command}. They know the signatures of the {@link Command} that is identical to * the one of the {@link EventKey}, but don't know what actual implementation is used. * * This class associates the {@link EventKey}s with their respective {@link Command} and this is the place where you can * change what {@link Command} is triggered by a given {@link EventKey}, without modifying the caller. */ public class HelloWorldEventKeys {

/** * Request to print the name of the root {@link Node} of the {@link DataModel} (which is called HelloWorld in this * example). The return type and message type generics are mandatory, but are not used here. */ public static final DefaultEventKey<Boolean, String> ORG_KERIDWEN_SAMPLE_HELLOWORLD_PRINT = new DefaultEventKey<Boolean, String>( PrintRootNameCommand.class);

/** * This method registers all the {@link EventKey}s in the given {@link BundleController}. * * @param controller * the {@link BundleController} that will store the {@link EventKey}s */ public static void registerAllKeysToController(final BundleController controller) { controller.registerEvent(ORG_KERIDWEN_SAMPLE_HELLOWORLD_PRINT); } }

2.4 Bundle starter

The BundleStarter is the class that contains the methods that are called when the Bundle is started or stopped by the OSGi framework.

In this example, when the bundle is started, the start() method below is called. It triggers the event key that prints "HelloWorld".

/**
 * Project        : Keridwen
 * Web site       : http://www.keridwen.org
 * Copyright      : (c) Artenum SARL, 24 rue Louis Blanc
 *                  75010, Paris, France 2000-2012
 *                  http://www.artenum.com
 * Email          : contact (at) artenum.com
 * License        : cf. LICENSE.txt
 * Developed By   : Artenum SARL
 * Authors        : Benoit Thiebault
 *                  Jeremie Turbet
 *                  Benjamin Jeanty-Ruard
 *                  Pierre Souquet
 *                  Julien Forest
 * Contract       : Internal development
 * Creation Date  : 2011-09-15
 */
package org.keridwen.sample.helloworld.osgi;

import org.keridwen.core.data.model.DataModel; import org.keridwen.core.data.model.Node; import org.keridwen.core.data.model.impl.DefaultDataModel; import org.keridwen.core.data.model.impl.DefaultNode; import org.keridwen.core.messaging.BundleController; import org.keridwen.core.messaging.DefaultBundleController; import org.keridwen.core.messaging.EventBuilder; import org.keridwen.core.messaging.EventDispatcher; import org.keridwen.core.messaging.EventKey; import org.keridwen.sample.helloworld.key.HelloWorldEventKeys;

/** * The BundleStarter is the class that contains the methods that are called when the Bundle is started or stopped by the * OSGi framework. This behaviour is configured in the src/main/resources/metadata.xml file. * * @author Benoit Thiebault */ public class BundleStarter { /** Event dispatcher of the application that is injected dynamically by iPOJO. */ private final EventDispatcher dispatcher = null; /** Bundle controller that stores the {@link EventKey}s. */ private final BundleController controller;

/** * Default constructor. */ public BundleStarter() { // This is the root node of the data model used in the whole application. // Generally, the application data model is created and managed in a specific bundle, but in this example, for // the sake of simplicity, it is created here. // This root node is named HelloWorld and will not store any particular data. final Node<Boolean> rootNode = new DefaultNode<Boolean>("HelloWorld");

// The data model is a tree of Nodes containing the data to manipulate and save. To create the data model, we // provide him with a root Node final DataModel applicationModel = new DefaultDataModel(rootNode);

// The bundle controller is initiated with the application model and the bundle data model (null here as we // don't need to store anything locally). this.controller = new DefaultBundleController(applicationModel, null);

// The EventKeys are then registered in the bundle controller HelloWorldEventKeys.registerAllKeysToController(this.controller); }

/** * This method is called whenever the OSGi framework starts the bundle. */ public void start() { // The bundle controller is registered in the dispatcher this.dispatcher.registerBundle(this.controller);

// An event is triggered to print the name of the root node of the application data model. EventBuilder.event(HelloWorldEventKeys.ORG_KERIDWEN_SAMPLE_HELLOWORLD_PRINT, null).triggerCallEvent(); }

/** * This method is called whenever the OSGi framework stops the bundle. */ public void stop() { // The controller is unregistered from the dispatcher this.dispatcher.unregisterBundle(this.controller); } }

3. Hello World Caller

The org-keridwen-sample-helloworld.caller bundle is much more simpler than the previous one. It's purpose is to demonstrate how to trigger the event create in the org-keridwen-sample-helloworld bundle to print HelloWorld from an external bundle, thus achieving inter-bundle communications.

/**
 * Project        : Keridwen
 * Web site       : http://www.keridwen.org
 * Copyright      : (c) Artenum SARL, 24 rue Louis Blanc
 *                  75010, Paris, France 2000-2012
 *                  http://www.artenum.com
 * Email          : contact (at) artenum.com
 * License        : cf. LICENSE.txt
 * Developed By   : Artenum SARL
 * Authors        : Benoit Thiebault
 *                  Jeremie Turbet
 *                  Benjamin Jeanty-Ruard
 *                  Pierre Souquet
 *                  Julien Forest
 * Contract       : Internal development
 * Creation Date  : 2011-09-15
 */
package org.keridwen.sample.helloworld.caller.osgi;

import org.keridwen.core.messaging.EventBuilder; import org.keridwen.core.messaging.EventDispatcher; import org.keridwen.core.messaging.EventKey; import org.keridwen.sample.helloworld.key.HelloWorldEventKeys;

/** * The BundleStarter is the class that contains the methods that are called when the Bundle is started or stopped by the * OSGi framework. This behaviour is configured in the src/main/resources/metadata.xml file. * * This bundle only triggers the {@link EventKey} * * @author Benoit Thiebault */ public class BundleStarter { /** Event dispatcher of the application that is injected dynamically by iPOJO. */ private final EventDispatcher dispatcher = null;

public BundleStarter() { // This is just a trick to prevent a compilation warning this.dispatcher.toString(); }

public void start() { // An event is triggered to print the name of the root node of the application data model. EventBuilder.event(HelloWorldEventKeys.ORG_KERIDWEN_SAMPLE_HELLOWORLD_PRINT, null).triggerCallEvent(); }

public void stop() { } }

4. Compilation

These samples are provided with the standard Keridwen Core distribution. The compilation process is done using Apache Maven.

At the Keridwen Core root directory, just type the following command:

mvn -DskiptTests=true clean install

The following sample bundles should be successfully compiled:

  • org-keridwen-sample-helloworld
  • org-keridwen-sample-helloworld-caller

5. Deployment on the runtime

To be run the aboves examples should be deployed on an OSGI complient runtime, like Felix.

The procedure is the following one:

a) Download and install locally the Felix runtime, for instance.

b) Copy the generated Keridwen bundles into the $FELIX_ROOT/bundles directory:

  • org-keridwen-core-data-model-2.0.2-SNAPSHOT.jar
  • org-keridwen-core-messaging-2.0.2-SNAPSHOT.jar
  • org-keridwen-sample-helloworld-2.0.2-SNAPSHOT.jar
  • org-keridwen-sample-helloworld-caller-2.0.2-SNAPSHOT.jar
c) Copy the following dependencies into the $FELIX_ROOT/bundles directory:
  • logback-classic-1.0.7.jar
  • logback-core-1.0.7.jar
  • org.apache.felix.bundlerepository-1.6.6.jar
  • org.apache.felix.gogo.command-0.12.0.jar
  • org.apache.felix.gogo.runtime-0.10.0.jar
  • org.apache.felix.gogo.shell-0.10.0.jar
  • org.apache.felix.ipojo-1.8.0.jar
  • osgi-over-slf4j-1.7.2.jar
  • slf4j-api-1.7.2.jar
  • xom-1.2.8.jar
These dependencies are available in the Keridwen dependencies pack.

d) Launch the runtime, with the following command:

java -jar bin/felix.jar

The sample should return the following message:

g! 15:59:54.614 [pool-3-thread-1] DEBUG o.k.c.m.DefaultBundleController - Print root name command - My_Root_Node
15:59:54.811 [pool-4-thread-1] INFO  o.k.s.h.command.PrintRootNameCommand - HelloWorld
15:59:55.780 [pool-3-thread-1] DEBUG o.k.c.m.DefaultBundleController - Print root name command - My_Root_Node
15:59:55.836 [pool-4-thread-1] INFO  o.k.s.h.command.PrintRootNameCommand - HelloWorld

Enjoy!

6. Validated platforms

This example has been successfully run on the following platforms:

  • Felix runtime:
    • I586 - Linux - 64/32 bits
    • I586 - Apple Mac OSX 64 bits 10.4 and higher
    • I586 - Microsoft Windows Seven 64 bits
    • ARM 32 bits,


Last edited by null at - Edit content - View history - View source