2009/04/15 - Apache HiveMind has been retired.

For more information, please explore the Attic.

Apache > HiveMind
Apache
 
Font size:      

Bootstrapping the Registry

Before you can access the configuration points and services defined in your application's module deployment descriptors, you need a registry; here we'll describe how to construct the registry.

The key class here is RegistryBuilder, which contains code for locating and parsing the module deployment descriptors and constructing a registry from the combined data. The descriptors are all found on the class path; they'll include the descriptors for HiveMind itself with descriptors packaged into your application's JARs.

Note
As HiveMind grows in popularity, we may start to see third party frameworks come bundled with HiveMind module deployment descriptors ... but it's too soon for that, now.

Let's examine how all this comes together. The layout of the project is shown below.

[Project Layout]

Service Interfaces and Implementations

The first step is to define the service interface:

package hivemind.examples;

public interface Adder
{
    public int add(int arg0, int arg1);
} 

Next we need an implementation for that service:

package hivemind.examples.impl;

import hivemind.examples.Adder;

public class AdderImpl implements Adder
{
  public int add(int arg0, int arg1)
  {
    return arg0 + arg1;
  }

} 

The example includes three additional interfaces and matching implementations: for a Subtracter, Multiplier, Divider, and lastly, a Calculator that combines them:

package hivemind.examples;

public interface Calculator extends Adder, Subtracter, Multiplier, Divider
{

}    

The Calculator implementation will require some wiring; it expects that each of the other four services (Adder, Subtracter, Multiplier and Divider) will be plugged into it:

package hivemind.examples.impl;

import hivemind.examples.Adder;
import hivemind.examples.Calculator;
import hivemind.examples.Divider;
import hivemind.examples.Multiplier;
import hivemind.examples.Subtracter;

public class CalculatorImpl implements Calculator
{
  private Adder _adder;
  private Subtracter _subtracter;
  private Multiplier _multiplier;
  private Divider _divider;

  public void setAdder(Adder adder)
  {
    _adder = adder;
  }

  public void setDivider(Divider divider)
  {
    _divider = divider;
  }

  public void setMultiplier(Multiplier multiplier)
  {
    _multiplier = multiplier;
  }

  public void setSubtracter(Subtracter subtracter)
  {
    _subtracter = subtracter;
  }

  public int add(int arg0, int arg1)
  {
    return _adder.add(arg0, arg1);
  }

  public int subtract(int arg0, int arg1)
  {
    return _subtracter.subtract(arg0, arg1);
  }

  public int multiply(int arg0, int arg1)
  {
    return _multiplier.multiply(arg0, arg1);
  }

  public int divide(int arg0, int arg1)
  {
    return _divider.divide(arg0, arg1);
  }
}

Module Deployment Descriptor

Finally, we need the HiveMind module deployment descriptor, hivemodule.xml.

The module descriptor creates each of the services in terms of an interface, and an implementation. In addition, each service gets its own logging interceptor.

<?xml version="1.0"?>
<module id="hivemind.examples" version="1.0.0" package="hivemind.examples">
  <service-point id="Adder" interface="Adder">
    <create-instance class="impl.AdderImpl"/>
    <interceptor service-id="hivemind.LoggingInterceptor"/>
  </service-point>
  
  <service-point id="Subtracter" interface="Subtracter">
    <create-instance class="impl.SubtracterImpl"/>
    <interceptor service-id="hivemind.LoggingInterceptor"/>
  </service-point>
  
  <service-point id="Multiplier" interface="Multiplier">
    <create-instance class="impl.MultiplierImpl"/>
    <interceptor service-id="hivemind.LoggingInterceptor"/>
  </service-point>
  
  <service-point id="Divider" interface="Divider">
    <create-instance class="impl.DividerImpl"/>
    <interceptor service-id="hivemind.LoggingInterceptor"/>
  </service-point>
  
  <service-point id="Calculator" interface="Calculator">
    <invoke-factory>
      <!-- Autowires service properties based on interface! -->
      <construct class="impl.CalculatorImpl"/>
    </invoke-factory>
    <interceptor service-id="hivemind.LoggingInterceptor"/>
  </service-point>
  
</module>

Here we've chosen to have the module id, hivemind.examples, match the package name but that is not an absolute requirement.

The interesting part is the use of the hivemind.BuilderFactory (through <invoke-factory>) to construct the Calculator service and connect it to the other four services.

Building the Registry

Before your code can access any services (or configuration points), it must construct the Registry. The Registry is the application's gateway to the services and configurations managed by HiveMind.

package hivemind.examples;

import org.apache.hivemind.Registry;
import org.apache.hivemind.impl.RegistryBuilder;

public class Main
{
  public static void main(String[] args)
  {
    int arg0 = Integer.parseInt(args[0]);
    int arg1 = Integer.parseInt(args[1]);

    Registry registry = RegistryBuilder.constructDefaultRegistry();

    Calculator c =
      (Calculator) registry.getService(Calculator.class);

    System.out.println("Inputs " + arg0 + " and " + arg1);

    System.out.println("Add   : " + c.add(arg0, arg1));
    System.out.println("Subtract: " + c.subtract(arg0, arg1));
    System.out.println("Multiply: " + c.multiply(arg0, arg1));
    System.out.println("Divide  : " + c.divide(arg0, arg1));
  }
}
 

RegistryBuilder contains a static method for constructing a Registry, which is suitable for most situations.

Now that we have the registry, we can use the Calculator interface as a key for finding the Calculator implementation. In real applications, where there will often be multiple services implementing the same interface, we would have to specify a fully qualified service id as well.

Using the reference to the Calculator service, we can finally invoke the add(), subtract(), multiply() and divide() methods.

Building the Example

Building and running the example using Ant is a snap; all the details are in the build.xml:

<?xml version="1.0"?>

<project name="HiveMind Adder Example" default="jar">

  <property name="java.src.dir" value="src/java"/>
  <property name="test.src.dir" value="src/test"/>
  <property name="conf.dir" value="src/conf"/>
  <property name="descriptor.dir" value="src/descriptor"/>
  <property name="target.dir" value="target"/>
  <property name="classes.dir" value="${target.dir}/classes"/>
  <property name="test.classes.dir" value="${target.dir}/test-classes"/>
  <property name="example.jar" value="${target.dir}/hivemind-examples.jar"/>
  <property name="lib.dir" value="lib"/>
  <property name="junit.temp.dir" value="${target.dir}/junit-temp"/>
  <property name="junit.reports.dir" value="${target.dir}/junit-reports"/>

  <path id="build.class.path">
    <fileset dir="${lib.dir}">
      <include name="*.jar"/>
    </fileset>
  </path>
      
  <path id="test.build.class.path">
    <path refid="build.class.path"/>
    <path location="${classes.dir}"/>
  </path>
        
  <path id="run.class.path">
    <path refid="build.class.path"/>
    <pathelement location="${classes.dir}"/>
    <pathelement location="${descriptor.dir}"/>
    <pathelement location="${conf.dir}"/>
  </path>
  
  <path id="test.run.class.path">
    <path refid="run.class.path"/>
    <path location="${test.classes.dir}"/>
  </path>  
  
  <target name="clean" description="Delete all derived files.">
    <delete dir="${target.dir}" quiet="true"/>
  </target>
  
  <target name="compile" description="Compile all Java code.">  
    <mkdir dir="${classes.dir}"/>    
    <javac srcdir="${java.src.dir}" destdir="${classes.dir}" classpathref="build.class.path"/>
  </target>
  
  <target name="compile-tests" description="Compile test classes." depends="compile">
    <mkdir dir="${test.classes.dir}"/>
    <javac srcdir="${test.src.dir}" destdir="${test.classes.dir}" classpathref="test.build.class.path"/>
  </target>
  
  <target name="run-tests" description="Run unit tests." depends="compile-tests">
  
    <mkdir dir="${junit.temp.dir}"/>
    <mkdir dir="${junit.reports.dir}"/>

    <junit haltonfailure="off" failureproperty="junit-failure" tempdir="${junit.temp.dir}">    
      <classpath refid="test.run.class.path"/>
    
      <formatter type="xml"/>
      <formatter type="plain"/>
      <formatter type="brief" usefile="false"/>
    
      <batchtest todir="${junit.reports.dir}">
        <fileset dir="${test.classes.dir}">
          <include name="**/Test*.class"/>
        </fileset>
      </batchtest>
    </junit>

    <fail if="junit-failure" message="Some tests failed."/>

  </target>
  
  <target name="jar" description="Construct the JAR file." depends="compile,run-tests">
    <jar destfile="${example.jar}">
      <fileset dir="${classes.dir}"/>
    <fileset dir="${descriptor.dir}"/>
    </jar>
  </target>
  
  <target name="run" depends="compile" description="Run the Adder service.">
    <java classname="hivemind.examples.Main" classpathref="run.class.path" fork="true">
      <arg value="11"/>
      <arg value="23"/>
    </java>
  </target>

</project>

The important part is to package both the classes and the HiveMind module deployment descriptor into the JAR.

The only other oddity was to add src/conf to the runtime classpath; this is to include the log4j.properties configuration file; otherwise Log4J will write console errors about missing configuration.

Running the Examples

bash-2.05b$ ant run
Buildfile: build.xml

compile:
    [mkdir] Created dir: C:\workspace\hivemind-example\target\classes
    [javac] Compiling 15 source files to C:\workspace\hivemind-example\target\classes

run:
     [java] Inputs 11 and 23
     [java] Add     : 34
     [java] Subtract: -12
     [java] Multiply: 253
     [java] Divide  : 0



BUILD SUCCESSFUL
Total time: 3 seconds