This section describes considerations related to creating custom services.
In the simplest case, creating a service involves the following tasks:
MBean interface. It extends jrunx.kernel.ServiceMBean and includes any methods exposed to JMX management (typically get/set methods for read/write attributes, and get methods for read-only attributes).
MBean interface and provides method definitions for nonmanageable functional behavior of the service. These methods are not be exposed to JMX, but service consumers can use them.Note: Services can contain nonmanageable functionality, which is best defined in an interface that extends the service's MBean interface. Any functionality described in such an interface is not exposed to JMX.
MBean interface (or the optional functional interface, if one was created). Also implement any lifecycle methods (init, start, stop, destroy) that are meaningful in the context of this Service's functionality. This class extends jrunx.kernel.ServiceAdapter. The ServiceAdapter class provides empty implementations of the lifecycle methods, so you only override the lifecycle methods that are meaningful to your service's functionality.
JMX enforces the following naming convention: your MBean interface name must end in MBean and your concrete class must have exactly the same name without the MBean suffix. For example, a service named MyService would have an MBean interface named MyServiceMBean.java, and the concrete service class would be named MyService.java.
In some cases, services must contain other services, and those internal services have no real meaning outside the context of their parent. The jrunx.kernel.ServicePartitionMBean interface and jrunx.kernel.ServicePartition class are designed to provide this functionality. These classes extend the ServiceMBean and ServiceAdapter to permit adding new services to the partition's JMX domain rather than the default global JMX domain. The ServicePartition has the following two methods to facilitate this behavior:
addService(serviceMBean service)
addService(ServiceMBean service, ObjectName serviceName)
The JRun EJB architecture uses this behavior. Each EJB container spawned by the EJBContainerFactory is a ServicePartition. EJB containers contain interceptors that handle functionality such as security, persistence, transactions, and so on. Each of these interceptors is a standard service, because they have no external meaning, they are not bound to the global default JMX domain. Instead, the container (since it extends ServicePartition) adds them to the container's domain.
To define a service in the jrun.xml file, perform the following tasks:
service element that specifies the service's class and assigns a name.
attribute elements within the service element. Each attribute element has a name parameter that corresponds to get and set methods in the MBean. For example, if you have an attribute with a filename parameter, the MBean interface must define getFilename and setFilename methods, and the service startup process calls the setFilename method before calling the init method.
The following example shows an XMLLogEventHandler service that is defined as a service within ThreadedLogEventHandler service in the jrun.xml file:
...
<service class="jrunx.logger.ThreadedLogEventHandler" name="ThreadedLogEventHandler">
<service class="jrunx.logger.FileLogEventHandler" name="FileLogEventHandler">
<attribute name="filename">
{jrun.rootdir}/logs/{jrun.server.name}-event.log</attribute>
<attribute name="rotationSize">200000</attribute>
<attribute name="rotationFiles">3</attribute>
<attribute name="heading">
# Created by JRun on {date MM/dd HH:mm:ss}</attribute>
<attribute name="closeDelay">5000</attribute>
</service>
<service class="XMLLogEventHandler" name="XMLLogEventHandler">
<attribute name="filename">
{jrun.rootdir}/logs/{jrun.server.name}-event.xml
</attribute>
</service>
</service>
...
The majority of manageable services in a running JRun server are not configured using jrun.xml. Typically, you configure a factory service or deployer service using jrun.xml, and that service creates and deploys numerous other services. You configure the other services using a deployment descriptor (for example, web.xml or ejb-jar.xml), a separate server-defined descriptor (for example, jrun-users.xml or jrun-jms.xml), or through hard coded operations inside another service (for example, the EJB deployer creates an EJBContainerFactory using a secure, final hard coded invocation).
The simplest way to automatically create a service and register it implicitly with the underlying JMX server is to use the jrunx.kernal.ServiceFactory object's static createService method:
// ServiceFactory takes (1) the MBean server, which is known
// by the variable "this.server" within all extensions of
// ServiceAdapter, (2) the name of the service, (3) the Class of the
// service (as a String), and (4) a Properties object containing
// attribute names and values to be automatically set on the Service.
Properties props = new Properties();
props.put("foo", "foobar");
ServiceFactory.createService(this.server, "service=FooService","jrun.example.FooService", props);
Another programmatic method of creating such services is to manually create the service and subsequently register it with the underlying MBean server:
// Create the object
SomeService svc = new SomeService();
// Often the service name is included as the value of
// the interface's static String OBJECT_NAME.
// Here the OBJECT_NAME is set to "service=FooService"
svc.setName(svc.OBJECT_NAME);
svc.setFoo("foobar");
svc.setWhateverInt(789);
// Register the object using this server's MBean Server
this.server.registerMBean(svc, svc.getName());
// Since you created the service, you must invoke its startup methods
svc.init();
svc.start();
If the service extends ServicePartition, it can use one of the addService methods:
// executed within a ServicePartition -- this snip is taken
// from the jrunx.kernel.prototype.PrototPartition example:
ProtoService newservice = new ProtoService();
newservice.setSomething("partition test");
newservice.setName("testPartitionedService");
addService(newservice);
// partitions must invoke the lifecycle methods of their services
newservice.init();
newservice.start();
newservice.setBindToJNDI(true);
newservice.bind();
The following section describes steps and sample code to create a custom service that implements a custom log event handler. For more information on JRun logging, see JRun Administrator's Guide.
To create a customized log event handler, you must perform the following steps:
MBean interface.
service element in the jrun.xml file.
The MBean interface, which is required as part of the JRun JMX-based architecture, defines methods that correlate to properties in the jrun.xml file. When you specify values for these properties in the jrun.xml file, JRun automatically initializes these values when the server starts. A log event handler MBean interface must extend the LogEventHandlerMBean interface and must also include the following items:
import statement for jrunx.logger.*.
import statement for jrunx.kernal.*.set and get method definitions for all properties that you pass to the log event handler through attribute elements in the jrun.xml file.
The following code defines an MBean interface for the XML log event handler:
import jrunx.logger.*;
import jrunx.kernel.*;
/**
* MBean interface for the XML Log Event Handler
*
* @author Macromedia, Inc.
* @date 5December2001
*/
public interface XMLLogEventHandlerMBean
extends LogEventHandlerMBean {
/**
* Sets the name of the file to be used for XML logging
* @param filename The filename
*/
public void setFilename(String filename);
/**
* Gets the name of the file to be used for XML logging
* @return The filename
*/
public String getFilename();
}
A log event handler is a Java class that must include the following items:
import statement for jrunx.logger.* (to compile, you must add jrun_root/lib/jrun.jar to the classpath)
jrunx.logger.LogEventHandler and implements the MBean interfaceget and set methods for the log event handler's propertieslogEvent method, defined as follows:public synchronized boolean logEvent(LogEvent event)
JRun calls the logEvent method when a log request occurs. This method contains the majority of your logging logic.
init, start, stop, and destroy methods, which perform lifecycle-specific processing.JRun calls log event handler methods in the following order:
set methods, using attributes defined in jrun.xml
initstartlogEvent, which is called repeatedly while JRun is runningstopdestroyThe following code defines a log event handler that writes log messages in a simple XML-like format:
import java.io.*;
import java.util.*;
import java.text.*;
import jrunx.logger.*;
/**
* Log events in XML format to a file
* This extends LogEventHandler, which requires that this
* class implement the logEvent method.
* It implements XMLLogEventHandlerMBean, which defines the
* getFilename and setFilename methods.
*/
public class XMLLogEventHandler extends LogEventHandler
implements XMLLogEventHandlerMBean {
protected String filename;
protected SimpleDateFormat dateFormat = new SimpleDateFormat("MM/HH hh:mm:ss");
protected String defaultFormat = null;
// JRun initializes the writer at server startup
public void init(Properties props) {
dateFormat = new SimpleDateFormat("MM/HH hh:mm:ss");
}
/**
* Sets the name of the file to be used for XML logging.
* @param filename The filename
*/
public void setFilename(String filename) {
if (filename == null) {
this.filename = "xml.log";
}
else {
this.filename = filename;
}
}
/**
* Gets the name of the file to be used for XML logging
* @return The filename
*/
public String getFilename() {
return filename;
}
// JRun calls the logEvent method when a log action is requested.
public synchronized boolean logEvent(LogEvent event) {
PrintWriter out = null;
try {
FileOutputStream fos = new FileOutputStream(filename, true);
out = new PrintWriter(fos, true);
// Get the pieces and parts.
Properties p = event.getVariables(defaultFormat);
String level = (String)p.get("log.level");
String msg = (String)p.get("log.message");
String exception = (String)p.get("log.exception");
Date date = new Date();
String formattedDate;
synchronized(dateFormat) {
formattedDate = dateFormat.format(date);
}
// Crude format.
out.println("<log>");
out.println(" <time>" + date + "</time>");
out.println(" <level>" + level + "</level>");
out.println(" <message>" + msg + "</message>");
out.println(" <exception>" + exception + "</exception>");
out.println("</log>");
}
catch (Exception ex) {
ex.printStackTrace();
}
finally {
if (out != null) {
out.close();
} // end if
} // end finally
return true;
} // end logEvent method
public void flush()
{
// No-op
}
}
You define the XMLLogEventHandler as a service within ThreadedLogEventHandler service in the jrun.xml file. The service contains a filename attribute, as the following example shows:
...
<service class="jrunx.logger.ThreadedLogEventHandler"
name="ThreadedLogEventHandler">
<service class="jrunx.logger.FileLogEventHandler" name="FileLogEventHandler">
<attribute name="filename">{jrun.rootdir}/logs/{jrun.server.name}-event.log
</attribute>
<attribute name="rotationSize">200000</attribute>
<attribute name="rotationFiles">3</attribute>
<attribute name="heading">
# Created by JRun on {date MM/dd HH:mm:ss}</attribute>
<attribute name="closeDelay">5000</attribute>
</service>
<service class="XMLLogEventHandler" name="XMLLogEventHandler">
<attribute name="filename">
{jrun.rootdir}/logs/{jrun.server.name}-event.xml</attribute>
</service>
</service>
...
Send me an e-mail when comments are added to this page | Comment Report
Current page: http://livedocs.adobe.com/jrun/4/JRun_SDK_Guide/creatingservices3.htm