Developing EdgeClick Processor Services and Agents03 August 2006 |
![]() |
The EdgeClick Processor (ECP) is a server environment which presents a set of built-in services and APIs for doing the work necessary to run an EdgeClick mobile digital service.
Services written for this environment are written in simple Java. In general, only knowledge of the Java language and basic Java APIs are necessary. Applications involving a local database will require knowledge of some Java database programming methodology; several may be used.
Development of these services is generally done within an IDE; Eclipse is recommended. Other IDEs and command-line development and builds may be done as well. Ant is used as the Java build tool for services.
After EdgeBuilder installation, the ECP is started and stopped via shortcut icons located on the Windows desktop. Once built, services are hot-deployed into the ECP environment; it is not necessary to bring down and restart the ECP. If Eclipse is being used, the whole build and deployment (or redeployment) cycle is done with one click of a button, making for very fast development and debugging cycles. Much of a service's functionality may be tested with special-purpose HTML Test Clients, which allow you to develop and test the service component without having to go through an actual mobile device.
Developing EdgeClick services involves three primary steps:
The core APIs for the EdgeClick Processor are exposed as packages
within the hierarchy com.sco.phoenix.commons
("Phoenix" is just a SCO code word for the EdgeClick and Me Inc.
projects, and has no other significance).
The core APIs have generally been designed along modern Java principals, especially that of using "Plain Old Java Objects" (POJOs).
Documentation for the core APIs is presented in the usual javadoc format; see the link on the EdgeBuilder Documentation page.
Some of the key APIs will be mentioned in the following sections.
With the help of the core APIs, it is easy to parse incoming messages and generate response messages without knowing the underlying protocol, i.e. HTTP.
Internally, the HTTP POST message is converted to a "plain old Java object" (POJO) represented by the com.sco.phoenix.commons.helper.Parameters object. When a service consumes a messsage it is passed the CMD opcode and Parameters which represents the message body sent by the client. For example, when the Shout service consumes the above message and wants to retrieve the account_id portion it uses:
int accountId = params.getInt(PARAM_ACCOUNTID);
The params is the Parameters object passed to the service. PARAM_ACCOUNTID is a constant String defined as "account_id". The Parameters.getInt() call will retrieve the parameter passed to the ECP as "account_id" and return its value as a java.lang.Integer. The Parameters class has has several methods to retrieve parameters as various types such as Boolean, Date, String, Object, etc.
After service consumes a command opcode, Parameters object, and performs some task, it must generate a response. The response is encapsulated via a com.sco.phoenix.commons.Result object. The Result object abstracts the actual messsaging format that the ECP will return to the client, i.e. a HTTP message. In the future the underlying protocol between clients and the protocol could change depending on deployment but the Service writer doesn't and shouldn't care! A Result object has a required return code and a number of optional components. When a Service successfully consumes a message it can return success by doing the following:
return new Result(Result.CODE_SUCCESS);
The above line allocates a new Result object and initializes the only required member field, return code, with CODE_SUCCESS which equals zero. The Result object defines several types of standard return codes a Service writer can use to determine whether or not a message was consumed successfully or not. If a service receives a message with malformed arguments it tell the client exactly that:
return new Result(Result.CODE_INVALID_ARGUMENTS, e.getMessage(), null);
The Return object specifies the return code CODE_INVALID_ARGUMENTS as well as an optional String message, the exception that was thrown, and any optional data, null. The ECP messsage bus will take this return code and construct an HTTP response for the client to process.
The CMD
codes used between the client and ECP are
established in the service and client code and must be:
CMD
codes used by all
services running on the ECPAn app_code
can be used along with a CMD
code,
and can be any integer, so long as it is unique amongst all the
app_code
s used by all services running on the ECP.
The following Return Codes
can be returned to the client
by the ECP in response to a CMD
:
0. SUCCESS 1. NO COMMAND GIVEN 2. UNKNOWN COMMAND 3. INVALID ARGUMENTS 4. EXCEPTION 5. NOT AUTHENTICATED 6. NOT AUTHORIZED 7. APP EXCEPTION 8. UNSUPPORTED VERSION 9. REQUIRES PROFILE UPDATE 10. NEW VERSION AVAILABLE 11. PARTIAL SUCCESS 12. REQUIRES LICENSE ACCEPTANCE
UNSUPPORTED VERSION
means that the current
version is no longer supported and you must upgrade before continuing.
REQUIRES PROFILE UPDATE
means that the system
only has temporary information about you and you must supply additional
information (for example after someone first signs up and is given a
temporary password).
NEW VERSION AVAILABLE
means that the current
version is still supported, but that a newer version is available and
you should upgrade soon.
Information needed from the EdgeClick Processor local database (regarding EdgeClick accounts, subscribers, groups, contacts, etc.) is acquired through provided APIs such as IAccount and ITeam, and requires no database programming on the part of the developer.
However, an application service may also need to define its own local database. This is typically done to keep track of agents, or to accumulate or cache information collected from mobile devices, or for various other reasons. An application service-specific local database may be defined and accessed by a variety of Java database techniques, including:
Which of these approaches to use depends primarily on the knowledge set and desires of the EdgeClick service developer; all are supported and have been successfully used.
In order to write your own service, you will need to implement the com.sco.phoenix.commons.services.processor.IProcessor interface. Each EdgeClick Service is encapsulated by an implementation of the IProcessor interface. The IProcessor interface defines the following methods:
true
or false
if the Command opcode can be consumed by your
service true
or false
if the request is compatible with your service's
version Every new EdgeClick service that gets deployed must implement the above methods.
Let's take a look at the HelloService which is a simple sample service that comes with EdgeBuilder. (Please see the EdgeBuilder Release Notes on how to install and deploy the HelloService.)
Here is the bulk of the code:
@Stateless
@LocalBinding ( jndiBinding = "edgeclick/services/HelloWorldService/local" )
public class HelloProcessor extends BaseProcessor implements IProcessor
{
// Appcode
private static final int _applicationCode = 1338;
// Commands
private static final int HWS_CMD_SAY_HELLO_SUBSCRIBER = 0x1;
// Dependecy Injections
@EJB private static IRoadrunnerApi RoadRunnerApi;
@EJB private static IPhoenixCommons commons;
...
public Result processCommand(int cmd, Parameters params) {
Result result = null;
switch (cmd) {
case HWS_CMD_SAY_HELLO_SUBSCRIBER:
result = sayHelloSubscriber(params);
break;
default:
result = new Result(Result.CODE_UNKNOWN_COMMAND);
break;
}
return result;
}
private Result sayHelloSubscriber(Parameters params) {
Result result = null;
int subscriberId = params.getInt(PARAM_USERID);
int accountId = params.getInt(PARAM_ACCOUNTID);
// Use the Hosted Broker to find the actual Subscriber based on account
// and subscriber ID parsed from our contacts XML document above
Subscriber fromSubscriber = commons.getTeam().getSubscriber(accountId, subscriberId);
// We are sending it to ourself, construct the neccessary arguments!
List<Subscriber> toSubscribers = new ArrayList<Subscriber>();
toSubscribers.add(fromSubscriber);
String fromEmail = fromSubscriber.getEmail();
String message = "Hello " + fromSubscriber.getFirstName();
// Send SMS
Utils.getLogger().log(Level.INFO, "Sending SMS to " + fromEmail);
commons.getPostOffice().sendRoadRunnerSMSOrEmail(toSubscribers, fromSubscriber, "Hello!", "", "", message, _applicationCode, -1, -1, accountId);
result = new Result(Result.CODE_SUCCESS);
return result;
}
The processCommand() method services one command opcode, CMD_HWS_SAY_HELLO_SUBSCRIBER, which calls the function sayHelloSubscriber() that contains the bulk of the service logic.
In addition, certain Java annotations (markers with a starting @
,
whose use was greatly expanded in the Java 5 revision of the language)
must be specified in the service code. This allows the ECP to correctly
deploy and process the service. It is not necessary to fully understand
what these annotations do, but simply to insert them in the right
places.
In order to define the HelloService as an EdgeClick Processor service, we use the @Stateless annotation which is located on top of our class definition, followed by the @LocalBinding annotation as shown. The name given in the @LocalBinding should be something related to, but not the same as, the class name. Within the service class, there are two fixed @EJB annotations which are used to make available references to the ECP's core API services,
In the sayHelloSubscriber() function, we obtain the requests subscriber and account ID which are then used to lookup and find the actual Subscriber object stored in the database. We do that by first retrieving the appropriate connector via the getTeam() call and then retrieve the subscriber by calling its getSubscriber() method. As you can see, the getTeam() is not the only service available via the IPhoenixCommons service locator. There is also the IPostOffice interface which provides a general purpose service to send SMS or email messages. Here the HelloService makes use of it to send an SMS to the active subscriber.
The ECP service application for Meter (Meter Reading) Demo is included in EdgeBuilder. This is not a large application, but it does exercise some key areas that the HelloService does not, such as ECP local database access and agent calling.
What follows is some background on the Meter demo, covering the client, ECP, and agent components that are all included as samples.
The idea is that there are 100 or so Billing Centers, and 1000s of Meter Readers. Each Meter Reader is assigned to work for just one Billing Center. Each Meter Reader is also associated with a Me Inc subscriber login. (These associations would be managed by web admin pages, which don't exist yet; right now manual MySQL table changes on the EdgeClick Processor have to be made for them.)
So when you enter the app, you are first requested to log in. If the subscriber is in the Meter Readers table, the login is accepted, and you are associated with a Meter Reader ID and a Billing Center.
You then do "go back" and have a choice of entering the actual meter reading amount, keying it either on the Meter ID (a number on the physical meter outside the house, you can make anything up in the demo) or by the house's address (but that option isn't implemented yet). The Meter Level reading number can also be anything. When you do "send", the meter reading info is sent to the appropriate Billing Center.
Normally the response back would just be an "Ok, processed" text. But for demo purposes, this output has been expanded to include the details about the Meter Reader and Billing Center, so people can see an idea of what data is being used in the system. If payment were made on the spot and a physical receipt were printed (discussed for Meter), the receipt might look like this output in part.
In terms of what the back-end agent is actually doing with the meter reading data, right now it is writing it out to a flat file in CSV form on the Billing Center's agent machine. Some existing application would then periodically read this file to gain the data. An example entry looks like this:
Timestamp=Thu Mar 09 21:37:46 EST
2006,meterReader=1051,meterId=222,customerName=null,customerAddress=null,custome
rTown=null,currentMeterLevel=555.0
In real life, the integration would probably be tighter than just depositing entries in a flat file, but specifics would all depend on the individual customer. For example, a database 4GL that an application is written in might have web services support, which would make integration very direct.
Alternate user interface strategies are also possible, such as where the meter reading is tapped in on a numeric keypad representation (like the Treo phone interface).
See the Getting Started Guide for a functional description of this sample.
When things are not working as expected, it is sometimes helpful to see the raw HTTP transmission going out over the wire between the client and the EdgeClick Processor. There are a variety of tools that allow you to see this. Two that have been used successfully in EdgeClick development are Apache's tcpmon and the Network Monitor in Sun's Wireless Toolkit for Java ME.
Once you implement IProcessor in your own service, you need to package it for deployment. All services use three standard Java deployment formats, which in increasing order of scope are JARs (Java archives), WARs (web application archives), and EARs (enterprise archives). That is, an EAR can contain any number of JARs or WARs, while a WAR can contain any number of JARs.
Your service logic, i.e., IProcessor implementation and any corresponding objects should be packaged as a standard JAR file. They could technically be split up among several JAR files depending on how a service is logically divided. Each JAR file then should be packaged within an EAR archive with a standard EAR descriptor.
Our HelloService looks like this:
edgeclick-service-hello.ear
META-INF/MANIFEST.MF
META-INF/application.xml
edgeclick-service-hello.jar
com/sco/edgeclick/HelloProcessor.class
META-INF/MANIFEST.MF
The application.xml file is a standard XML-based descriptor which specifies the modules within our HelloService.ear file. Here is what it looks like:
<application>
<description>A sample EdgeClick service that sends an SMS to the active subscriber</description>
<display-name>EdgeBuilder Hello Service</display-name>
<module>
<java>edgeclick-service-hello.jar</java>
</module>
</application>
Through the application.xml file, you can define several sub-modules to deploy. The edgeclick-service-hello.jar MANIFEST.MF file within the META-INF directory is also used when you want to include optional third-party libraries that might be needed by your service. For example, let's say the HelloService needed to use the Spring Framework. It could include spring.jar like so:
edgeclick-service-hello.jar
com/sco/edgeclick/HelloProcessor.class
lib/spring.jar
META-INF/MANIFEST.MF
MANIFEST.MF:
Class-Path: lib/spring.jar
By including the line "Class-Path: lib/spring.jar" in the MANIFEST.MF file of our JAR file, we tell the deployer to add the spring.jar file to our runtime CLASSPATH. Because we have configured the ECP EAR deployer to be SCOPED, optional libraries included in our HelloService EAR file are not visible across another deployed EAR. This allows different services to use different versions of the same library without running into class loading conflicts.
Each ECP is responsible for managing any number of accounts. Every
account has a subscribers that need to login to have proper
access to services deployed on the ECP. The subscriber's account
is represented by a subdomain which in the production EdgeClick Park
website is the first substring of the fully qualified DNS name of the
ECP. For example, the SCO ECP is named http://sco.edgeclickpark.com
.
The subdomain is "sco" and there are a number of subscribers under the SCO Account. When a POST from a client hits the ECP at one of its servlet endpoints, the name/value pairs within the HTTP message body is internally converted to our Parameters structure. When this occurs, the subdomain of the URL is parsed, i.e. sco in the above case, and quereid against the incore database to deteremine what account ID to add to the Parameters structure. By the time a message reaches a service, the account ID has already been deteremined in this manner. If the subdomain can not be calculated or does not match what is registered in the ECP's internal database then subscriber's will not be able to login successfully no matter what. A valid account subdomain must be registered within the ECP.
Subscribers login via a username/password challenge mechanism the first time they access the ECP via a client. When a successful login occurs, a HTTP session_id is generated and stored in the database which makes this subscriber now active and able to send additional commands. The session_id stored is valid only for 2 weeks before the subscriber has to reauthenticate with the ECP. When a client on the subscriber's phone or web browser accesses the ECP, it will send the valid session_id that was used for authentication. Because this session_id is in our internal database, we know the subscriber has previously logined correctly and now has full access to services deployed on the ECP. Again, when we convert the POST to our internal Parameters structure the subscriber ID is added and passed to the service.
The current release of the ECP defines three primary URLs:
http://<ECP NAME>/eps-1.0/epsweb
http://<ECP NAME>/eps-1.0/epsmob
http://<ECP NAME>:8080/meinc/app
The main difference between the epsweb and epsmob servlets is the underlying IdentifyService that is used to authenticate clients who send HTTP requests to them. The epsweb URL is used by web clients such as browsers are static HTML pages. The epsmob client is used by all mobile clients that are running on a smart device. The meinc/app URL is for the deployed admin site to manage subscribers, contacts, groups, etc. registered on the ECP.
When you write clients, be aware of these three URLs and understand that mobile clients must access epsmob and web clients must use epsweb or subscribers will not be able to authenticate properly.
As has been stated, the ECP supplies a series of services and abstractions. Occasionally it necessary to know more about what is going on inside the ECP; this is especially true when debugging an otherwise difficult problem.
The ECP is deployed under a Java EE application server. The ECP gets deployed indirectly when the application server starts. The current Java EE application server the ECP uses is the JBoss AS. When JBoss starts it will deploy the ECP enterprise archives and any other services that have been installed. Services you develop will be deployed in a similar fashion as the ECP itself.
The primary logic of any Java EE application is encapsulated by Enterprise Java Beans (EJBs). EJBs are objects that follow a certain structure and are deployed under an application server's EJB container. Currently, IProcessors objects are implemented as Stateless Session beans. Stateless Session beans as well as all EJB Version 3 (EJB3) objects are defined using Java Standard Edition 5 annotations.
In services, the @Stateless annotation is used to indicate this. The immediately following @LocalBinding annotation to register a service in the Java Naming Directory Interface (JNDI) registry. All Java EE platforms include a general purpose registry for objects. The ECP core looks up services by walking over JNDI space looking for services registered under "edgeclick/services/<ServiceName>/local." The "local" part describes the service as colocated within the same JVM as the ECP itself and calls to it will be done via the standard Call-by-Reference mechanism, i.e. a standard Java direct method call.
All Services must register themselves in the "edgeclick/services/<SERVICENAME>/local" naming context for the EdgeClick Processor to find the service.
Within a service, instead of having to do nasty lookups, a service writer can inject the EJB resources they need directly via the @EJB annotation. In fact, all services that are registered within the ECP are accessible in this matter. In the future, through injection and other messaging mechanisms, services will be reusable and composable to create more complex applications.
The JAR, WAR, and EAR deployment formats, as well as some of the
deployment descriptors such as application.xml
,
all follow standard Java EE conventions.
Some EdgeClick applications services may require administration from an interface other than a mobile device. Such administration may involve defining the roles of different users within the application, defining where agents are, and a variety of other tasks. The mobile device is typically not a suitable interface for such administration because of limited visual real estate, and the possible need to import information from other files or locations.
Thus, a conventional browser-based interface is used for this administration role, implemented using Java web programming using the same development environment as discussed previously in above sections.
EdgeClick agents are the most varied of EdgeClick components in development terms, because they are the most tied to existing business application systems.
An EdgeClick agent may be written in any language that supports SOAP/XML-based web services, and currently, most commonly-used application development languages have such support. The tools to write agents will correspond to whatever language and application environment is required.
For existing applications that happen to be running on SCO Unix operating systems, SOAP and XML implementations are provided in five languages ñ Java, C, C++, Perl, and PHP -- any of which may be used to write EdgeClick agents. See the SCOx optional feature on those products.
The doc/samples
part of EdgeBuilder contains one
sample agent, for the Meter application. This agent is simple
in function but gets the idea across of how an agent may be situated
and how communication with the ECP is done.
See the Getting Started Guide for a
functional description of this sample.
EdgeClick agents run on external back-end systems and communicate with the EdgeClick Processor. Some EdgeClick applications, although they require data from back-end systems, can do without an agent running on the back-end system, because they only deal with entities already defined within the EdgeClick Enterprise Connector Component. An example is a special purpose piece of code that runs on the EdegClick Processor that uses the ITeam interface to retrieve data from an Human Resources databse running on a back-end system.
In cases like this, all the code runs on the EdgeClick Processor, and this code communicates with the back-end system database via one or more methods: a SOAP call, a remote JDBC connection, or an ftp connection, for example.
A connector sample and tutorial will be included in a future revision of this document.
© Copyright 2006 The SCO Group, Inc. All Rights Reserved.