Implementing a HelloWorld service

From Filtered Push Wiki
Jump to: navigation, search

Creating the HelloWorld service

This example has been implemented and checked in to https://filteredpush.svn.sourceforge.net/svnroot/filteredpush/FP-Hello/trunk

First create a new Java project and name it FP-Hello in Eclipse for what will become the EJB service component. If you are using maven a good base to use for the pom.xml file in the new project can be found in the Fuseki implementation of "Knowledge". This project can be found in the svn repository at https://filteredpush.svn.sourceforge.net/svnroot/filteredpush/FP-Knowledge/FP-Knowledge-Fuseki/trunk/, the pom.xml file is in the top level project directory (you could simply copy this pom over into your new project and change the name to FP-Hello-0.0.1-SNAPSHOT to reflect the new project name).

Create the remote interface for the EJB (use the @Remote java annotation):

import javax.ejb.Remote;

@Remote
public interface HelloWorldRemote {
	public void sayHello();
}

Next create an implementation class as a Stateless session bean using the @Stateless annotation:

import javax.ejb.Stateless;

@Stateless
public class HelloWorld implements HelloWorldRemote {
	public void sayHello() {
		System.out.println("Hello World!");
	}
}

Now this service can be deployed for use by the FilteredPush node.

FilteredPush Node

Once we have a service we need to do the following to the FilteredPush node implementation:

  1. Create a new FPMessage type and/or scheme
  2. Create a new concrete implementation of AbstractJob that invokes the service
  3. Register the the new job with the Triage implementation

Our current implementation of FP-Node will require the checkout of the FP-Node project and its dependencies:

svn co https://filteredpush.svn.sourceforge.net/svnroot/filteredpush/FP-Node/trunk FP-Node
svn co https://filteredpush.svn.sourceforge.net/svnroot/filteredpush/FP-API-Library/trunk FP-API-Library
svn co https://filteredpush.svn.sourceforge.net/svnroot/filteredpush/FP-Knowledge/FP-Knowledge-Fuseki/trunk FP-Knowledge-Fuseki
svn co https://filteredpush.svn.sourceforge.net/svnroot/filteredpush/FP-Messaging/FP-Messaging-SparqlPuSH/trunk FP-Messaging-SparqlPuSH
svn co https://filteredpush.svn.sourceforge.net/svnroot/filteredpush/FP-Deployments/FP-EAR/trunk FP-EAR

The project that contains most of what we need is FP-Node. FP-Knowledge-Fuseki and FP-Messaging-SparqlPuSH are two service EJB implementations for knowledge and messaging respectively. FP-API-Library contains the apis for use by FilteredPush and FP-EAR is the project that we will deploy to glassfish.

FPMessage Type

This is done in the FP-API-Library project. The classes we will need to add to can be found in package org.etaxonomy.applepie.api (the Apple Pie network instance implementation classes). First we can start with defining a new MessageType in ApplePieMessageType.

When the network receives an FPMessage from a client (see edu.harvard.mcz.fp2.message.FPMessage), triage will check the FPMessage instance's type field. The value of the ApplePieMessageType will determine which job implementation/service triage will invoke. One example is an FPMessage with a message type of REGISTER_INTEREST which when encountered by triage will result in the execution of the ApplePieRegisterInterestJob (more about this job later).

Add the following to ApplePieMessageType to define a message type of HELLO:

public static final String HELLO = "HELLO";

and add !name.equals(NOTIFICATION) to the if clause in the setName method.

FPMessage Scheme

After triage has scheduled a job and the job's execute method is invoked, the job might want to take a different action depending on the message content supplied (i.e. RDF_XML or SPARQL). The scheme field of the FPMessage instance is used to determine what the message content is and can be checked to determine what action the job should take. For example, when an FPMessage of type REGISTER_INTEREST schedules an interest registration job the interest can be expressed as either KVP or SPARQL. The ApplePieRegisterInterestJob uses the scheme of the FPMessage to determine which service to invoke given the message content (execute the query on the triple store in the case of SPARQL or parse the key value pairs and look for them in the MySQL messagestore in the case of KVP).

We can define a new scheme or use an existing one. Since we have our message type for HELLO already and there is really only one action for now (and no message content) we will chose to ommit the use of a message scheme for this job implementation.

Job Implementation

First add a dependency to the FP-Node pom.xml:

<dependency>
	<groupId>org.filteredpush</groupId>
	<artifactId>FP-Hello</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<type>ejb</type>
</dependency>

In the FP-Node project in package org.etaxonomy.applepie create a new job implementation (see ApplePieRegisterInterestJob for an example of the job for REGISTER_INTEREST):

package org.etaxonomy.applepie;

import java.util.Properties;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.etaxonomy.applepie.api.ApplePieMessageType;
import org.filteredpush.examples.HelloWorldRemote;

import edu.harvard.mcz.fp2.message.FPMessage;
import edu.harvard.mcz.fp2.triage.api.AbstractJob;
import edu.harvard.mcz.fp2.triage.api.JobExecutionException;
import edu.harvard.mcz.fp2.triage.api.NetworkConfigurationException;
import edu.harvard.mcz.fp2.triage.api.UnsupportedMessageTypeException;

public class ApplePieHelloWorldJob extends AbstractJob {
	private FPMessage message;

	@Override
	public void setMessage(FPMessage message) throws UnsupportedMessageTypeException {
		if (!message.getType().getName().equals(ApplePieMessageType.HELLO)) {
			throw new UnsupportedMessageTypeException("ApplePieHelloWorldJob given message that isn't a hello message.");
		}

		this.message = message;
	}

	public void execute() throws JobExecutionException {
		if (message == null) {
			throw new JobExecutionException("ApplePieHelloWorldJob has null message. This job must be associated with an FPMessage prior to execution.");
		}
		
		Properties environment = new Properties();

		environment.put("java.naming.factory.initial",
				"com.sun.enterprise.naming.impl.SerialInitContextFactory");
		environment.put("java.naming.factory.url.pkgs",
				"com.sun.enterprise.naming");
		environment.put("java.naming.factory.state",
				"com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
		
		environment.put("org.omg.CORBA.ORBInitialHost", "localhost");
		environment.put("org.omg.CORBA.ORBInitialPort", "3700");
		
		try {
			Context initialCtx = new InitialContext(environment);
			
			// Here we obtain the ejb via jndi and invoke the sayHello method
			HelloWorldRemote hello = (HelloWorldRemote) initialCtx.lookup("java:global/FP-EAR-0.0.1-SNAPSHOT/FP-Hello-0.0.1-SNAPSHOT/HelloWorld");
			hello.sayHello();
			
		} catch (NamingException e) {
			throw new NetworkConfigurationException(e);
		}
	}
}


Triage JobPlanner

Now that we have a job implementation that invokes our service we can tie the message type we defined earlier to this job via the Triage job planner implementation found in org.etaxonomy.applepie.StaticApplePieJobPlanner. Add a new instance of the job to the map of prototypes in the constructor (the job planner will clone a job given it's message type, eventually new messagetype/job pairings can be defined at runtime):

prototypes.put(ApplePieMessageType.HELLO, new ApplePieHelloWorldJob());


Deployment

If re-deploying via the FP-EAR project, edit the pom so that maven will package the ejb inside the ear for deployment. Add the following dependency:

<dependency>
	<groupId>org.filteredpush</groupId>
	<artifactId>FP-Hello</artifactId>
	<type>ejb</type>
	<version>0.0.1-SNAPSHOT</version>
</dependency>

Now update your maven project configuration, rebuild the projects in the following order (via mvn install) and deploy FP-EAR-0.0.1-SNAPSHOT.ear to glassfish.: FP-API-Library, FP-Hello, FP-Node, FP-EAR

Using the Service

And example of how the new service would be invoked via the AccessPoint from Java is presented below and can be found in the FP-ClientHelper project in the sourceforge svn repository hosted at: https://filteredpush.svn.sourceforge.net/svnroot/filteredpush/FP-ClientHelper/trunk

package org.filteredpush.examples;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.UUID;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

import org.etaxonomy.applepie.api.ApplePieMessageType;

import edu.harvard.mcz.fp2.identity.ClientIdentity;
import edu.harvard.mcz.fp2.webservice.client.generated.FPNetworkAccessPointWebServiceService;

public class TestHelloWorld {
	public static void main(String[] args) throws DatatypeConfigurationException, MalformedURLException {
		URL wsdlLocation = new URL("http://localhost:8088/FPNetworkAccessPointWebServiceService/FPNetworkAccessPointWebService?wsdl");
		FPNetworkAccessPointWebServiceService service = new FPNetworkAccessPointWebServiceService(wsdlLocation);

		// Construct FPMessage
		UUID originator = UUID.randomUUID();
		ClientIdentity origin = new ClientIdentity();
		String uuid = UUID.randomUUID().toString();

		GregorianCalendar cal = new GregorianCalendar();
		cal.setTime(new Date());
		XMLGregorianCalendar date = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);

		String result = service.getFPNetworkAccessPointWebServicePort().acceptMessage(
				ApplePieMessageType.HELLO, uuid, date,
				"", null, originator.toString(),
				origin);

		System.out.println("Response: " + result);
	}
}