Skip to content.

The E-Learning Framework

Sections
Personal tools
You are here: Home » ELF Project Directory » Web Services for Reflective Learning (WS4RL) » How to use WS4RL SDKs - a brief guide
Views
  • State: published

How to use WS4RL SDKs - a brief guide

This is a web page describing how to use the PWPWS and PIADS SDKs to make the deplyoment of PDP web services easy.

Click here to get the file

Size 20.0 kB - File type text/html

File contents

<html>

<body>



<h1>Using the PDP-WS SDK toolkit to create a minimal Personal Development Planning

web service</h1>

<em>Written by Adrian Cuthbertson and Adam Marshall (The University of Oxford) adapted from Enterprise SDK guide by Scott Wilson, CETIS.

 October 05, 2004

</em>

<!-- p>

The <a href="http://www.imsglobal.org/">IMS Web Services 

</a>specification may be a pretty complex spec with reams of documentation, 

but with the PDP-WS SDK for Java the implementation process is 

drastically simplified.

</p-->

<p>

This 'how to guide' shows you how to write and deploy a minimal 

Personal development Planning (PDP) web service implementation! The service

we create correctly handles all operations it receives by responding with the

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

</p>

<p>Note that we have made the decison to use the <a href="www.imsglobal.org/af/IAFReviewv1.pdf">IMS Abstract Framework</a> 

	as the basis for our web services. This seemed sensible as we do not want to invent our own 

	infrastructure when there is a perfectly good one out there which will be maintained and improved

	independently. We also figure that, in the future, 

	developers will be familliar with this framework which will mean minimal development time.</p>

<p>



To follow the examples, you will need a Java application server,  such as JRun,

JBoss, <a href="http://jakarta.apache.org/tomcat/index.html">Tomcat</a> or similar, with <a href="http://ws.apache.org/axis/">AXIS 1.1</a> installed to provide Web Services support.

A basic knowledge of creating and deploying Java web applications is also

required.

</p>

<h2>Step 1: Get the SDK libraries</h2>

<p>

The first thing we need to

do is get hold of the SDKs. You can download this from 

<a href="https://sourceforge.net/projects/pdpws/">https://sourceforge.net/projects/pdpws/</a>.<br />

The SDK consists of some documentation of the code, plus libraries and examples for two web services,

PDPWS and PIADS. The framework and example jars are:

<ul type="disc">



	<li>pdpws-framework.jar</li>

	<li>pdpws-example.jar</li>

	<li>pdpws-example2.jar</li>

	<li>piads-framework.jar</li>

	<li>piads-example.jar</li>

	<li>piads-example2.jar</li>



</ul>

The lib directory also contains all the axis 1.1 jars and the build files required to build

and deploy the examples. The frameworks contain all the code we need to start working with, including all

the data types used in both the PDP and PIADS web services, all the web service "stubs" and "skeletons",

and so on. They also contain a very important component called the <em>Bridge</em>.

</p>

<p>

The Bridge is a component for connecting a Web Service endpoint to

your own implementation. The parts of the Bridge are:

	

<ul type="disc">

<li>DataBridge interface</li>

<li>DataBridgeFactory</li>

<li>FactoryFinder</li>

<li>ConfigurationException</li>

</ul>



Basically, when the Web Service is called, 

it needs to find a <i>DataBridge</i>to tell it where to find the classes we're

going to write that actually implement the PDP-WS / PIADS WS calls; it does this by getting

the <i>FactoryFinder</i> to look in a special configuration file to see what

we've called our own implementation of a <i>DataBridgeFactory</i>. It then

locates this class, and asks it for an instance of our <i>DataBridge</i>

implementation. Finally, it interrogates the <i>DataBridge</i> implementation

to return a handle on our service implementation. This procedure is the same for both services.

</p>



<p>

This may sound a bit complicated, but in practice it is actually very simple

to use. So lets write our code! For purposes of this userguide we will use the

PDPWS web service as an example.

</p>



<h2>Step 2: Write your implementation classes</h2>



<p>Please note that the actual classes described here and supporting ant build

files are contained in the examples directory in the sdk zip.

</p>

<p><em>Important - also read the examples-readme.txt for instructions on how 

to build and deploy the examples. You can easily adapt the ant build.xml to do your own

build and deploy as well.</em>

</p>

<p>

We need to create 2 bridge classes to connect the service to each

implementation:

	

<ul type="disc">

	<li>DataBridgeImpl</li>



	<li>DataBridgeFactoryImpl</li>

</ul>



For PDPWS we also need to create our actual implementation, this will be for the

PDPServiceSync service, so we also need to write the class:

	

		<ul type="disc">

			<li>PDPServiceSyncImpl</li>

		</ul>

		

These 3 classes represent the sum total of java code we need to create for 

this implementing the PDPWS. </p> 

	

		<p>Implementing the PIADS WS would involve a very similar process, 

			however, we would then be implementing PIADSServiceSync.</p>



<h3>DataBridgeFactoryImpl</h3>



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



<blockquote>

<pre>

/*

 *     DataBridgeFactoryImpl.java

 */



package example.pdp.server;

import uk.ac.ox.oucs.bridge.DataBridge;

import uk.ac.ox.oucs.bridge.DataBridgeFactory;

import example.pdp.server.DataBridgeImpl;



public class DataBridgeFactoryImpl extends

             uk.ac.ox.oucs.bridge.DataBridgeFactory{



/*

 *     Creates a new instance of DataBridgeFactoryImpl

 */ 



  public DataBridgeFactoryImpl() { } 



  public DataBridge newDataBridge() {

    return new DataBridgeImpl();

  }

}

</pre>

</blockquote>

</p>



<p>The PIADS equivalent would look very similar, but would refer to piads

instead of pdp.</p>



<h3>DataBridgeImpl</h3>



<p>The DataBridgeImpl class needs to realise the appropriate <i>DataBridge</i> interface

from the SDK; this consists of a call to return an instance of the implementation

of the approriate service (either, PDPServiceSync or PIADSServiceSync).  For 

requests for a new PDPServiceSync, we return a new instance

of our <i>PDPServiceSyncImpl</i> class.</p>



<blockquote>

<pre>

/* 

 *     DataBridgeImpl.java

 */

package example.pdp.server;

import example.pdp.server.PDPServiceSyncImpl;

import uk.ac.ox.oucs.bridge.ConfigurationException;

import uk.ac.ox.oucs.bridge.DataBridge;

import uk.ac.ox.oucs.pdp.ports.PDPServiceSync;





public class DataBridgeImpl implements DataBridge {



 /*

  *     Creates a new instance of DataBridgeImpl

  */

  public DataBridgeImpl(){  }



 /*

  * Returns our implementation of PDPServiceSyncImpl

  */

  public Object getServiceManager() throws Exception {

    return new PDPServiceSyncImpl();

  }

}

</pre>

</blockquote>

	<p>  Requests for a new PDPServiceSync, would return a 

	new instance of our <i>PDPServiceSyncImpl</i> class. This means that the

	Web Service when it is called, actually calls our implementation! Neat huh?</p>





<h3>PDPServiceSyncImpl</h3>



<p>

This is where it all happens for the PDP web service. This is the class which 

actually implements the operations specified in the PDP-WS PDPServiceSync

interface. There's a bit more to this class, so we'll take it in stages.

</p>



<p>

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:

<blockquote>

<pre>

/*

 *     PDPServiceSyncImpl

 */

package example.pdp.server;



import uk.ac.ox.oucs.pdp.ports.PDPServiceSync;

import uk.ac.ox.oucs.pdp.messages.*;

import uk.ac.ox.oucs.pdp.messages.holders.*;

import uk.ac.ox.oucs.pdp.ports.PDPServiceSync;

import uk.ac.ox.oucs.iaf.messages._syncRequestHeaderInfo;

import uk.ac.ox.oucs.iaf.messages._syncResponseHeaderInfo;

import uk.ac.ox.oucs.iaf.messages._codeMajor;

import uk.ac.ox.oucs.iaf.messages._severity;

import uk.ac.ox.oucs.iaf.messages._operationIdRef;

import uk.ac.ox.oucs.iaf.messages.StatusInfoDType;

import uk.ac.ox.oucs.iaf.messages.holders._syncResponseHeaderInfoHolder;



</pre>

</blockquote>



<p>

This class needs to realize the interface <i>PDPServiceSync</i>, which is defined in

the package <i>uk.ac.ox.oucs.pdp.ports</i> within the SDK libraries.

This interface specifies all the operations that are defined by the WSDL specification,

so we need to provide an implementation method for each of these.

</p>

<p>

So, our definition needs to reference the interface we're implementing, and contain

our default constructor.

<p>

For each of the methods defined in <i>PDPServiceSync</i> we need to provide an implementation. 

The minimal sort of implementation  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 for

the createLearnerByProxy method:

</p>



<blockquote>

<pre>

public class PDPServiceSyncImpl implements PDPServiceSync {



  public void createLearnerByProxy(

       uk.ac.ox.oucs.pdp.messages._createLearnerByProxyRequest parameters, 

       uk.ac.ox.oucs.iaf.messages._syncRequestHeaderInfo headerInfoRequest, 

       uk.ac.ox.oucs.pdp.messages.holders._createLearnerByProxyResponseHolder response,

       uk.ac.ox.oucs.iaf.messages.holders._syncResponseHeaderInfoHolder headerInfoResponse) 

                  throws java.rmi.RemoteException {

		StatusInfoDType status = new StatusInfoDType();

		status.setCodeMajor(_codeMajor.unsupported);

		status.setSeverity(_severity.warning);

		status.setOperationIdRef( new String[] { "createLearnerByProxy" } );

		response.value = new uk.ac.ox.oucs.pdp.messages._createLearnerByProxyResponse();

		_syncResponseHeaderInfo headerInfo = new _syncResponseHeaderInfo();

		headerInfo.setStatusInfo(status);

		headerInfoResponse.value = headerInfo;

  }

		

  public void createLearner(... ) 

}

</pre>

</blockquote>

<p>

Taking this one step at a time:



<ol>

	<li>the code creates a <i>status</i> variable of the StatusInfoDType type

	defined by IMS in the Abstract Framework WSDL;</li> 

    <li>this is populated by setting the CodeMajor elemet to "unsupported";</li>

	<li>the severity is then set to "warning";</li>



	<li>the OperationIdRef should then be set to to the message (method) name 

"createLearnerByProxy";</li>

	<li>the response is set up by creating a message of the appropriate type;</li>

	<li>a container for the status message is created (headerInfo);</li>

	<li>and the status assigned to it;</li>

	<li>finally the status container is assigned to the appropriate response parameter (headerInfoResponse).</li>



</ol>



 The above basically returns an unsupported message in a standard 

fashion!

</p>





<p>

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. 

</p>

<p>

In order to fill out the service the above should be  repeated for every operation defined in <em>PDPServiceSync</em>. 

(Rather than us showing all this repetitive code here, you can 

	<a href="http://sourceforge.net/projects/pdpws/">download a full implementation from SourceForge</a>

	 as part of the sdk.)

</p>



<p>

Once we're done, that's our code written. You just need to ensure that the SDK libraries are

in your classpath, and compile away. Use the ant build supplied in the sdk as a basis

for creating your build. The build file is well documented and there is also an examples-readme.txt

file which describes the details. You can use the build to do everything required; compile, jar,

deploy your web app to tomcat (or similar web server), deploy your wsdd (web service definitions)

and run a test client application.

</p>



<p>Exactly the same process as above is used for the PIADS web service; clearly the imports 

	are different and the methods being implemented are the ones from the PIADS service, 

	but otherwise the principle is identical.</p>

	

<h2>Step 3: Package up your implementation</h2>

<p>

To deploy the services we need to package up with all the relevant metadata for our application server(s).

You should use the ant build.xml files described above to do this, but we describe the

basics here so that you can see what's required.

</p>

<p>

Firstly we need to make a .jar file that contains all our compiled classes, plus a META-INF

directory.

</p>

<p>

You may remember us mentioning earlier that a special configuration file is needed by the 

SDKs to locate your <i>DataBridgeFactoryImpl</i>. This needs to be called 

	<b>uk.ac.ox.oucs.bridge.DataBridgeFactory</b> (no extension) 

		and placed inside a folder called <strong>pdpws</strong> within the META-INF part 

		of the deployment jar file. 



<br/><br/>See the <b>example/pdp/server/META-INF/pdpws/uk.ac.ox.oucs.bridge.DataBridgeFactory</b>

		file for an example.

</p>

<p>

The file itself should simply contain the line:

</p>

<blockquote>

<pre>example.pdp.server.DataBridgeFactoryImpl</pre>

</blockquote>

<p>

Which is the name of our DataBridgeFactory class.

<p>

Once we have this in the .jar file, we can create our deployment.

</p>

<p>



Note that the configuration file for a piads service would need to be in the META-INF/piads

directory and would contain the package name of your piads bridge factory implementstion,

e.g. example.piads.server.DataBridgeFactoryImpl

</p>



<h2>Step 4: Create the deployment </h2>

<p>

To deploy the service you can either use the ant build.xml described above, or create

your own deployment war as follows;

</p>

<p>

We need to create a deployment directory on the 

application server, which looks like this:

</p>

<blockquote>

<pre>

mypdpws-war/

 WEB-INF/

  web.xml

  lib/

    pdpws-example.jar

    pdpws-framework.jar

    piads-example.jar

    piads-framework.jar

    axis.jar

    axis-ant.jar

    commons-discovery.jar

    commons-logging.jar

    jaxrpc.jar

    log4j-1.2.8.jar

    saaj.jar

    wsdl4j.jar

</pre>

</blockquote>



<p>

The <b>web.xml</b> file is a general web application configuration file. Specific application

servers have their own configuration files, if you use JRun you should 

also have a file called <b>jrun-web.xml</b>. The <b>web.xml</b> file basically contains the

name and description for the web application, and can be based on the templates

supplied by your application server. The format of file is well documented, and 

again, any book about servlets will explain.

</p>

<p>

The lib directory contains the sdk jars (pdpws-framework.jar, piads-framework.jar),

the example/s you've built (piads-example.jar, pdppdpws-example.jar) and the jars

from the axis distribution.

</p>

<p>

It is also necessary to run the axis deploy process, to notify axis about your web service

and to configure the serice. This is done using the axis AdminService facility and a deploy

WSDD file for the web service The <b>deploy.wsdd</b> files for pdpws and piads are contained in

the <b>wsdd/pdpws</b> and <b>wsdd/piads</b> directories in the sdk. You can also use the ant build.xml

to run the AdminService and deploy the files.



</p>

<p>

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

</p>



<h2>Step 4: Test the service</h2>



<p>

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 our deployment the location

of this is <i>http://localhost:8080/pdpws/services/PDPServiceSyncSoap</i>.



</p>

<p>

Entering this URL in a browser returns this cheerful message:

<blockquote><pre>

Hi there, this is an Axis service.



</pre></blockquote>



</p>

<p>

(If you don't get this message, then something has gone wrong with your deployment

check your error logs and also check the ant build to make sure you've done

everything and try again.)

</p>

<p>

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 <b>sniffer</b> 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.

</p>

<p>

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

</p>

<p>

<blockquote>



<pre>

POST /pdpws/services/PDPServiceSyncSoap HTTP/1.0

Host: 127.0.0.1

Content-Type: text/xml; charset=utf-8

SOAPAction: &quot;http://www.imsglobal.org/soap/pdp/createLearnerByProxy&quot;



&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;

&lt;SOAP-ENV:Envelope xmlns:SOAP-ENV=&quot;http://schemas.xmlsoap.org/soap/envelope/&quot;&gt;



&nbsp;&lt;SOAP-ENV:Header&gt;

&nbsp;&nbsp;&lt;h:syncRequestHeaderInfo xmlns:h=&quot;http://www.imsglobal.org/specs/imsMessBindSchemav1p0.xsd&quot;&gt;

&nbsp;&nbsp;&nbsp;&lt;h:messageIdentifier&gt;AB12345e4t6789&lt;/h:messageIdentifier&gt;

&nbsp;&nbsp;&lt;/h:syncRequestHeaderInfo&gt;

&nbsp;&lt;/SOAP-ENV:Header&gt;

&nbsp;&lt;SOAP-ENV:Body&gt;  

&nbsp;&nbsp;&lt;m:createLearnerByProxy xmlns:m=&quot;http://www.imsglobal.org/schemas/PDPMessSchemav1p0.xsd&quot;&gt;



&nbsp;&nbsp;&nbsp;&lt;m:sourcedId&gt;source:id&lt;/m:sourcedId&gt;

&nbsp;&nbsp;&lt;/m:createLearnerByProxy&gt;

&nbsp;&lt;/SOAP-ENV:Body&gt;

&lt;/SOAP-ENV:Envelope&gt;

</pre></blockquote>

<p>

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

</p>

<blockquote>

<pre>

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





&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; 

&nbsp;&nbsp;&lt;soapenv:Envelope xmlns:soapenv=&quot;http://schemas.xmlsoap.org/soap/envelope/&quot; xmlns:xsd=&quot;http://www.w3.org/2001/XMLSchema&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;&gt;

&nbsp;&nbsp;&nbsp;&nbsp;&lt;soapenv:Header&gt;



&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ns1:syncResponseHeaderInfo xmlns:ns1=&quot;http://www.imsglobal.org/specs/imsMessBindSchemav1p0.xsd&quot;&gt;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ns1:messageIdentifier&gt;PDPWS:FD56423253:0&lt;/ns1:messageIdentifier&gt;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ns1:statusInfo&gt;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ns1:codeMajor&gt;unsupported&lt;/ns1:codeMajor&gt;  

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ns1:severity&gt;warning&lt;/ns1:severity&gt;



&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ns1:messageIdRef xsi:nil=&quot;true&quot;/&gt;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ns1:operationIdRef&gt;createLearnerByProxy&lt;/ns1:operationIdRef&gt;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ns1:description&gt;  

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ns2:language xmlns:ns2=&quot;http://www.imsglobal.org/specs/imsCommonSchemav1p0.xsd&quot;&gt;en-US&lt;/ns2:language&gt;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ns3:text     xmlns:ns3=&quot;http://www.imsglobal.org/specs/imsCommonSchemav1p0.xsd&quot;&gt;The service requested is not supported by the target system&lt;/ns3:text&gt;



&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/ns1:description&gt;  

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/ns1:statusInfo&gt;  

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ns1:statusInfoSet xsi:nil=&quot;true&quot;/&gt;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/ns1:syncResponseHeaderInfo&gt;

&nbsp;&nbsp;&nbsp;&nbsp;&lt;/soapenv:Header&gt;

&nbsp;&nbsp;&nbsp;&nbsp;&lt;soapenv:Body&gt;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;createLearnerByProxyResponse xmlns=&quot;http://www.imsglobal.org/specs/PDPMessSchemav1p0.xsd&quot;/&gt;



&nbsp;&nbsp;&lt;/soapenv:Body&gt;

 &lt;/soapenv:Envelope&gt;

</pre></blockquote>



</p>

Which is exactly what we wanted - our implementation doesn't support a createLearnerByProxy 

request, and so it returns the "unsupported" status block as required by the 

IMS specification.

</p>

<p>

Of course, if we actually wanted to really implement PDP-WS, we would make our 

PDPServiceSyncImpl methods do something rather more than just return the 

unsupported code, instead we could have added some JDBC methods to connect 

to a database.

</p>



</body>

</html>





Created by adamm
Last modified 2004-11-17 11:14 AM