# Edgeflow Reference

# 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:

- Edgeflow Module Overview
- Writing Custom Modules
- Edgeflow API Overview
- Custom Module Sample

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

## Writing Custom Modules

When you create a custom module, you must:

- Implement the Required Interface and Static Methods
- Implement Custom Serializers and Deserializers, if needed
- Build and Deploy Your Custom Module

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 class com.bea.rfid.edge.api.workflow.AbstractWorkflowModulePlugin.
- Implement the static method to get PluginMeta.

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

### 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:

- It uses the Edge Server event handling mechanism, sets up the event queue using the queue size specified in the Edge Server configuration and using resources available within the Edge Server.
- Startup function and receiving input messages to the module are done using the event queuing mechanism, which ensures that the module receives only one message at a time.
- If an exception is thrown by an input port's processMessage method, error output is sent with the exception information in it.
- If an exception occurs during doStartup, a warning level message is logged.
- Provides the utility method, sendToModules, to send output to other modules.
- Provides the log methods, which adds module and/or port names to the log message. The log methods use a logger created by this class, with the fully qualified class name of the module appended with the module name as the name of the logger. This allows logging to be filtered for the specific instance of the module or for all modules of that class.

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.

The example uses the default zero-argument constructor provided by Java.

2. Provide an implementation for the configure() interface method, as shown in Listing 4-1.
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);

        }

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, as shown in Listing 4-2.
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;

        }

4. If the module has startup functions to perform, override the doStartup method.

The example does not use 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. The only method that needs to be implemented in this implementation is processMessage, as shown in Listing 4-3.
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);

                }

            }

        }

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:

`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

### 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:

- ErrorOutput — Interface representing error output produced by edge flow modules.
- Manifest — Interface representing the manifest (parent and children identifiers expected) for an aggregation. This may just contain the error details, if an error occurred while trying to get the manifest.
- TriggerOutput — Represents trigger output generated by trigger receiver modules. This is an empty interface for now. This may be extended in the future to include additional information.

Classes :

- WorkflowModuleState — Class representing the different states of an edge flow module. A module may be in one of the following states:

WORKING: A module that is actively processing inputs that it receives is in this state.

IDLE: A module that is not processing its inputs is in this state.

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:

- MessageDeserializer — Interface that must be implemented by edge flow message deserializers for messages should be automatically deserialized by the framework.
- MessageSerializer — Interface that must be implemented by edge flow message serializers for messages should be automatically serialized by the framework.

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:

- DynamicSetConstants — Interface that defines enumerations for identifiers and parameter names of dynamic sets in WORKFLOW_MODULE role.

Class:

- DynamicSetConstants.DynamicSetID — Defines identifiers for the Dynamic Set types supported by the edge flow system.

### 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:

- ModuleDescriptor — Contains information about about a module's input and output ports.
- OneWayInputPort — Represents input port of an edge flow modules that receives one-way messages. This is the only input port type that is currently supported.
- Port — Represents an input or output port of a module.
- PortType — Holds information about what type of messages a port may send or accept. This interface has methods that will help determine if it is valid to connect two port types or if a port type will process a certain message.
- WorkflowManager — Edgeflow manager interface used by the edge server to define, reconfigure or delete edge flow modules. This interface is implemented by the edgeflow engine.
- WorkflowModulePlugin — This interface defines methods that are required by the edgeflow engine to instantiate, initialize, configure and startup an edgeflow module. The sequence in which initialization/startup methods are called is as follows:

1. constructor
2. initialize
3. configure
4. startup

- WorkflowNamingService — Interface to get to workflow related objects like workflow modules and serializers and deserializers for the different types. This interface is implemented by the edgeflow engine.

### 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:

- AbstractWorkflowModulePlugin — Abstract base class for edge flow modules.
- AnyPortType — Represents the type of a Port that sends or receives messages of any type.
- ListOneWayPortType — Represents a port type that sends or receives one-way list messages containing a certain class of objects.
- ModuleDescriptorImpl — A simple implementation of ModuleDescriptor.
- MultiTypeOneWayPortType — Represents the type of a one-way Port that accepts/sends multiple types of input.
- PortImpl Simple — Implementation of Port interface.
- SerializationHelper — Singleton class with utility methods that help in serialization and deserialization of objects within the edge flow system.
- SimpleOneWayPortType — Represents a port type that sends or receives one-way scalar type messages.

### 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
- com.bea.rfid.workflow.print
- com.bea.rfid.workflow.util

#### 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:

- AggregationEventGenerator — Java class to generate EPCIS Aggregation Events for input tags.
- AggregationReconciler — Compares a given manifest and aggregation and produces a result based on the property settings.
- EventGenerator — Abstract base class for generating EPCIS events for list of com.connecterra.ale.api.ECReportGroupListMember containing tags read.
- ObjectEventGenerator — Plain java class to generate EPCIS Object Events for input tags.
- ReconciliationResult — Utility class to hold Aggregation event reconciliation result.

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

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

Interfaces

- PrintObserver — Interface that must be implemented by classes that should process callbacks from PrintTag.

Classes

- PrintTag — Java class for programing tags for specified EPC class or with the specified EPC.

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

The following interfaces and classes are defined:

Interfaces

- AccumulationObserver — Interface for observers of Accumulator. AccumulationObserver is the interface that Accumulator uses to notify regarding the final accumulated tags.
- DirectionObserver — Interface for observers for A to B direction.

Classes

- Accumulator — This Java class encapsulates the Tag Accumulator module functionality. AccumulationObserver is the interface that Accumulator uses to notify regarding the final accumulated tags.
- BarcodeDecoder — Converts bar code string or EPC to EPC ID pattern.
- BarcodeDecoder.BarcodeEncoding Converts EPC ID pattern to UCC12 or UCC13???????
- Directionality — Class to determine direction of tags moving between two points. This Java class determines the direction of tags based on ECReports with addition and deletion reports, or based on tag reads inputs (tag list) at different points.
- TagListBuilder — The tag list builder is used to get list of tags from an ECReports or PCWriteReport.

## 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:

- Module that generates an EPCIS Object Event from a list of tags.
- Module that generates an EPCIS Aggregation Event from a list of tags.
- Module that provides EPC serial numbers in the configured range for an EPC type
- Module that generates manifest for an EPCIS Aggregation Event using the EPCIS query interface.

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".