Copyright  2001-2007 Nicholas Quaine.
Home   Basics   ServerSide   ClientSide   Demos   FAQ   Resources
ServerSide
1. Overview
2. Deploying Tomcat
3. Deploying SOAP
4. Deploying Services
Packages
Version Details
Sun Java
Tomcat
Apache SOAP
JavaMail
JAF
Xerces

Prev   1    2    3    4    Next
Server-Side SOAP
4. Writing and Deploying your own Services

This section walks you through the steps you are required to take to write and deploy your own web services using Tomcat and Apache SOAP for Java. The basic idea here is that we will be developing and running the service within a Java IDE (rather than starting tomcat at the command line and using precompiled class files). The greatest benefit of this is that you can debug the services with your IDE's debugger as they are being invoked. You can use the IDE of your choice. It is presumed only that you are quite familiar with how to use and configure it. These instructions can be applied to both Win32 and UNIX operating systems.

The service we will be writing as an example is the Quotation Database service (which, notably, is the same as the service that is implemented and available online in the Demo Section). It is basically a service that gives you access to a list of quotations categorised by author's name. The database of quotations is persistent across shutdowns of the appserver so the data (while in memory at runtime) is stored in an xml file that is updated on a regular basis. As for service methods, things are fairly simple: you can get a full list of quotations, get a list of quotations by author, or you can submit a quotation to the database.

Here are the three service method signatures:

Quotation[] getAllQuotations ( );
String[] getQuotationsByAuthor ( String author );
void submitQuotation ( String author, String text );

Where the user-defined type Quotation is defined as:

Quotation
{
     String author;
     String text;
}

STEP NINE Develop your Service Classes



The java source files for the Quotation Database service are available for you in the soapuser-1.0 archive:
For Win32 : soapuser-1.0.zip
For UNIX : soapuser-1.0.tar.gz
Save the file to your machine and unpack it to the soap directory (ie. C:\soap under Win32 or /home/me/soap under Unix). Everything will be contained in a subdirectory called soapuser-1.0. In particular the quotation service java source files will be under soapuser-1.0\src in a directory structure that complies with the package name (ie. com\soapuser\soap\server\quotation). Along with the java source files there are also some xml files and some java client source files which are explained in detail further down on this page.

To develop using the example source files take the following steps (note that the root level directory "soap" in the following instructions is is C:\soap on Win32 and /home/me/soap on UNIX):

  • Create a project in your preferred Java IDE, with project source path set to soap\soapuser-1.0\src.
  • Ensure that the JDK in use for the project is JDK 1.3.0 or later - note that you may need to configure your IDE to take into account the JDK 1.3 that you downloaded in step one (see Section 2).
  • Add the three java source files found in soap\soapuser-1.0\src\com\soapuser\soap\server\quotation to the project.
  • Make all the required libraries available to the project (note that these are the same jar files that we specified in mystartup.bat in the previous sections). Important Note : Ensure with whatever priority scheme that your IDE employs that xerces.jar is before parser.jar on the classpath. Here are the libraries to add:
    • SOAP : soap\soap-2_2\lib\soap.jar
    • JAVAMAIL : soap\javamail-1.2\mail.jar
    • JAF : soap\jaf-1.0.1\activation.jar
    • XERCES : soap\xerces-1_2_3\xerces.jar
    • TOMCAT : soap\jakarta-tomcat-3.2.x\lib\ant.jar, jasper.jar, jaxp.jar, parser.jar, servlet.jar, webserver.jar
  • Compile the project.

Things to ensure when developing your own classes:

  1. All service methods for a service should be implemented in the same class. Note that you can have several different classes that implement service methods but they cannot be grouped under the same URN if they are not implemented by the same class. If they are logically related service methods (ie. all pertaining to the same service) then you should address them via the same URN and thus you should implement them all in the same class.
  2. All service methods must be defined as public. For clarity, other methods within the class should be defined as private if possible.
  3. Every complex type must be implemented as a Java-Bean complete with accessors (ie. getters and setters) for every field. Fields should be defined as private.
  4. Each complex type must have a no-parameter constructor.
  5. For simplicity, it is a good idea to put your complex types in the same package as your service implementation class.
Note the following corresponding features in the Quotation Database service code:
  1. The implementation class QuotationDB (in QuotationDB.java) implements all three service methods.
  2. All the service methods implemented in QuotationDB are public. All other methods in this class are private.
  3. The class Quotation is a correct Java-Bean implementation of a complex type. Notice that there are accessors for every field and all fields are defined as private.
  4. The class Quotation contains a no-parameter constructor.
  5. All classes are packaged into the com.soapuser.soap.server.quotation package.
You have probably noticed that there is a third class (QuotationDBInitServlet). This is a very simple servlet that will be used to pass parameters to the implementing class (QuotationDB). Remember that we are implementing a database and that we would like the database to persist across shutdowns of the appserver. The database is implemented as an XML file. We need a way to send parameters to the implementation class to specify the fully qualified name of the database file as well as the time interval between flushes to disk. More on this in step eleven.


STEP TEN Write a Deployment Descriptor



Alongside the java source files for the Quotation Database service is the corresponding deployment descriptor (DeploymentDescriptor.xml). If you are developing your own service classes then note the sections shown in green below - these are where you will potentially need to make changes. Explanatory notes follow.


  <isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
               id="urn:QuotationService">

      <isd:provider type="java"
                    scope="Application"
                    methods="getAllQuotations getQuotationsByAuthor submitQuotation">
            <isd:java class="com.soapuser.soap.server.quotation.QuotationDB"
                         static="false"/>
      </isd:provider>

      <isd:faultListener>org.apache.soap.server.DOMFaultListener</isd:faultListener>

      <isd:mappings>
          <isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
                   xmlns:x="urn:QuotationService"
                   qname="x:quotation"
                   javaType="com.soapuser.soap.server.quotation.Quotation"
                   java2XMLClassName="org.apache.soap.encoding.soapenc.BeanSerializer"
                   xml2JavaClassName="org.apache.soap.encoding.soapenc.BeanSerializer"/>
      </isd:mappings>

  </isd:service>

Respective explanatory notes:

  • id="urn:QuotationService" : This is where you specify the URN of the service. You can choose any URN - but it will need to be made known to the client.
  • scope="Application" : The scope refers to the lifetime of the service object (in our case the QuotationDB object) and it can take one of three values. In this way you can specify to the SOAP servlet how long the service object should stay alive. Specifying Request means that the object will only live for the duration of the service invocation. Specifying Session means that the object will remain alive as long as the http session that created it (the object is available only to that http session). We have specified Application to indicate that we want the service object to be instantiated once and that everyone should share this object from then on. This makes sense as our service class represents a shared database of quotations where by "shared" we mean shared across all invocations and http sessions.
  • methods="getAllQuotations getQuotationsByAuthor submitQuotation" : This is simply where you specify your service method names.
  • class="com.soapuser.soap.server.quotation.QuotationDB" : The fully qualified classname of the implementing class (note that following this, you can specify static as true if the service methods are defined static in the implementing class).
  • <isd:mappings> : In this section we are specifying details of the complex types used. Note that you can have many complex types as you like (as was the case in the addressbook example). In this case you would simply add more <isd:map ... /> elements.
  • xmlns:x="urn:QuotationService" : This field is the namespace of the XML element representing the complex type. You can use the URN though this is no way a must.
  • qname="x:quotation" : This is the name of the XML element representing the complex type.
  • javaType="com.soapuser.soap.server.quotation.Quotation" : This is the fully qualified classname of the class implementing the complex type as a java-bean.

STEP ELEVEN Startup Tomcat within your Java IDE



You already made the Tomcat jars available to your project in step nine. To startup Tomcat is then simply a matter of setting your project's main class to be org.apache.tomcat.startup.Tomcat and specifying the application parameters. The only application parameter you are required to specify is the Tomcat home directory from which the appserver will find its way to its configuration information (Tomcat looks for configuration information in %TOMCAT_HOME%/conf/server.xml). To do this, set the application parameters to the following:
  Win32
  -home C:\soap\jakarta-tomcat-3.2.x
  UNIX
  -home /home/me/soap/jakarta-tomcat-3.2.x

Now, before we go on, let's revisit the logic behind the third java source file, namely QuotationDBInitServlet.java as it has repercussions on the Tomcat configuration. As mentioned in step nine, this is a very simple servlet that will be used to pass parameters to the implementing class (QuotationDB). The parameters pertain to the database file that gives us persistency across shutdowns of the appserver. The basic idea is that we create a servlet which is loaded at Tomcat startup time and whose sole purpose in life is to read the QuotationDB parameters and store them in memory. At some time later when the QuotationDB is instantiated it will ask the QuotationDBInitServlet for the parameters. There are only two: a fully qualified filename for the database file and the interval time for flushing the database to disk. Once the QuotationDB constructor has the parameters it shall, if the database file exists, construct the database in memory from the contents of the file. It will also launch a thread that wakes up from time to time according to the flush interval and dumps the database to disk. The handy part about using a servlet to read the parameters for us is that we can make use of the servlet parameter framework already in place for servlets.

To make Tomcat aware of the new servlet we have to declare it as a webapp in the server.xml configuration file. Add the following context information (in bold) at the end of the server.xml config file which can be found in soap\jakarta-tomcat-3.2.x\conf (again, the root directory "soap" as mentioned here is C:\soap on Win32 and /home/me/soap on UNIX). Note that the webapp configuration file is soap\soapuser-1.0\src\com\soapuser\soap\server\quotation\web-inf\web.xml and is declared via the docBase attribute (Tomcat will expect it under the web-inf subdirectory of the docBase). It is this file that declares the class that implements the servlet (QuotationDBInitServlet) and that holds the values for the QuotationDB parameters.
For Win32

        <Context path="/quotation"
                 docBase="C:/soap/soapuser-1.0/src/com/soapuser/soap/server/quotation"
                 crossContext="true"
                 debug="0"
                 reloadable="true"
                 trusted="false">
        </Context>

    </ContextManager>
</Server>
For UNIX

        <Context path="/quotation"
                 docBase="/home/me/soap/soapuser-1.0/src/com/soapuser/soap/server/quotation"
                 crossContext="true"
                 debug="0"
                 reloadable="true"
                 trusted="false">
        </Context>

    </ContextManager>
</Server>

Important: You may also need to update the quotation database fully qualified path name in the web.xml configuration file (soap\soapuser-1.0\src\com\soapuser\soap\server\quotation\web-inf\web.xml) to match your actual soap directory (UNIX developers will definitely need to do this unless they actually have set up the user 'me'!). As delivered, the value is as follows. If the directory as shown here exists you do not need to change anything. Do not concern yourself with ensuring that the file quotationdb.xml exists - it will be created by the service at the appropriate time.

  Win32
  <param-value>C:\soap\soapuser-1.0\quotationdb.xml</param-value>
  UNIX
  <param-value>/home/me/soap/soapuser-1.0/quotationdb.xml</param-value>

You can now startup Tomcat within the IDE. First ensure that any Tomcat server that you started via the command line when going though the previous sections has been shutdown (so that we free up port 8080) and then start Tomcat from within the IDE by running the main class as configured above. You should get output similar to the following - notice the new context (aka webapp) called quotation.

  2001-07-24 01:18:15 - ContextManager: Adding context Ctx( /examples )
  Starting tomcat. Check logs/tomcat.log for error messages 
  2001-07-24 01:18:15 - ContextManager: Adding context Ctx( /admin )
  2001-07-24 01:18:15 - ContextManager: Adding context Ctx( /quotation )
  2001-07-24 01:18:15 - ContextManager: Adding context Ctx(  )
  2001-07-24 01:18:15 - ContextManager: Adding context Ctx( /test )
  2001-07-24 01:18:15 - ContextManager: Adding context Ctx( /soap )
  2001-07-24 01:18:20 - PoolTcpConnector: Starting HttpConnectionHandler on 8080
  2001-07-24 01:18:20 - PoolTcpConnector: Starting Ajp12ConnectionHandler on 8007

Note that as the Tomcat server is running within the IDE you can run it in debug mode and put breakpoints in the service code. If you download the source code for SOAP and Tomcat from Apache you can even debug all the way through those levels as well (to do that you will need to add the SOAP and Tomcat source files to your project's source path, remove the corresponding jars from the required librearies and recompile). As a further aid to debugging you can run the TCP/IP Tunnel GUI (as specified in the previous sections) to see the SOAP dialogue as it is happening.


STEP TWELVE Deploy your services



Once Tomcat has completed its startup run the deployer script (as developed in step seven), giving the quotation service deployment descriptor's filename as a parameter, like this (note that the line breaks are only for display purposes - type the whole command on a single line):

  Win32
  > mydeployer.bat
    C:\soap\soapuser-1.0\src\com\soapuser\soap\server\quotation\DeploymentDescriptor.xml
  UNIX
  > mydeployer.sh
    /home/me/soap/soapuser-1.0/src/com/soapuser/soap/server/quotation/DeploymentDescriptor.xml
Just as before, if there was no error it means the service was deployed successfully. You should be able to see it listed as urn:QuotationService on the SOAP admin page http://localhost:8080/soap/admin/index.html.


STEP THIRTEEN Verify that your services are operating correctly



Included in the soapuser-1.0.zip archive are a few java source files for some simple SOAP clients to the quotation service. Whereas our server-side code was in com\soapuser\soap\server\quotation the client-side source files are under com\soapuser\soap\client\quotation.

Create a new project with same source path and libraries (attention pedants, technically this time you will not need the Tomcat jars), add the three java source files and compile the project. Running GetAllQuotations (specifying only your SOAP server URL as an application parameter) should result in a list comprised of the two default quotations (one by Winston Churchill and another by Oscar Wilde). You can add a third quotation by running SubmitQuotation (specifying the URL, the author and the text as parameters). Running GetAllQuotations again should result in a list of quotations including the new one. You can test the getQuotationsByAuthor service method by running GetQuotationsByAuthor (specifying the URL and the author as parameters).

Below is one way to run these clients (you will probably need to alter the classpath entry shown in bold so that it corresponds to the place where your class files are created). Note that the arrow ( ) is used to indicate where a line is a continuation of the preceeding one but we had to split it just for display purposes.
Win32 script : testquotationservice.bat

  set CLASSPATH=C:\soap\soap-2_2\lib\soap.jar
  set CLASSPATH=%CLASSPATH%;C:\soap\javamail-1.2\mail.jar
  set CLASSPATH=%CLASSPATH%;C:\soap\jaf-1.0.1\activation.jar
  set CLASSPATH=%CLASSPATH%;C:\soap\xerces-1_2_3\xerces.jar
  set CLASSPATH=%CLASSPATH%;C:\soap\soapuser-1.0\classes
  java com.soapuser.soap.client.quotation.GetAllQuotations
    http://localhost:8080/soap/servlet/rpcrouter
  java com.soapuser.soap.client.quotation.SubmitQuotation
    http://localhost:8080/soap/servlet/rpcrouter
    "Kennedy, John F." "Forgive your enemies, but never forget their names."
  java com.soapuser.soap.client.quotation.GetQuotationsByAuthor
    http://localhost:8080/soap/servlet/rpcrouter "Wilde, Oscar"
  pause
UNIX script : testquotationservice.sh

  #!/bin/sh
  CLASSPATH=/home/me/soap/soap-2_2\lib\soap.jar
  CLASSPATH=${CLASSPATH}:/home/me/soap/javamail-1.2/mail.jar
  CLASSPATH=${CLASSPATH}:/home/me/soap/jaf-1.0.1/activation.jar
  CLASSPATH=${CLASSPATH}:/home/me/soap/xerces-1_2_3/xerces.jar
  set CLASSPATH=${CLASSPATH}:/home/me/soap/soapuser-1.0/classes
  export CLASSPATH
  java com.soapuser.soap.client.quotation.GetAllQuotations
    http://localhost:8080/soap/servlet/rpcrouter
  java com.soapuser.soap.client.quotation.SubmitQuotation
    http://localhost:8080/soap/servlet/rpcrouter
    "Kennedy, John F." "Forgive your enemies, but never forget their names."
  java com.soapuser.soap.client.quotation.GetQuotationsByAuthor
    http://localhost:8080/soap/servlet/rpcrouter "Wilde, Oscar"

You should now also see that a file called quotationdb.xml has been created in the soapuser-1.0 directory and that this file is being updated every 30 minutes. Note that the filename (including the path) and the interval can be configured by altering the web.xml file in soapuser-1.0\src\com\soapuser\soap\server\quotation\web-inf but you will have to bounce your Tomcat server before any change is taken into account. Check that the database is being persisted correctly by taking a look at the contents of the file (noting that it can be out of sync with what is in memory if you have submitted new quotations since the last flush).

One final test is to shutdown and restart Tomcat to check if your submitted quotations were persisted and picked back up at restart. To test this simply bounce the Tomcat server and run GetAllQuotations again.


That winds things up for the server-side tutorial. Now that you have a Tomcat server running and the quotation service deployed you are ready to see how the service can be exploited using a variety of client-side technologies. To get started, go to the section on Client-Side SOAP.

[ Nicholas Quaine ]

Go to Client-Side SOAP

Copyright 2001-2007 Nicholas Quaine. All rights reserved.