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.
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: "http://www.imsglobal.org/soap/pdp/createLearnerByProxy"
<?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:createLearnerByProxy xmlns:m="http://www.imsglobal.org/schemas/PDPMessSchemav1p0.xsd">
<m:sourcedId>source:id</m:sourcedId>
</m:createLearnerByProxy>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
</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
<?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>PDPWS:FD56423253:0</ns1:messageIdentifier>
<ns1:statusInfo>
<ns1:codeMajor>unsupported</ns1:codeMajor>
<ns1:severity>warning</ns1:severity>
<ns1:messageIdRef xsi:nil="true"/>
<ns1:operationIdRef>createLearnerByProxy</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>
<createLearnerByProxyResponse xmlns="http://www.imsglobal.org/specs/PDPMessSchemav1p0.xsd"/>
</soapenv:Body>
</soapenv:Envelope>
</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>


