Edgeflow Reference

     Previous  Next    Open TOC in new window  Open Index in new window  View as PDF - New Window  Get Adobe Reader - New Window
Content starts here

Writing Custom Modules

This section describes how to use the edgeflow APIs to write and deploy custom edgeflow modules.

The following topics are described in this section:

Sample custom workflow modules are available in the samples directory (RFID_EDGE_HOME\samples\EdgeFlows\CustomModules) in the Edge Server installation hierarchy. In order to use the custom modules, they must be deployed to the Edge Server. See Building and Deploying the Sample.

 


Edgeflow Module Overview

The Edge Server installation creates a standard set of edgeflow modules, as described in Edgeflow Module Reference.

You may want to write custom modules to satisfy specific use cases not handled by the standard set of modules. Java docs for the interfaces used within the edgeflow engine are available for your use. This section, when used along with the Java docs, describes how to write and deploy custom edgeflow modules.

Different edgeflow modules carry out different tasks, which are defined by the module's implementation. At run time, instances of workflow modules may be created, configured, and wired together to carry out an overall use case. Table 4-1 shows design time vs. run time tasks:

Table 4-1
Design Time Tasks
Run-time Tasks
Choose name for module
Create an instance of a module
Define module configuration parameters
Choose name for module instance
Define module input and output ports
Set module configuration parameter values
Write module code
Wire input and output ports to other module instances
Deploy module into Edge Server
 
Design Time Versus Run-time Tasks

Interaction Between Edgeflow Engine and Module When Creating a New Module

The sequence of calls that occur when the edgeflow engine receives a request to create a new edgeflow module (initiated by definePlugin on WorkflowManager by the Edge Server when you create a new instance in the console) is as follows:

  1. Look up the name of the class to be instantiated from the PluginMeta instance.
  2. Create the AbstractWorkflowModulePlugin module instance by invoking the zero-argument constructor.
  3. Call the initialize method on the module instance.
  4. Send the configuration parameters using the module's configure method.
  5. Call startup for any module startup functions that must happen before the module starts receiving messages.

Sequence of Calls When Sending Message to Edgeflow Module Port

As described in Configuring and Using Edgeflows, an edgeflow module instance can receive data from, and send data to, other module instances. It sends data to other module instances through output ports and receives data from other module instances through input ports. A module may have multiple input ports and multiple output ports.

The sequence of calls that occur when the edgeflow engine sends a message to a module's port is shown in Figure 4-1.

As described in Extend the AbstractWorkflowModulePlugin Class, your custom module should extend the AbstractWorkflowModulePlugin class, including providing an implementation for the abstract method createModuleDescriptor. This method should return a ModuleDescriptor that contains com.bea.rfid.edge.api.workflow.OneWayInputPort for all the input ports of the module and com.bea.rfid.edge.api.workflow.Port for all the output ports.

Figure 4-1 Sequence of Edgeflow Calls

Sequence of Edgeflow Calls

 


Writing Custom Modules

When you create a custom module, you must:

These tasks are described in the sections that follow.

Implement the Required Interface and Static Methods

At the very high level, a custom module class must satisfy the following requirements:

Extend the AbstractWorkflowModulePlugin Class

The abstract base class, com.bea.rfid.edge.toolkit.workflow.AbstractWorkflowModulePlugin implements WorkflowModulePlugin.

Your module should extend the AbstractWorkflowModulePlugin class.

The abstract base class provides the following:

Perform the following steps to implement an edgeflow module using this abstract base class:

  1. The class must have a zero-argument constructor, either explicitly implemented or the default one for the class when no others are implemented. The edgeflow framework relies on this constructor being there to instantiate a module.
  2. Provide an implementation for the configure() interface method.
  3. Provide an implementation for the abstract method createModuleDescriptor. This method should return a ModuleDescriptor that contains com.bea.rfid.edge.api.workflow.OneWayInputPort for all the input ports of the module and com.bea.rfid.edge.api.workflow.Port for all the output ports.
  4. If the module has startup functions to perform, override the doStartup method.
  5. Provide an input port implementation for each input port of the module. This can be done by extending the inner class OneWayPortImpl in AbstractWorkflowModulePlugin. The only method that needs to be implemented in this implementation is processMessage.
  6. If the module has an error output port, it should call setupErrorOutputs(PluginConfig) during the configuration step to make sure that the error output connection is set up correctly in the abstract class.

Example of Extending the AbstractWorkflowModulePlugin Class

This section provides an example of extending the AbstractWorkflowModulePlugin class.

The example code is from the ProvisioningSerialRangeModule.java file in the samples directory (EDGE_SERVER_HOME\samples\EdgeFlows\CustomModules\).

Note: Only code fragments are shown here for example purposes; see the actual samples for complete code.
  1. Implement a zero-argument constructor for the class.
  2. The example uses the default zero-argument constructor provided by Java.

  3. Provide an implementation for the configure() interface method, as shown in Listing 4-1.
  4. Listing 4-1 Sample Implementation of Configure() Method
    // interface method implementation
        public void configure(PluginConfig config)
            throws PluginException
        {
            long rangeStart = Long.parseLong(getStringValue(config, RANGE_START_PARAM));
            long rangeEnd = Long.parseLong(getStringValue(config, RANGE_END_PARAM));
            if (rangeEnd < rangeStart)
                throw new PluginException("Range end cannot be less than range start");                
            m_rangeBO.setBegin(rangeStart);
            m_rangeBO.setEnd(rangeEnd);
            
            // output connections
            m_outModules = getOutputModulesList(config, OUT_PORT_KEY);
            setupErrorOutputs(config);
        }
  5. Provide an implementation for the abstract method createModuleDescriptor. This method should return a ModuleDescriptor that contains com.bea.rfid.edge.api.workflow.OneWayInputPort for all the input ports of the module and com.bea.rfid.edge.api.workflow.Port for all the output ports, as shown in Listing 4-2.
  6. Listing 4-2 Example of Implementing the createModuleDescriptor() Method
    // abstract method implementation
        protected ModuleDescriptor createModuleDescriptor()
        {
            List <Port> inPorts = new ArrayList <Port> (1);
            inPorts.add(new EPCStringInputPort());
            return new ModuleDescriptorImpl(inPorts, getOutputPorts());
        }
    :
    private static List<Port> getOutputPorts()
        {
            List<Port> outPorts = new ArrayList(2);
            outPorts.add(new PortImpl(OUT_PORT_KEY,
                                      "Replenishment",
                                      ListOneWayPortType.URI_STRING_LIST));
            outPorts.add(ERROR_OUT_PORT);
            return outPorts;
        }
  7. If the module has startup functions to perform, override the doStartup method.
  8. The example does not use the doStartup method.

  9. Provide an input port implementation for each input port of the module. This can be done by extending the inner class OneWayPortImpl. The only method that needs to be implemented in this implementation is processMessage, as shown in Listing 4-3.
  10. Listing 4-3 Example Input Port Implementation
    /**
         * Implementation for the object input port.
         */
        private class EPCStringInputPort
            extends OneWayInputPortImpl
        {
            public EPCStringInputPort()
            {
                super(IN_PORT);
            }
            public void processMessage(Object msg)
                throws Exception
            {
                String inStr = (String)msg;
                log(Level.FINE, "processing message: " + inStr);
                // get EPC range for this EPC string
                List<String> ranges = m_rangeBO.getRanges(inStr);
                // get EPC range for this EPC string
                log(Level.FINE, "retrieved EPC ranges: " + ranges.toString());
                
                // send it out
                if ((m_outModules!=null && m_outModules.size()>0))
                {
                    sendToModules(ranges, m_outModules);
                }
            }
        }
  11. If the module has an error output port, it should call setupErrorOutputs(PluginConfig) during the configuration step to make sure that the error output connection is set up correctly in the abstract class:
  12. setupErrorOutputs(config);

Implement Static Method to Get PluginMeta

Your custom module needs to implement a static method to get the PluginMeta.

Every module has configuration parameters that determine the behavior of the module instance. For example, the External Sender module has parameters that let the user specify an XSLT file, destination URI(s), input data connections, and error output connections for a given instance of that module.

The configuration framework uses the static getPluginMeta() method to get the PluginMeta object with the configuration parameter names and other configuration parameters of the module.

The PluginMeta object contains a list of PluginParameterMeta objects with the details of its configuration parameters. The order of the PluginParameterMeta objects in the list is the order in which the administration console will display the parameters.

You use the com.connecterra.ale.dynamicconfig.bean.DynamicConfigBeanFactory(); constructor to create an instance of the com.connecterra.ale.dynamicconfig.api.DynamicConfigFactory interface (DynamicConfigBeanFactory implements DynamicConfigFactory), and then use the resulting DynamicConfigFactory.createPluginMeta() method to create an instance of PluginMeta. This sequence is shown in Listing 4-4.

When adding parameters to the PluginMeta object you created, you need to define all the output ports under the sub-configuration Module Outputs and all the input ports under sub-configuration Module Inputs.

To do this, the AbstractWorkflowModulePlugin class has utility methods, createOutputPortsSubConfig() and createInputPortsSubConfig(), to create the sub-configuration entries from a given list of ports. These utility methods create the ports under the appropriate sub-configuration entries and add constraints to enable correct wiring with other module instances. All of the required information is extracted from the ports that are passed in.

Listing 4-4 Example of Implementing PluginMeta
private static com.connecterra.ale.dynamicconfig.api.PluginMeta s_pluginMeta;
    
    public static  com.connecterra.ale.dynamicconfig.api.PluginMeta getPluginMeta()
        throws PluginException
    {
        if (s_pluginMeta != null)
            return s_pluginMeta;
        com.connecterra.ale.dynamicconfig.api.DynamicConfigFactory factory =
                         new com.connecterra.ale.dynamicconfig.bean.DynamicConfigBeanFactory();
        s_pluginMeta = factory.createPluginMeta(
                         ProvisioningSerialRangeModule.class.getName(),
                         MODULE_NAME,
                         I18N_MODULE_NAME,
                         com.connecterra.ale.dynamicconfig.api.PluginMeta.Role.WORKFLOW_MODULE);
        s_pluginMeta.setDescription("Provisioning Serial Range Module");
        // range start
        s_pluginMeta.addParameterMeta(factory.createRequiredNumericMeta(
            RANGE_START_PARAM, // key name
            I18N_RANGE_START_PARAM, // display name
            "EPC range start", // desc
            0,
            Long.MAX_VALUE));
                                  
        // range end
        s_pluginMeta.addParameterMeta(factory.createRequiredNumericMeta(
            RANGE_END_PARAM, // key name
            I18N_RANGE_END_PARAM, // display name
            "EPC range end", // desc
            0,
            Long.MAX_VALUE));
                                  
        // Output ports
        s_pluginMeta.addParameterMeta(createOutputPortsSubConfig(factory,
                                                                      getOutputPorts()));
        // Input ports
        List<Port> inPorts = new ArrayList(1);
        inPorts.add(IN_PORT);
        s_pluginMeta.addParameterMeta(createInputPortsSubConfig(factory, inPorts));
        return s_pluginMeta;
    }
    private static List<Port> getOutputPorts()
    {
        List<Port> outPorts = new ArrayList(2);
        outPorts.add(new PortImpl(OUT_PORT_KEY,
                                  "Replenishment",
                                  ListOneWayPortType.URI_STRING_LIST));
        outPorts.add(ERROR_OUT_PORT);
        return outPorts;
    }

Implement Custom Serializers and Deserializers

Edge flow modules may accept XML or string messages from outside the edgeflow system. These messages must be converted to the corresponding Java type before they are sent to the modules.

Edge flow messages can also be sent out externally (via HTTP or JMS, for example). The messages will be serialized to their XML or String over-the-wire format before they are sent outside.

Custom modules may have their own data types that need to be passed over the wire and sent to and from the custom modules using the standard framework mechanisms.

To do this, you need to write serializers that implement the com.bea.rfid.workflow.api.MessageSerializer interface, and deserializers that implement the com.bea.rfid.workflow.api.MessageDeserializer interface.

You then need to register these classes with the naming service (described in the Edgeflow API Overview section).

The com.bea.rfid.edge.plugin.workflow.epcis.encoding.EPCISDocumentSerializer class, which is shown in Listing 4-5, implements both the com.bea.rfid.workflow.api.MessageSerializer and com.bea.rfid.workflow.api.MessageDeserializer interfaces.

Once the serializers and deserializers are created and registered, the framework will look up the appropriate serializer based on the Java type, and the appropriate deserializer based on the XML namespace and root element name, and perform the conversion.

Listing 4-5 shows an example of registering serializers and deserializers.

Listing 4-5 Example of Registering Serializers and Deserializers
import com.bea.rfid.edge.plugin.workflow.epcis.encoding.EPCISDocumentSerializer;
:
private static void registerSerializers(WorkflowNamingService ns)
    {
        if (s_serializersRegistered)
            return;
        EPCISDocumentSerializer ser = new EPCISDocumentSerializer();
        ns.registerDeserializer(ser);
        ns.registerSerializer(ser);
        s_serializersRegistered = true;
    }
registerSerializers(getWorkflowManager().getWorkflowNamingService());

Build and Deploy Your Custom Module

Use the following steps to build and deploy the your custom modules:

  1. Modify the example build script (EDGE_SERVER_HOME\samples\EdgeFlows\CustomModules\build.bat) to compile your custom module Java files and package them into a jar file. In particular, make sure that rfta-common.jar and edgeserver.jar are in your classpath.
  2. Copy your custom module jar file to the RFID_EDGE_HOME\plugin\edgeflows directory.
  3. Restart the Edge Server.

The Edge Server Administration Console should now list the custom module.

 


Edgeflow API Overview

Figure 4-2 shows the high level packages in edgeflow API and the dependencies between the packages. More details of the packages and the classes in them are in the sections that follow.

Figure 4-2 Edgeflow API High-Level Packages

Edgeflow API High-Level Packages

Package com.bea.rfid.api.workflow

This package defines public interfaces that may be used on the client side or the server side of the edgeflow system.

Types

The types sub-package contains the data types used by various BEA-provided modules. This package defines interfaces and classes for common message data types sent or received by edge flow modules and messaging framework. The following interfaces and classes are defined:

Interfaces:

Classes :

Most modules have an error output port, which is used to send the details of errors during message processing. The ErrorOutput interface defines a type for the error output data. Similarly, an interface is defined for trigger output, and a class for module state.

Encoding

The encoding sub-package contains interfaces for encoding and decoding edge flow message types.

Custom module developers must write serializers and deserializers for custom message types if you want the serialization and deserialization to be handled transparently by the system. These serializers and deserializers must implement the interfaces and must be registered with the edgeflow naming service (described in the Edgeflow API Overview section).

For example, an External Sender will try to find a serializer for any message type that it receives by looking at the serializers registered with the system.

The following interfaces are defined:

Edgeflow modules may accept XML or other messages from outside the edgeflow system. These messages must be converted to the corresponding Java type before they are sent to the modules. Classes that convert messages from their over-the-wire format to the Java types must implement the MessageDeserializer interfaces.

Edge flow messages can be sent externally (via HTTP or JMS, for example). The messages will be serialized to their XML or other over-the-wire format, before they are sent outside. Classes that serialize the Java types must implement the MessageSerializer interfaces.

Config

The Config sub-package defines constants for the dynamic set types handled by the edgeflow framework. You should use the interface and class in this package when creating configuration parameters whose values come from a dynamically computed set.

For example, the DynamicSetConstants.DynamicSetID.LOGICAL_READER constant should be used as the set ID for a parameter whose value must be one of the logical readers defined in the system.

The following interface and class are defined:

Interface:

Class:

Package com.bea.rfid.edge.api.workflow

The interface for an edgeflow module and its properties as required by the edgeflow engine is defined here. These interfaces are used by the edgeflow engine to create a module instance or to get to an input port of a module and send a message to it.

Note: You do not need to implement these interfaces. Implementations are provided for your convenience in the com.bea.rfid.edge.toolkit.workflow package.

The following interfaces are defined in this package:

Package com.bea.rfid.edge.toolkit.workflow

This package defines a set of abstract classes and utility classes to assist in writing custom modules. The modules provided with the Edge Server installation are implemented using these classes.

The following classes are provided:

Package com.bea.rfid.workflow

This package defines a set of Java classes that do not rely on any edgeflow framework classes. Many of the modules provided with the Edge Server installation use these POJO classes. These classes isolate the core tasks of the module from the edgeflow framework. This means that if a custom module is to be written that behaves slightly differently from a standard module, it can be done easily by making use of these Java classes. Similarly, these classes make it easy to create similar modules in some other workflow framework outside of the Edge Server.

Three sub-packages are provided:

com.bea.rfid.workflow.epcis Sub-Package

The object and aggregation event generators include Java classes that generate EPCIS Object or Aggregation events for tag read inputs.

The following classes are provided:

com.bea.rfid.workflow.print Sub-Package

This is the Java class for programming tags. The following interface and class are provided:

Interfaces

Classes

com.bea.rfid.workflow.util Sub-Package

The following interfaces and classes are defined:

Interfaces

Classes

 


Custom Module Sample

The RFID Edge Server 3.0 software includes a custom module sample in the following directory: EDGE_SERVER_HOME\samples\EdgeFlows\CustomModules

The sample includes Java files that demonstrate how to write custom modules. These files include:

All of the source code for the example is provided.

Building and Deploying the Sample

Use the following steps to build and deploy the sample modules:

  1. Run the build script (EDGE_SERVER_HOME\samples\EdgeFlows\CustomModules\build.bat) to compile the Java files and package them into the custom-modules.jar file.
  2. Copy the custom-modules.jar file to the RFID_EDGE_HOME\plugin\edgeflows directory.
  3. Restart the Edge Server.

The Edge Server Administration Console should now list the custom modules, with names that begin with "Sample".


  Back to Top       Previous  Next