skip to main page content CETIS: Click here to return to the homepage
the centre for educational technology interoperability standards

skip over the long navigation bar
Home
News
Features
Events
Forums
Reference
Briefings
Press centre

Inside Cetis
what is Cetis?
Contact us
Cetis staff
Jobs at CETIS


 




Syndication
XML: Click here to get the news as an RSS XML file XML: Click here to get the news as an Atom XML file iCAL: Click here to get the events as an iCalendar file

Background
what are learning technology standards?
who's involved?
who's doing what?

CETIS Groups
what are cetis groups?
what difference can they make?
Assessment SIG
Educational Content SIG
Enterprise SIG
Metadata SIG
Life Long Learning Group
Portfolio SIG
Accessibility Group
Pedagogy Forum
Developer's forum

Subjects
Accessibility (310)
Assessment (74)
Content (283)
Metadata (195)
Pedagogy (34)
Profile (138)
Tools (197)
For Developers (569)
For Educators (344)
For Managers (339)
For Members (584)
SCORM (118)
AICC (18)
CEN (34)
DCMI (36)
EML (47)
IEEE (79)
IMS (302)
ISO (21)
OAI (24)
OKI (20)
PROMETEUS (12)
W3C (37)

print this article (opens in new window) view printer-friendly version (opens in new window)

Using the Enterprise SDK to create a minimal IMS Enterprise web service


The IMS Enterprise Web Services specification may be a pretty complex spec with reams of documentation, but with the IMS Enterprise SDK for Java the implementation process is drastically simplified.

This article shows you how to write and deploy a minimal Enterprise Group Management service - and by minimal we mean minimal! The service we create correctly handles all operations it recieves by responding with the appropriat "unsupported" status code defined by IMS, which although rather useless in practice, shows how you would go about building a real service using the SDK as a starting point.

To follow the examples, you will need a Java application server, such as JRun, JBoss, Tomcat or similar, with AXIS 1.1 installed to provide Web Services support. A basic knowledge of creating and deploying Java web applications is also required.

Step 1: Get the SDK libraries

The first thing we need to do is get hold of the SDK. You can download this from https://sourceforge.net/projects/cetis-es/.

The SDK consists of some documentation of the code, plus two libraries:
  • cetis-ws.jar
  • cetis-ws-client.jar
These two libraries contain all the code we need to start working with, including all the data types used in Enterprise, all the web service “stubs” and “skeletons”, and so on. They also contain a very important framework called the Bridge.

The Bridge is a framework for connecting an Enterprise Web Service endpoint to your own implementation, such as a student records system or directory service. The parts of the Bridge framework are:
  • DataBridge interface
  • DataBridgeFactory
  • FactoryFinder
  • ConfigurationException
Basically, when the Web Service is called, it needs to find a DataBridge to tell it where to find the classes we’re going to write that actually implement the Enterprise calls; it does this by getting the FactoryFinder to look in a special configuration file to see what we’ve called our own implementation of a DataBridgeFactory. It then locates this class, and asks it for an instance of our DataBridge implementation. Finally, it interrogates the DataBridge implementation to return a handle on our service implementation.

This may sound a bit complicated, but in practice it is actually very simple to use. So lets write our code!

Step 2: Write your implementation classes


We need to create 2 classes to connect the service to the implementation:
  • DataBridgeImpl
  • DataBridgeFactoryImpl
We also need to create our actual implementation, which will be for the GroupManagement service, so we also need to write:
  • GroupManagementImpl
These 3 classes represent the sum total of java code we need to create for this project.

DataBridgeFactoryImpl

The sole purpose of this class is to return an instance of our DataBridgeImpl class, and so all it contains are its constructor, and a method that returns a new DataBridgeImpl. Note that this class extends the DataBridgeFactory class provided in the SDK.
/*
 * DataBridgeFactoryImpl.java
 */

package scott.bridge;
import uk.ac.cetis.enterprise.bridge.DataBridge;
import scott.bridge.DataBridgeImpl;

public class DataBridgeFactoryImpl extends uk.ac.cetis.enterprise.bridge.DataBridgeFactory{

/*
 * Creates a new instance of DataBridgeFactoryImpl
 */

  public DataBridgeFactoryImpl() {
  }

  public uk.ac.cetis.enterprise.bridge.DataBridge newDataBridge() {
    return new DataBridgeImpl();
  }
}


DataBridgeImpl


The DataBridgeImpl class needs to realize the DataBridge interface from the SDK, which consists of calls to return instances of implementations of each of the Enterprise services (Person, Group, Membership). As we are only interested in Group for this project, the other request are going to return Null for now. For requests for a new GroupManager, we return a new instance of our GroupManagementServiceImpl class.


/*
 * DataBridgeImpl.java
 */

package scott.bridge;
import scott.bridge.GroupManagementImpl;
import uk.ac.cetis.enterprise.bridge.ConfigurationException;
import uk.ac.cetis.enterprise.bridge.DataBridge;
import uk.ac.cetis.enterprise.group.ports.GroupManagementServiceSync;
import uk.ac.cetis.enterprise.membership.ports.MembershipManagementServiceSync;
import uk.ac.cetis.enterprise.person.ports.PersonManagementServiceSync;

public class DataBridgeImpl implements DataBridge {
 /*
  * Creates a new instance of DataBridgeImpl
  */

  public DataBridgeImpl(){
  }

  public uk.ac.cetis.enterprise.group.ports.GroupManagementServiceSync newGroupManager() throws Exception {
    return lang=EN-US>new GroupManagementImpl();
  }

  public uk.ac.cetis.enterprise.membership.ports.MembershipManagementServiceSync newMembershipManager() throws Exception {
    return null;
  }

  public uk.ac.cetis.enterprise.person.ports.PersonManagementServiceSync newPersonManager() throws Exception {
    return null;
  }
}

GroupManagementImpl


This is where it all happens – the class which actually implements the operations specified in the IMS Enterprise GroupManagement interface. There’s a bit more to this class, so we’ll take it in stages.

First off we’ve got a lot of importing to do. We need our DataBridgeImpl class, plus a while load of classes from the SDK:
/*
 * GroupManagementImpl
 */
package scott.bridge;
import java.util.Collection;
import java.util.Iterator;
import scott.bridge.DataBridgeImpl;
import uk.ac.cetis.enterprise.group.messages.*;
import uk.ac.cetis.enterprise.group.messages.holders.*;
import uk.ac.cetis.enterprise.group.ports.GroupManagementServiceSync;
import uk.ac.cetis.enterprise.group.types.GroupDType;
import uk.ac.cetis.enterprise.group.types.RelationshipDType;
import uk.ac.cetis.enterprise.iaf.HeaderInfoHelper;
import uk.ac.cetis.enterprise.iaf.StatusInfoHelper;
import uk.ac.cetis.enterprise.iaf.UniqueIdGenerator;
import uk.ac.cetis.enterprise.iaf.messages._syncRequestHeaderInfo;
import uk.ac.cetis.enterprise.iaf.messages._syncResponseHeaderInfo;
import uk.ac.cetis.enterprise.iaf.messages.StatusInfoDType;
import uk.ac.cetis.enterprise.iaf.messages.holders._syncResponseHeaderInfoHolder;
import uk.ac.cetis.enterprise.iaf.types.IdentifierDType;


This class needs to realize the interface GroupManagementServiceSync, which is defined in the package uk.ac.cetis.enterprise.group.http package within the SDK libraries. This interface specifies all the operations that are defined by the IMS specification, so we need to provide an implementation method for each of these.

So, our definition needs to reference the interface we’re implementing, and our default constructor:

public class GroupManagementImpl implements GroupManagementServiceSync {
  public GroupManagementImpl(){
};


Now, for each of the methods defined in GroupManagementServiceSync we need to provide an implementation. The minimal sort of implementation that IMS allows is one that returns a status code of “Not implemented”, so we need to know how to write one of those. Basically, it looks like this:

public void createGroup(
    uk.ac.cetis.enterprise.group.messages._createGroupRequest parameters,
    _syncRequestHeaderInfo headerInfoRequest,
    uk.ac.cetis.enterprise.group.messages.holders._createGroupResponseHolder response,
    _syncResponseHeaderInfoHolder headerInfoResponse){

  // initialise response
  StatusInfoDType status;
  status = uk.ac.cetis.enterprise.iaf.StatusInfoHelper.getStatusInfo( "x0003" );
  status.setOperationIdRef( new String[] { "createGroup" } );
  response.value = new uk.ac.cetis.enterprise.group.messages._createGroupResponse();
  _syncResponseHeaderInfo headerInfo = HeaderInfoHelper.newResponseHeaderInfo();
  headerInfo.setStatusInfo( status );
  headerInfoResponse.value = headerInfo;
}


Taking this one step at a time, the code creates a status variable which is of the StatusInfo type defined by IMS, which it then populates by asking the StatusInfoHelper class in the SDK to create a status code of “x0003”, which is the identifier for the “not supported” status message.

Next, we identify the operation that the StatusInfo is a response to (in this example, it’s the “createGroup” operation).

Finally, we create a Response message, and insert into it our status into the header.

The actual mechanism of listening for requests and sending responses is done by the SDK and AXIS, so we don’t need anything more to implement an operation. Simple, eh?

Now for the boring bit – repeat for every operation defined in GroupManagementServiceSync. (Rather than me showing all this repetitive code here, you can download the sources for this project.)

Once we’re done, that’s our code written. You just need to ensure that both of the SDK libraries are in your classpath, and compile away.

Step 3: Package up your implementation


To deploy the service we need to package it up with all the relevant metadata for our application server. For this we first need to make a .jar file that contains all our compiled classes, plus a META-INF directory.

You may remember me mentioning earlier that a special configuration file is needed by the SDK to locate your DataBridgeFactoryImpl. This needs to be called uk.ac.cetis.enterprise.bridge.DataBridgeFactory (no extension) and placed inside a folder called services within the META-INF part of the deployment jar file.

The file itself should simply contain the line:
scott.bridge.DataBridgeFactoryImpl

Which is the name of our DataBridgeFactory class.

Once we have these in a .jar file (which I called “bridge.jar”), we can create our deployment.

Step 4: Create the deployment


To deploy the service, we need to create a deployment directory on the application server, which looks like this:

enterprise-war/
    WEB-INF/
        server-config.wsdd
        web.xml
        lib/
            bridge.jar
            cetis-ws-client.jar
            cetis-ws.jar


There are two configuration files here, server-config.wsdd is used to tell AXIS what to expose as web services, and what classes in the SDK they relate to. The other, web.xml, is a general web application configuration file. Specific application servers have their own configuration files, in my case as I’m using JRun I also have a file called jrun-web.xml.

An example server-config.wsdd file exists in the CVS repository on Sourceforge where the SDK lives, but for convenience I’ve included a copy in this example deployment set (168k). Don’t worry too much what goes in here, its largely automatically generated, and will be included in future distributions of the SDK.<

The web.xml file basically contains the name and description for the web application, and can be based on the templates supplied by your application server.

To make the deployment run smoothly, ensure that the lib directory is in the classpath for your server. In JRun you can do this by going to Settings > JVM Settings in the management console, and adding the path to the lib folder there – in my deployment this looks like:



Deploy your application, and (fingers crossed) away it goes!

Step 4: Test the service


OK, so how do we know this is doing anything? Well, one useful thing to do is simply ask the server if there is an AXIS web service present. You can do this by typing in the URL of the service we deployed. In my deployment the location of this is http://localhost:8100/enterprise-war/services/GroupManagementServiceSyncSoap.

Entering this URL in my browser returns this cheerful message:


(If you don’t get this message, then something has gone wrong with your deployment – check your error logs and try again.)

So far, so good. But it would be nice to actually send some SOAP messages and see what happens. One way of doing this is to run the application sniffer that comes with JRun. Sniffer is a handy little TCP/IP monitoring application that we can also use to send test messages to our service.

Using Sniffer (or something similar), send your service this message:

POST /enterprise-war/services/GroupManagementServiceSyncSoap HTTP/1.0
Host: 127.0.0.1
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://www.imsglobal.org/soap/gms/readGroup"

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Header>
      <h:syncRequestHeaderInfo xmlns:h="http://www.imsglobal.org/specs/imsMessBindSchemav1p0.xsd">
      <h:messageIdentifier>AB12345e4t6789</h:messageIdentifier>
    </h:syncRequestHeaderInfo>
    </SOAP-ENV:Header>
  <SOAP-ENV:Body>
    <m:readGroup xmlns:m="http://www.imsglobal.org/schemas/imsGroupManMessSchemav1p0.xsd">
      <m:sourcedId>source:id</m:sourcedId>
    </m:readGroup>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>


With any luck, the response from the server will be something like:

HTTP/1.0 200 OK
Date: Thu, 24 Jun 2004 10:45:04 GMT
Content-Type: text/xml; charset=utf-8
Server: JRun Web Server

<?xml version="1.0" encoding="UTF-8"?>
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Header>
      <ns1:syncResponseHeaderInfo xmlns:ns1="http://www.imsglobal.org/specs/imsMessBindSchemav1p0.xsd">
      <ns1:messageIdentifier>CETIS:FD56423253:0</ns1:messageIdentifier>
      <ns1:statusInfo>
        <ns1:codeMajor>unsupported</ns1:codeMajor>
        <ns1:severity>warning</ns1:severity>
        <ns1:messageIdRef xsi:nil="true"/>
        <ns1:operationIdRef>readGroup</ns1:operationIdRef>
        <ns1:description>
            <ns2:language xmlns:ns2="http://www.imsglobal.org/specs/imsCommonSchemav1p0.xsd">en-US</ns2:language>
            <ns3:text xmlns:ns3="http://www.imsglobal.org/specs/imsCommonSchemav1p0.xsd">The service requested is not supported by the target system</ns3:text>
        </ns1:description>
      </ns1:statusInfo>
      <ns1:statusInfoSet xsi:nil="true"/>
      </ns1:syncResponseHeaderInfo>
    </soapenv:Header>
    <soapenv:Body>
      <readGroupResponse xmlns="http://www.imsglobal.org/specs/imsGroupManMessSchemav1p0.xsd"/>
  </soapenv:Body>
</soapenv:Envelope>


Which is exactly what we wanted – our implementation doesn’t support a readGroup request, and so it returns the “unsupported” status block as required by the IMS specification.

Of course, if we actually wanted to really implement Enterprise, we would make our GroupManagementImpl methods do something rather more than just return the unsupported code, instead we could have added some JDBC methods to connect to a database, for example.

The rather more complete demo available from the CVS repository on the SDK website shows how to implement the DataBridge using J2EE session and entity beans, which should be easy enough to understand if you’ve got this example working!

Download the example source code


Download the example deployment package

Related items:

Comments:

No responses have been posted

copyright cetis.ac.uk
Creative Commons License This work is licensed under a Creative Commons License.

syndication |publisher's statement |contact us |privacy policy

 go to start of page content