In HiveMind, a service is simply an object that implements a particular
interface, the service interface. You supply the service
interface (packaged as part of a module). You supply the core
implementation of the interface (in the same module, or in a different
module). At runtime, HiveMind puts it all together.
HiveMind uses four service models: primitive, singleton, threaded and
pooled. In the primitive and singleton models, each service will
ultimately be just a single object instance. In the threaded and pooled
models, there may be many instances simultaneously, one for each thread.
Unlike EJBs, there's no concept of location transparency: services are
always local to the same JVM. Unlike XML-based web services, there's no
concept of language transparency: services are always expressed in terms
of Java interfaces. Unlike JMX or Jini, there's no concept of hot-loading
of of services. HiveMind is kept delibrately simple, yet still very
powerful, so that your code is kept simple.
A service definition begins with a Java interface, the service
interface. Any interface will do, HiveMind doesn't care, and there's no
base HiveMind interface.
A module descriptor may include <service-point> elements to define
services. A module may contain any number of services.
Each <service-point> establishes an id for the service and defines the
interface for the service. An example is provided later in this
HiveMind is responsible for supplying the service implementation as
needed; in most cases, the service implementation is an additional Java
class which implements the service interface. HiveMind will instantiate
the class and configure it as needed. The exact timing is determined
from the service's service model:
primitive : the service is constructed on first
singleton : the service is not constructed until a
method of the service interface is invoked
threaded : invoking a service method constructs and
binds an instance of the service to the current thread
pooled : as with threaded, but service
implementations are stored in a pool when unbound from a thread for
future use in other threads.
Additional service models can be defined via the hivemind.ServiceModels
HiveMind uses a system of proxies for most of the service
models (all except the primitive service model, which primarily exists
to bootstrap the core HiveMind services used by other services). Proxies
are objects that implement the service interface and take care of
details such as constructing the actual implementation of a service on
the fly. These lifecycle issues are kept hidden from your code behind
A service definition may include service contributions, or may
leave that for another module.
Ultimately, a service will consist of a core implementation (a Java
object that implements the service interface) and, optionally, any
number of interceptors. Interceptors sit between the core implementation
and the client, and add functionality to the core implementation such as
logging, security, transaction demarkation or performance monitoring.
Interceptors are yet more objects that implement the service interface.
Instantiating the core service implementation, configuring it, and
wrapping it with any interceptors is referred to as constructing the
service. Typically, a service proxy will be created first. The
first time that a service method is invoked on the proxy, the service
implementation is instantiated and configured, and any interceptors for
the service are created.
Any module may contribute to any service extension point. An <implementation>
element contains these contributions. Contributions take three forms:
- Service constructors:
<interceptor> to add additional logic to a core implementation
A service constructor is used to instantiate a Java class as the core
implementation instance for the service.
There are two forms of service constructors: instance creators and
An instance creator is represented by a <create-instance> element. It
includes a class attribute, the Java class to instantiate.
An implementation factory is represented by a <invoke-factory>
element. It includes a service-id attribute, the id of a service
implementation factory service (which implements the ServiceImplementationFactory
interface). The most common example is the hivemind.BuilderFactory
An implementation factory is used to create a core implementation for
a service at runtime.
Often, the factory will need some additional configuration
information. For example, the hivemind.lib.EJBProxyFactory
service uses its parameters to
identify the JNDI name of the EJB's home interface, as well as the
home interface class itself.
Parameters to factory services are the XML elements enclosed by the <invoke-factory>
element. Much like a configuration contribution, these parameters are
converted from XML into Java objects before being provided to the
The most common service factory is hivemind.BuilderFactory. It is
used to construct a service and then set properties of the service
An interceptor contribution is represented by an <interceptor>
element. The service-id attribute identifies a service interceptor
factory service: a service that implements the ServiceInterceptorFactory
An interceptor factory knows how to create an object that implements
an arbitrary interface (the interface being defined by the service
extension point), adding new functionality. For example, the hivemind.LoggingInterceptor
factory creates an instance that logs entry and exit to each method.
The factory shouldn't care what the service interface itself is ...
it should adapt to whatever interface is defined by the service
extension point it will create an instance for.
A service extension point may have any number of interceptor
contributions. If the order in which interceptors are applied is
important, then the optional before and after
attributes can be specified.
In this example, is was desired that any method logging occur first,
before the other interceptors. This ensures that the time taken to log
method entry and exit is not included in the performance statistics
(gathered by the performance interceptor). To ensure that the logging
interceptor is the first, or earliest, interceptor, the special value
* (rather than a list of interceptor service ids) is
given for its before attribute (within the <interceptor>
element). This forces the logging interceptor to the front of the list
(however, only a single interceptor may be so designated).
Likewise, the security checks should occur last, after logging and
after performance; this is accomplished by setting the after
attribute to *. The performance interceptor naturally
falls between the two.
This is about as complex as an interceptor stack is likely to grow.
However, through the use of explicit dependencies, almost any
arraingment of interceptors is possible ... even when different
modules contribute the interceptors.
Interceptors implement the toString() method to provide
a useful identification for the interceptor, for example:
<Iterceptor: hivemind.LoggingInterceptor for
This string identifies the interceptor service factory
(hivemind.LoggingInterceptor), the service extension point
(com.myco.MyService) and the service interface
If toString() is part of the service interface (really,
a very rare case), then the interceptor does not override the
service implementation's method. However, this is not a recommended practice.
A short example
As an example, let's create an interface with a single method, used to
add together two numbers.
public interface Adder
public int add(int arg1, int arg2);
We could define many methods, and the methods could throw exceptions.
Once more, HiveMind doesn't care.
We need to create a module to contain this service. We'll create a
simple HiveMind deployment descriptor. This is an XML file, named
hivemodule.xml, that must be included in the module's META-INF
<module id="com.myco.mypackage" version="1.0.0">
<service-point id="Adder" interface="com.myco.mypackage.Adder"/>
The complete id for this service is com.myco.mypackage.Adder
, formed from the module id and the service id. Commonly, the service id
will exactly match the complete name of the service interface, but this
is not required.
Normally, the <service-point> would contain a <create-instance> or <invoke-factory>
element, used to create the core implementation. For this example, we'll
create a second module that provides the implementation. First we'll
define the implementation class.
public class AdderImpl implements Adder
public int add(int arg1, int arg2)
return arg1 + arg2;
That's what we meant by a POJO. We'll create a second module to provide
<module id="com.myco.mypackage.impl" version="1.0.0">
The runtime code to access the service is very streamlined:
Registry registry = . . .
Adder service = (Adder) registry.getService("com.myco.mypackage.Adder", Adder.class);
int sum = service.add(4, 7);
Another module may provide an interceptor:
<module id="com.myco.anotherpackage version="1.0.0">
Here the Logging interceptor is applied to the service extension point.
The interceptor will be inserted between the client code and the core
implementation. The client in the code example won't get an instance of
the AdderImpl class, it will get an instance of the interceptor, which
internally invokes methods on the AdderImpl instance. Because we code
against interfaces instead of implementations, the client code neither
knows nor cares about this.
Primitive Service Model
The simplest service model is the primitive service
model; in this model the service is constructed on first reference. This
is appropriate for services such as service factories and interceptor
factories, and for several of the basic services provided in the hivemind module.
Singleton Service Model
Constructing a service can be somewhat expensive; it involves
instantiating a core service implementation, configuring its properties
(some of which may also be services), and building the stack of
interceptors for the service. Although HiveMind encourages you to define
your application in terms of a large number of small, simple, testable
services, it is also desirable to avoid a cascade of unneccesary object
creation due to the dependencies between services.
To resolve this, HiveMind defers the actual creation of services by
default. This is controled by the model attribute of the <service-point>
element; the default model is singleton.
When a service is first requested a proxy for the service is
created. This proxy implements the same service interface as the actual
service and, the first time a method of the service interface is
invoked, will force the construction of the actual service (with the
core service implementation, interceptors, references to other services,
and so forth).
In certain cases (including many of the fundamental services provided
by HiveMind) this behavior is not desired; in those cases, the
primitive service model is specified. In addition, there is
rarely a need to defer service implementation or service interceptor
Threaded Service Model
In general, singleton services (using the singleton or primitive
service models) should be sufficient. In some cases, the service may
need to keep some specific state. State and multithreading don't mix, so
the threaded service model constructs, as needed, a
service instance for the current thread. Once constructed, the service
instance stays bound to the thread until it is discarded. The particular
service implementation is exclusive to the thread and is only accessible
from that thread.
The threaded service model uses a special proxy class (fabricated at
runtime) to support this behavior; the proxy may be shared between
threads but methods invoked on the proxy are redirected to the private
service implementation bound to the thread. Binding of a service
implementation to a thread occurs automatically, the first time a
service method is invoked.
The service instance is discarded when notified to cleanup; this is
controlled by the hivemind.ThreadEventNotifier service. If your
application has any threaded services, you are responsible for invoking
the fireThreadCleanup() method of the service.
A core implementation may implement the Discardable interface. If so,
it will receive a notification as the service instance is discarded.
HiveMind includes a servlet filter to
take care creating the Registry and managing the ThreadEventNotifier
Pooled Service Model
The pooled service model is very similar to the threaded model, in that
a service implementation will be exclusively bound to a particular
thread (until the thread is cleaned up). Unlike the threaded model, the
service is not discarded; instead it is stored into a pool for later
reuse with the same or a different thread.
As with the threaded model, all of this binding and unbinding is hidden
behind a dynamically fabricated proxy class.
Core service implementations may implement the RegistryShutdownListener
interface to receive a callback for final cleanups (as with the
singleton and deferred service models).
In addition, a service may implement the PoolManageable interface to
receive callbacks specific to the pooled service. The service is
notified when it is activated (bound to a thread) and deactivated
(unbound from the thread and returned to the pool).
As discussed, the service model determines when a service is
instantiated. In many cases, the service needs to know when it has been
created (to perform any final initializations) or when the Registry has
been shut down.
A core service implementation may also implement the RegistryShutdownListener
interface. When a Registry is shutdown, the
registryDidShutdown() method is invoked on all services (and many
other objects, such as proxies). The order in which these notifications
occur is not defined. A service may release any resources it may hold at
this time. It should not invoke methods on other service interfaces.
The threaded service model does not register services
for Registry shutdown notification; regardless of whether the core
service implementation implements the RegistryShutdownListener interface
or not. Instead, the core service implementation should implement the
Discardable interface, to
be informed when a service bound to a thread is discarded.
It is preferred that, whenever possible, services use the singleton
service model (the default) and not the primitive model. All the service
models (except for the primitive service model) expose a proxy
object (implementing the service interface) to client code (included
other services). These proxies are aware of when the Registry is
shutdown and will throw an exception when a service method is invoked on
Services and Events
It is fairly common that some services will produce events and other
services will consume events. The use of the hivemind.BuilderFactory
to construct a service simplifies this, using the <
event-listener> element. The BuilderFactory can register a
core service implementation (not the service itself!) as a
listener of events produced by some other service.
The producing service must include a matched pair of listener
registration methods, i.e., both addFooListener() and
removeFooListener. Note that only the implementation
class must implement the listener interface; the service interface
does not have to extend the listener interface. The core service
implementation is registered directly with the producer service,
bypassing any interceptors or proxies.
Frequently Asked Questions
Why do I pass the interface class to getService()?
This is to add an additional level of error checking and reporting.
HiveMind knows, from the module descriptors, the interface provided by
the service extension point, but it can't tell if you know
that. By passing in the interface you'll cast the returned service to,
HiveMind can verify that you won't get a ClassCastException. Instead,
it throws an exception with more details (the service extension point
id, the actual interface provided, and the interface you passed it).
What if no module provides a core implementation of the
HiveMind checks for a service constructor when the registry itself
is assembled. If a service extension point has no service
constructor, an error is logged (identifying the extension point
id). In addition, getService() will throw an
What if I need to do some initializations in my service?
If you have additional initializations that can't occur inside your
core service implementations constructor (for instance, if the
initializations are based on properties set after the service
implementation object is instantiated), then your class should use
the hivemind.BuilderFactory to invoke an initializer method.
What if I don't invoke Registry.cleanupThread()?
Then service implementations bound to the current thread stay
bound. When the thread is next used to process a request, the same
services, in whatever state they were left in, will be used. This
may not be desirable in a servlet or Tapestry application, as some
state from a client may be left inside the services, and a different
client may be associated with the thread in later executions.
What if I want my service to be created early, not just when
Contribute your service into the hivemind.EagerLoad
configuration; this will force HiveMind to instantiate the service
on startup. This is often used when developing an application, so
that configuration errors are caught early; it may also be useful
when a service should be instantiated to listen for events from some