Hello World With River

Experimental
Warning! The following describes how to use code which is currently only contained in a branch of River and has not been formally released yet.

Introduction

It can be argued that part of the barrier to entry of getting working River services is the complexity and difficulty with configuring services. The are many things that users, new and old, must remember when trying to set up new environments and tweak the configurations of their services.

The configurations themselves can be temperamental and difficult for new comers to get to grips with. Largely, they are text files with a Java-esque content which is parsed by the ServiceStarter. The other problem with the config files is ensuring that you can share configuration for certain services across the djinn. Encapsulating configuration in Java code, which can be more easily humanly read, verified and shared is possibly a better idea than having many slightly different copies of configuration files which must then be kept in step. The following describes how to use the "extra" bits of River to get around these issues.

Extra River

The classes required in this documentation live outside of the main River distribution, in fact they are not part of the core of the library neither are they part of the Jini and JavaSpaces specifications. In a standard River install, they are therefore completely optional.

Getting Started

Prerequisites

River Extras require the following additional JARs on their runtime classpath.

From now on, this document will refer to these two exact file system location of these JARs as;

From Binary Distribution

Download the latest version of River and extract it to some directory. From now on, this will be known as ${RIVER_HOME}

See the optional part of "From Source" if you also want to build and experiment with the example service.

From Source

You must first get the River source. Since this code is still experimental, that means checking out the code from a trunk. Ideally, this code will make the next release and so will come packaged as part of the River (source) distribution.


$  cd ~/projects/river
$  svn co http://svn.apache.org/repos/asf/river/jtsk/skunk/easystart helloworld
$  cd helloworld
$  ant # This will build the River distribution, including the lib/extra.jar

Additionally, if you want to be able to run the example code which uses this configuration technique, you must also build the JAR which contains it. This is done by executing the following Ant command. However, if you are only interested in using the extra.jar then there is no need to complete this step.


$  cd ../src-extra-examples
$  ant # This will build the lib/extra-examples.jar

From this point on, we shall refer to the installation of River as ${RIVER_HOME}. In this example, it would refer to directory ~/projects/river/helloworld.

Starting the HTTP Server

Jini/River services of course require a HTTP server. This is easily started in such a way as it will serve all the JAR files from the ${RIVER_HOME}/lib directory.

Open your IDE of choice, fix any build/setup errors and then setup a run configuration for the HTTP server - I use Eclipse, hence the terminology.

The main class to run is, org.apache.river.extra.examples.easystart.riverservices.StartHttpServer and can be found in the ${RIVER_HOME}/src-extra-examples source directory. It will need the following program arguments;

Obviously, the first argument is the value of ${RIVER_HOME} and the second is the HTTP port to use. This is the default port according to River Extras, if you want to use a different port then this will require changes (in Java code) of the configuration.

Execute this run configuration and leave it running. You can verify that it's working correctly by using your browser or wget to download a sample JAR, e.g.

$ wget http://localhost:8080/reggie.jar

The rest of this document describes code that can be found in the src-extra and src-extra-examples source directories.

Running Services

When running River services you will also require the following VM arguments.


-Djava.security.policy=${RIVER_HOME}/src-extra/policy.all 
-Djava.rmi.server.RMIClassLoaderSpi=net.jini.loader.pref.PreferredClassProvider
-DRIVER_HOME=${RIVER_HOME}

Important: The policy file specified above is a "grant all" policy file. You could consider it a "disable security" security policy. As such it is unsuitable for real-world use.

Common Service Configuration Options

We are going to use a ApplicationOptions to describe certain service configuration options that will be common to all the services we're going to start. From, org.apache.river.extra.examples.easystart.StartServices;

ApplicationOptions options = new ApplicationOptions();
options.setJiniPort(4162);
options.setHttpOptions("localhost", 8080, true);
options.addLookupGroup("extra").addLookupGroup("example");
options.setPackageName(ExampleService.PACKAGE);

So you can see we are using the non-default Jini port (4162). We are also setting the configuration package name of our services to some constant as defined in ExampleService.PACKAGE.

We shall describe ExampleService later.

Next we need a factory to build our configuration objects;

ApplicationConfigurationFactory configFac = new ApplicationConfigurationFactory(options);

This class extends a River Extra class, org.apache.river.extra.easystart.config.ConfigurationFactory. This base class knows how to create configuration objects for the standard River services using the djinn specific options as we have described above, the extending class also knows how to create configuration objects for our additional custom services.

Starting the Lookup Service

Staying with the same StartServices class from above.

First we need to configure the lookup service and then we can start it. This is easily done;


LookupServiceConfiguration lusConfig = configFac.lookupServiceConfig();
lusConfig.addMemberGroup("extra").addMemberGroup("example");
ServiceStarter.main(lusConfig.riverConfig());

Notice that we have set the lookup service's configuration's member groups to be the same as the lookup groups as defined in the ApplicationOptions instance above.

We now have a started lookup service; (but check your console for errors!)

Starting the Example Service

Next we need a configuration for our example service.

The configuration is described in our extended ApplicationConfigurationFactory, thus;


public AbstractEasyConfiguration exampleService(Name name) {
    ApplicationOptions exampleOptions = getDefaultOptions();
    exampleOptions.setImplementationClass(ExampleServiceImpl.class);
    exampleOptions.addInitialLookupAttribute(name);
    return new AbstractEasyConfiguration(exampleOptions) {};
}

Now we know how the configuration was done. You should also be able to see how easy it will be to extend this concept of ConfigurationFactory to handle all of your own custom services that make up your own applications.

We can start the example service in exactly the same way as we did the lookup service previously.

AbstractEasyConfiguration config = configFac.exampleService(new Name("Jeff")); ServiceStarter.main(config.riverConfig());

Now we have successfully configured and started our example service, and in all of this we did not have to bother ourselves with any configuration file.

It should be pointed out that the services we have started are using the default policy file which is unsuitable for a live environment, since it is a "grant all" policy file that can be found in ${RIVER_HOME}/src-extra/policy.all

So by defining and sharing our own implementation of the configuration factory we can be sure that all services started with configurations from that factory will be configured accordingly. What's more, we no longer have to maintain series of configuration files which potential contain many duplicate data items.

Additional Configuration

You can specify additional configuration data by creating custom org.apache.river.extra.easystart.config.settings.Setting objects and adding them to the org.apache.river.extra.easystart.config.ApplicationOptions instance before creating your configuration factory. In the same package, there are additional extending classes of Setting that can be used as templates for your own configuration options.