EMC Developer Network

Inside the Web Services Framework

January 2007
Contributed by - Fabian Lee (fabian.lee@flatironssolutions.com),Flatirons Solutions Corporation

Abstract

With the release of 5.3 and the Web Services Framework (WSF), Documentum has provided the tools to develop and deploy web services with a minimal learning curve. The WSF is integrated into the Documentum product suite, and Service Based Objects (SBO) can be exposed as web services transparently from within Application Builder. However, mastering web service design, implementation, and deployment requires a deeper understanding of the architecture and toolset. The purpose of this article is to take you beyond the published documentation, and explore the more advanced features of the WSF.

Table of Contents

Before you Begin

In this article I assume that you already have a basic understanding of web services, and have previously deployed Service Based Objects (SBO) as web services through the standard methods described in the Web Services Framework Development Guide. I make constant reference to the Hello and RepoAccessor services, which can be copied and pasted directly from the guide.

If you need an introduction to SOAP, web services, XML, or Java please see the Reference section of this document for introductory material and references.

Web Service Transport and Messaging

Tools such as Documentum Application Builder (DAB) keep the process of creating a web service at such a high-level that is easy to forget the underlying technology at work. So, let's first take a step back with a brief discussion on transport and messaging in the web service protocol stack. Which is really just a fancy way of saying 'How are you going to send the data (transport), and how I should I interpret it (messaging)'.

Although SOAP does not mandate HTTP as the transport protocol, it is certainly the most popular method. This can be attributed to the ubiquity of HTTP and its ability to cross corporate firewalls without major changes to network infrastructure. Leaving the discussion on security to a later section in this document, the ability of HTTP to transparently enable integration with external system has been a major driver for web services.

Messaging requires a bit more technical explanation, and is critical to a deeper understanding of web services. There are several standards available for the encoding of messages, but we will discuss the Simple Object Access Protocol (SOAP) because it is the most widely used and is also the protocol used by the WSF.

SOAP Message Structure

SOAP is an XML-based specification for exchanging structured information. It defines a generic structure for a message along with encoding rules for data types.

A SOAP message is made up of an Envelope, an optional Header, and a Body.

The Envelope is mandatory and is the root element of all SOAP messages. The Header element is optional, and is a generic mechanism for adding SOAP features. For example, this is where the WSF sends credential information. The Body element is mandatory, and is a container element for content. This is also where faults (web service exceptions) are placed.

The listing below shows an example SOAP request.


<env:Envelope
  xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
  env:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">

  <env:Header>
     <t:Transaction xmlns:t="some-URI" env:mustUnderstand="0">
     5
     </t:Transaction>
  </env:Header>

  <env:Body>
     <m:GetLastTradePrice xmlns:m="Some-URI">
        <symbol>DIS</symbol>
     </m:GetLastTradePrice>
  </env:Body>

</SOAP-ENV:Envelope>

SOAP Message Encoding

Encoding rules are crucial to understanding web services because they determine what data types can be serialized using SOAP. The SOAP specification defines language agnostic encodings for:

  1. string
  2. integer, byte, short, int, long
  3. decimal, float, double
  4. boolean
  5. dateTime, date, time, duration
  6. base64Binary
  7. User defined structures
  8. Arrays of the above types

Apache Axis, the SOAP engine that is used by the WSF adds Java specific bindings and serializers which support the following Java types:

  1. String
  2. Simple types and their wrappers (boolean, byte, short, int, long, float, double)
  3. Date, Calendar
  4. byte arrays
  5. Arrays of supported types
  6. Collections of supported types (breaks interoperability with .NET)
  7. JavaBeans

Additionally, WSF supplies serializers for the following types within an SBO:

  1. java.io.File (converted to byte[], must have content transfer enabled)
  2. IDfId (converted to String)
  3. IDfTime (converted to Calendar)

Using a tool such as DAB, it is difficult to appreciate why an IDfSysObject object can be used as a parameter in an SBO, but not a web service enabled SBO. Now you can see that answer is simply that there are no encoding rules or serializers for this type.

Custom Types

Now armed with a basic understanding of SOAP encoding, you should realize that you cannot simply send arbitrary objects across the wire and expect them to be interpreted correctly on the other end. Luckily, Axis provides a serializer for objects that conform to the JavaBean specification. The term 'JavaBean' is a label that refers to nothing more than a plain old Java object that follows these rules:

  1. The class must be public
  2. It must have a parameterless constructor
  3. It must implement java.io.Serializable
  4. It must have getter and setter methods following the JavaBeans pattern

Following these rules facilitates JavaBean introspection, which allows Axis to operate on a custom type. Here is an example of a JavaBean:


public class SearchResult implements java.io.Serializable {
	protected String objectName;
	protected Calendar lastModifiedDate;

	// no-args constructor
	public SearchResult() {
		objectName = "";
		lastModifiedDate = new GregorianCalendar();
	}

	public String getObjectName() { return objectName; }
	public void setObjectName(String s) { objectName = s; }

	public Calendar getLastModifiedDate() { return lastModifiedDate; }
	public void setLastModifiedDate(Calendar c) { lastModifiedDate = c; }
}

Now that it conforms to the JavaBean specification, it can be properly serialized by the WSF and can be used as either an argument or return type in your SBO methods.

Collections

Collections are fully supported by Axis, but run into issues of interoperability with .NET. For this reason, it is best to steer clear of their use in parameters or return types. Note that this is not a limitation on the internal implementation, merely on the method signature. If a collection is used internally, a quick conversion to an array is the safest route for maintaining interoperability.


public String[] returnStringArray() {
	ArrayList list = new ArrayList();
	list.add("item1");
	list.add("item2");
	return (String[]) list.toArray(new String[] {});
}

Arrays

Many developers assume that arrays are too complex for transmission by SOAP, but this is not the case. Both single and multi-dimensional array are supported by the SOAP specification. Currently, only single-dimensional arrays are supported by WSF.

If your data must be multi-dimensional, then my recommended workaround is to use an XML-Java binding library for serialization. In other words, instead of using a method signature like:

int[][] returnMultiDimensionalArray(int numberOfEntries)

write the method as:

String returnMultiDimensionalArray(int numberOfEntries)

Where the returned string could be structured as:

<intarray2d>
	<row>
		<value>1</value><value>1</value>
	</row>
	<row>
		<value>2</value><value>4</value>
	</row>
	<row>
		<value>3</value><value>9</value>
	</row>
</intarray2d>

The String value returned is structured XML which can be interpreted as a two dimensional integer array with three rows. The best way to accomplish this conversion is with an XML binding framework. Castor, JAXB, and Apache's XmlBeans are all libraries used for this type of marshalling. Castor is a natural choice since it is already distributed with the Axis engine.

Some Axis purist may argue that this workaround should have been implemented using a custom serializer, but I intentionally avoided this because it requires a custom deserializer to be registered on the client side as well. This is palatable when dealing with Java clients, but unacceptable when interoperability is desired.

Generating a Java Client

The WSF Development Guide provides ample documentation for deploying a web service provider, but avoids detailing the process for creating a web service consumer. The client consumer is an essential part the web service equation, so I will provide instructions in this section on generating a static Java client.

Without a helper utility, writing a web service client would be quite a chore with all the network communication and marshalling that needs to happen. Fortunately, Axis provides us with a tool called WSDL2Java which can take a web service definition and create all the supporting classes needed to make calls to a web service.

To run this utility against the DocbaseCredentials service (installed by default in WSF), use the following command from the console:

java org.apache.axis.wsdl.WSDL2Java http://localhost:8080/ws/services/DocbaseCredentials?wsdl

The WSDL2Java tool will generate a set of java files that can interact directly with the web service producer. The files will be located in the com.documentum.ws._2005.framework package which is directly related to the namespace specified in the WSDL, http://documentum.com/ws/2005/framework.

The java classes in this directory depend upon the SOAP libraries and dependencies, and so you must compile them against the jars which are located in the WEB-INF\lib directory of the WSF web application. Below is a test program that exercises the generated client code of the DocbaseCredentials service.


import com.documentum.ws._2005.framework.*; // DocbaseCredentials

public class TestDocbaseCredentials {
	
public static void main(String args[]) throws Exception {

	if(args.length<3) {
		System.out.println("Usage: docbase user pass");
		System.exit(1);
	}
		
	String repository = args[0];
	String domain = "";
	String username = args[1];
	String password = args[2];


	// web service locator
	DocbaseCredentialsServiceLocator credentialsLocator = new DocbaseCredentialsServiceLocator();
	credentialsLocator.setMaintainSession(	true);

	// fill credentials object
	DocbaseCredentials credentials = credentialsLocator.getDocbaseCredentials();
	DocbaseCredentialsInfo info = new DocbaseCredentialsInfo(repository, domain, password, username);

	// call web service and display token
	String token = credentials.newCredentials(info, true);
	System.out.println("Acquired Token=" + token);
		
}

}

This section was a very brief introduction to generating a Java web service consumer. In fact, there was so much material in this section that I broke it out into a separate article, Consumers of the Web Services Framework. This article goes into the full detail on writing a web service consumer in Java, C#, VB.NET, VBA, and Perl. Additionally, it addresses the issue of content transfer using Base 64 encoding in each environment.

Security

Web services are often used to integrate with systems external to an organization, which quickly lead security-minded administrators to voice concerns about the plain-text nature of the SOAP protocol. This concern should not to be taken lightly, considering that both authentication credentials and potentially sensitive business data will be transmitted across the public internet.

Since web services are merely resources served up by the application server, they can leverage the same techniques traditionally applied to servlets, images, static HTML and other web resources:

  1. Basic authentication ' a very simple authentication mechanism between client and server
  2. Secure Sockets Layer (SSL) ' an encrypted communication channel established between client and server

Basic authentication transmits credentials in plaintext, so it is necessary to implement both these measures in order to enhance the security of the WSF. An important note to remember is that this security is between the web service producer and consumer, and does not protect information between the WSF and the Docbase.

The specifics of configuring this level of security are beyond this high-level overview, but I have written an article entitled, Securing the Web Services Framework which gives a step-by-step approach.

Message Monitoring

The final tip I will leave you with is information on a debugging tool that comes bundled with the Axis engine. The tool is called tcpmon and acts as a proxy between a web consumer and provider, capturing the exchange of SOAP messages as illustrated below.

This tool can be invoked from the command line to forward network traffic from port 8081 to the WSF at port 8080, with the following command:

java org.apache.axis.utils.tcpmon 8081 localhost 8080

The web service consumer must then change their endpoint port address to route through the proxy. Using the Hello service as an example, this can be accomplished by manually setting the end point address and port of the service locator.

HelloServiceLocator hloc = new HelloServiceLocator();
hloc.setHelloEndpointAddress("http://localhost:8081/ws/services/Hello");

                                    

I have found this tool to be useful during development not only for message debugging, but also gauging the performance of a series of web service calls.

Summary

The Web Services Framework has provided a solid toolset for developing and deploying web services. But the abstractions that are necessary for novices can often be an obstacle for more advanced developers who need to harness the full power of the underlying engine.

In this article we have gone beyond the published documentation of the WSF in order to gain a deeper understanding of the core models, tools, and architectural decisions that must be made when implementing web services. Developers armed with this knowledge can work with higher-level tools, while remaining cognizant of the lower-level implications.

About the Author

Fabian Lee has over 10 years experience as a software engineer and consultant, and has been assisting customers with technical and business solutions based on Documentum for over six years. His clients have included some of the leading names in the airline, finance, insurance, health, government, and IT sectors. Fabian currently works as a consultant at Flatirons Solutions Corporation, focusing on their government ECM solutions in the Washington, D.C. area.

Fabian started working with the WDK at version 1.0.1, and has written customizations for each major version since. An avid open-source enthusiast, he works on several community projects including the Repository Interrogation Tool.

References

Related Articles

Securing the Web Services Framework, Fabian Lee

Consumers of the Web Services Framework, Fabian Lee

Using the 5.3 Web Services Framework, Lisa Hill

SOAP Tutorials

Introduction to SOAP

A Brief History of SOAP

SOAP Version 1.2 Part 0: Primer

SOAP: Simple Object Access Protocol

SOAP Libraries and Axis Engine

Axis Home Page

Web service implementations

Tomcat

Advanced serialization of Collections and Maps

DCTM Support Site

Unable to deploy a web service with multidimensional array as return value

SBO Checkin as Web Service [WS_CG_METHOD_PROCESS_FAILED_NO_PROCESSOR]

Has anyone used the UCF for Web Services content transfer?

Bug logged for session attribute problem

Bug logged against multi-dimensional arrays

EMC Documentum Technical Publications

WSF Development Guide