When you write asynchronous applications, the receiver has to be started manually. In most asynchronous applications, there is no automatic mechanism to start a service. A service is an application whose sole purpose is to wait for an event and then process the event. A good service infrastructure decouples the subsystem that processes the request and the subsystem that initializes the subsystem that processes the request. Relating this to the Commons Bridge best practice, the service subsystem is the interface that defines some kind of intention .
The Services package is a Jakarta Commons project that allows a developer to create a plug-in architecture. The service subsystem could be some program that initializes some state and then needs to start the various components. The components that are started are defined in a configuration file. This allows an administrator to configure which subsystem is instantiated , and in which order the instantiation occurs.
Tables 6.5 and 6.6 contain the abbreviated details necessary to use the Services package.
Item | Details |
---|---|
CVS repository | Jakarta-commons-sandbox |
Directory within repository | services |
Main packages used | org.apache.commons.services |
Class/Interface | Details |
---|---|
[services].ServiceManager | The main class that is used as a faade to control the configuration file contents. Essentially, this class is used to load a configuration and then execute the configuration. |
[services].Service | A base class used when you are creating custom services. |
[services].EventRegistration | A base class used to define a registration class that is used to reference an actual event. |
[services].Event | A base class used to define a custom event. |
The foundation of the Services package is an XML-defined service configuration file. The service configuration file is shown in Listing 6.30 (note that for space reasons, the package identifier com.devspace. jseng .asynchronous is replaced with com ).
<service-manager> <event-module> <event name="someEvent" type="com.CustomEventRegistration"/> <event name="anotherEvent" type="org.apache.commons.services.EventRegistration"/> </event-module> <service-module> <service name="firstService" type="com.ServiceOutput" /> <service name="secondService" type="com.ServiceProcessor" /> </service-module> <queue-module> <queue name="testQueue1" type="org.apache.commons.services.SequenceQueue" sequence="secondService,firstService"/> <queue name="testQueue2" type="org.apache.commons.services.SequenceQueue" sequence="firstService,secondService"/> </queue-module> </service-manager>
The service configuration file defined in Listing 6.30 has three sections: events, services, and queues. The events section is defined by the XML element event-module . Contained within the events section are individual events that can be sent to various services. It is important to realize that events are not events in the asynchronous messaging sense. In the Services package, events are used as context objects defined in the Command best practice. The idea behind the event object is to be able to dynamically configure a context object that will be consumed by the individual services. The XML element event is defined by two attributes: name and type . The attribute name defines an easy-to-understand identifier used to uniquely identify the event. The attribute type is a class that the Services package instantiates to generate an event object.
The services section is defined by the XML element service-module . Contained with the services section are individual services used to process some type of program logic. The XML element service has two attributes: name and type . The attribute name defines a unique identifier used for reference purposes. The attribute type is a classname that is instantiated when the service is started.
The queues section is defined by the XML element queue-module . Contained with the queues section are individual queues. However, as with the events section, the individual queues are not queues in the messaging sense, but more like batch processes that involve the execution of multiple services. The idea of service queues is to enable you to define a batch process that allows the definition of a lifecycle of service calls.
In Listing 6.30, two services were defined. To be able to execute those services, they have to be implemented as in Listing 6.31.
public class ServiceOutput extends Service { public Object execute( Event event ) { System.out.println( "Output from ServiceOutput"); return null; } } public class ServiceProcessor extends Service { public Object execute( Event event ) { System.out.println( "Output from ServiceProcessor"); return null; } }
In Listing 6.31, the custom services are classes that subclass the class Service . Each of the services implements one method, execute . The method execute is called when the service is to execute its programming logic. A null is returned, because in the Services package the return value of a service is not inspected. The execute method has one parameter, which is an event object. To execute these two services, Listing 6.32 is used.
ServiceManager serviceManager = new ServiceManager(); serviceManager.init( "file:///src/common/service.xml"); serviceManager.execute();
Listing 6.32 is extremely small and simple. The class ServiceManager is instantiated. The method init loads and parses a service configuration file like one defined in Listing 6.30. Once the service configuration file is processed , you can execute the services using the method execute .
When you call the method execute without any parameters, all of the services defined in the service configuration file will be called. The order in which the services are called is not predetermined. The order depends on the iterator used within the Services implementation. At the time of this writing, the iterator used was based on the class HashMap .
To have more control over what service is executed in which context, you can execute a specific queue, as shown in Listing 6.33.
ServiceManager serviceManager = new ServiceManager(); serviceManager.init( "file:///src/common/service.xml"); serviceManager.execute( "testQueue1");
In Listing 6.33, the only change from Listing 6.32 is the addition of a parameter to the method execute . The additional parameter specifies the queue that will be executed. This, in turn , causes the execution of a single or multiple services as per the comma-separated attribute sequence of Listing 6.30. Each service is executed in order that is referenced in the sequence attribute.
Each time thus far when the service was executed, the event object passed to the service was a default Event class instance. The service cannot do anything constructive with the event because it does not contain any useful information. To make the event more interesting and meaningful, we need to create two new classes. The first class is a class based on the class EventRegistration, as shown in Listing 6.34.
public class CustomEventRegistration extends EventRegistration { public String getType() { return "com.devspace.jseng.asynchronous.CustomEvent"; } }
The class CustomEventRegistration in Listing 6.34 is like a factory. This class does not actually instantiate the event class; rather, it provides a reference to the event class that will be instantiated. The reason for this has to do with how the services are called. The event class is instantiated and used only for the scope of the service call. Because the service call could occur thousands of times, the event class is allocated from a pool of objects (as we discussed in Chapter 4). The second new class, which is the class that is the event, is defined in Listing 6.35.
public class CustomEvent extends Event { private String _data; public CustomEvent() { } }
The class CustomEvent in Listing 6.35 is not defined in any particular fashion. The only requirement is that the event class subclass the class Event . Listing 6.36 uses the event class in conjunction with the queue used in Listing 6.33.
ServiceManager serviceManager = new ServiceManager(); serviceManager.init( "file:///src/common/service.xml"); Event event = serviceManager.getEventModule().getEvent( "someEvent"); serviceManager.execute( "testQueue1", event);
In Listing 6.36, the event instance is not directly instantiated, but retrieved. The class ServiceManager has references to the individual sections described in the service configuration file. You retrieve a specific event by referencing the event registration identifier of the service configuration file. In Listing 6.36, this means using the identifier someEvent because in Listing 6.30, this was cross-referenced with the class CustomEventRegistration . In addition, the class CustomEventRegistration references the class CustomEvent , which means that the event variable will reference an instance of the class CustomEvent . Then, the method execute is called with the event instance as the second parameter.