Developer: Java
Simplifying Web Services Development with the Decorator Pattern
By Jason Jones
Published December 2007
Learn how to layer functionality onto your Web services without having to go through the costly code/test/deploy cycle.
Oracle Fusion Middleware provides a multitude of options for building Web services: Java, BPEL, ESB, PHP, and more. Even the database is getting into the act with Database Native Web Services. Furthermore, most organizations have heterogeneous IT infrastructures. Thus, your service-oriented architecture (SOA) will probably comprise services from a variety of platforms, tools, programming languages, and vendors. This makes it quite difficult to apply common behavior and policies to Web services across your entire organization.
What's needed is a way to modify and enhance the behavior of all of your Web services without having to modify the underlying system. This problem aligns itself very well with the Gang of Four (GoF) Decorator Pattern. In this article you will learn how to "decorate" your Web services using Oracle Web Services Manager (OWSM), a component in Oracle SOA Suite.
Write for OTN
Promote your technical skills by writing a technical article for Oracle Technology Network.
The Decorator Pattern
The GoF decorator pattern is a design pattern used in object-oriented development that allows you to change the behavior of an object, without changing the original object. This is done by wrapping the original object with a decorator that implements the same interface but adds behavior and/or modifies input and output. A decorator commonly delegates to the underlying vanilla object, but in some cases a decorator may short-circuit the operation if certain conditions aren't met.
This is a very brief introduction to the decorator pattern. I highly recommend reviewing the book Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley) for more information.
Oracle Web Services Manager
Decorators are a good way to think about how OWSM operates. In OWSM one defines policies centrally. These policies are typically organized into four pipelines: PreRequest, Request Response, and PostResponse. Each policy pipeline is then composed of policy steps. All of this can be developed through the OWSM console. A pipeline can be as simple as to log the message. In fact, the default out-of-the-box policy is composed of a Request and Response pipeline which simply logs the message.
These policies act as decorators to the underlying Web service. They enhance or modify the behavior of the service from the client's perspective, but this goal is achieved without having to alter the underlying service.
OWSM allows for a flexible mapping of policies to services. Policies in OWSM can be mapped manually service-by-service, or can be assigned to a URL pattern, much like a Java EE Servlet Filter. Furthermore, pipeline templates can be developed to make it easy to apply a set of policies without having to repeat the setup for each service or URL.
OWSM Architecture
OWSM policies and service registrations are stored centrally in the OWSM Policy Manager, which is backed by a database. Policies and services are built through a Web-based user interface. Although policies in OWSM are built and stored centrally, they can be deployed to many enforcement points. Enforcement points communicate with the OWSM Policy Manager to obtain current policies and service registrations. Enforcement points take two forms: Gateways and Agents.
OWSM Gateways are HTTP proxy servers that run on a Java EE server such as Oracle Application Server. Gateways act as proxies that accept Web service requests, apply policies (including potentially manipulating the message), and then forward the request on. OWSM gateways can apply these same steps to the synchronous responses if appropriate. Gateways provide the most flexible option for enforcing policies. No code or configuration changes of the Web service client or service is required, only the URL that the client invokes has to change. For example, instead of invoking http://jj620.ciscoinc.com:7777/orabpel/default/SampleService/1.0 you would invoke http://jj620.ciscoinc.com:7777/gateway/services/SampleService.
OWSM Agents perform the same functions; however, they are installed in the service or client application directly. Agents operate in-process and require configuration changes to the Web service client and/or service. Luckily, if you're using the Oracle Application Server Web services stack, this is pretty easy. For example, if configuring an Oracle Web service client you add the following to the -client-webservices.xml config file:
<runtime enabled="owsm">
<owsm init-home="C:\java\oc4j_101330\owsm\config\interceptors\C0003005"/>
</runtime>
A final component OWSM users should know about is the OWSM Monitor. This component collects information from the enforcement points. This information is aggregated for logging and reporting on aspects such as performance, errors, and statistics.
A Simple Pipeline
As an example, let's build a pipeline that authorizes users based on the contents of an LDAP directory such as Oracle Internet Directory (OID). Let's say that you've got Web services that can only be invoked by an end-user with certain privileges. A common way to do that is to maintain LDAP groups that represent levels of access (read-only, edit, manage, admin, etc). Users are checked against these groups before being granted access.
You can implement this approach by having clients send along credential information in WS-Security SOAP Headers. Here's an example:
<env:Envelope>
<env:Header>
<wsse:Security>
<wsse:UsernameToken>
<wsse:Username>jjones</wsse:Username>
<wsse:Password>test</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</env:Header>
<env:Body>
...
</env:Body>
</env:Envelope>
In OWSM you can build a pipeline to process these credentials and check them against your LDAP directory. First you need to assign a policy to the service you registered in the previous section. To do this, click on the Manage Policies link on the navigation bar and choose the Policies link next to the enforcement point you just created.
Next, click edit next to the edit icon for the service you just registered. The default pipelines have already been populated.
The next step is to add an "Extract Credentials" step. This step pulls credential information from the SOAP message. Click Add Step Below on the log step in the Request pipeline; in the new step dialog choose Extract Credentials and click Ok.
The extract credentials step is configured by default to look at the HTTP Basic credentials. You can change it to lookup WS-Security UsernameToken header by replacing HTTP with WS-BASIC. Once you've extracted the user credentials you can now add the authorization step. Click the Add Step Below link on the Extract Credentials step you just created; choose the Ldap Authorize step and click Ok. Your Request pipeline should now look like this:
The extract credentials step is configured by default to look at the WS-Security UsernameToken headers. Once you've extracted the user credentials you can now add the authorization step. Click the Add Step Below link on the Extract Credentials step you just created. Choose the Ldap Authorize step and click Ok. Your Request pipeline should now look like this:
Finally you need to configure the Authorize step. Click the Configure link on the authorize step and edit the properties to point to a local LDAP server. The LDAP baseDN should be set to a container that is both users and groups, such as "dc=ciscoinc,dc=com". The ServiceRoles setting is a comma-separated list of ldap groups that are permitted to invoke the service. Finally, blank out the LDAPAdminDN and ensure that LDAPAdminLoginEnabled is set to false.
Click Ok and then click Next > Save. Finally make sure that you click Commit to save these policies. You've now decorated a Web service by securing it based on a user's credentials.
Writing Custom Steps
OWSM provides a ton of out-of-the-box steps that allow you to secure and monitor your Web services. It can also be a powerful part of your application's architecture. Java EE developers will be familiar with Java servlet filters. These filters can also be used to decorate Java servlets, JSPs, and your favorite Web application framework. OWSM Custom steps have access to the SOAP headers and body. The payload can be manipulated; XML elements can be added, modified, or deleted.
In this section you will build a custom step that adds a custom SOAP header that contains a GUID (Globally Unique Identifier). A GUID is an identifier that is essentially a long random number or string. Although it is possible for duplicates to occur, the sheer number of possible values makes it unlikely that a duplicate would occur. Furthermore many GUIDs derive from a MAC address and/or IP address to account for messages created during the same instant. Here's an example of the header you will add:
<env:Envelope>
<env:Header>
...
<otn:GUID>
B72C0BF6-F795-5A04-4D2F-AA24F2DD9888
</otn:GUID>
...
</env:Header>
<env:Body>
...
</env:Body>
</env:Envelope>
Having a globally unique message ID can be quite useful. The most common use I've seen is troubleshooting; it's common for a single Web service message to pass through multiple components and often each of these maintains to a separate log. By logging the message ID these can easily be searched for troubleshooting using command-line tools such as grep. Fortunately for us, OWSM already generates an internal GUID, so you're going to utilize that and add it as a SOAP header so downstream services can use it.
The first step is to start a new empty project in Oracle JDeveloper. Next go to the project properties and click on the Libraries option. Add the following JARs to your classpath:
$SOA_HOME/owsm/lib/coresv-4.0.jar
$SOA_HOME/owsm/lib/extlib/axis.jar
$SOA_HOME/owsm/lib/extlib/saaj.jar
When you're done it should look approximately like this:
Now create a new Java Class that needs to extend com.cfluent.policysteps.sdk.AbstractStep. JDeveloper should give you an error at this point saying you need to implement some required methods. Click on the Quick Fix balloon and choose Implement Methods... . This should provide a stub for the IResult execute(IMessageContext context) method. The execute method is run by the OWSM engine when your custom step is a pipeline. You must return an instance of IResult, to tell OWSM whether or not to proceed. Typically, the execute method returns IResult.SUCCEEDED which tells OWSM to continue processing the pipeline.
The message and associated metadata is passed to the custom step in the IMessageContext object. Most commonly the step will call context.getRequestMessage() or context.getResponseMessage() to get the SOAP message. In this case, because you need to manipulate the message, you need to cast the IMessageContext instance to com.cfluent.pipelineengine.container.MessageContext as shown below:
&
...
String stage = context.getProcessingStage(); // processing stage indicates if this is a request or a response
MessageContext msgContext = (MessageContext)context;
SOAPMessage message = null;
if (IMessageContext.STAGE_PREREQUEST.equals(stage) || IMessageContext.STAGE_REQUEST.equals(stage)) {
message = msgContext.getRequest(); // getting the Axis message
} else if (IMessageContext.STAGE_RESPONSE.equals(stage) || IMessageContext.STAGE_POSTRESPONSE.equals(stage)) {
message = msgContext.getRequest(); // getting the Axis message
}
...
The next step is to add the SOAP header to the message. First you need to get a handle on the SOAPEnvelope. (Note that this is the "Axis" SOAP envelope.)
There are a couple more things to note about the code shown below. First, you need to cast the SOAPEnvelope and other elements to the Axis version. Only the Axis versions allow us to modify the payload. Second, if you have changed the payload you need to call setDirty(true) to let the OWSM engine know that the payload has changed. It has been my experience that this method does not recursively mark child elements as dirty. Although I haven't found any documentation confirming this, it seems that if you change something four levels deep, you need to make sure to call setDirty(true) on each element all of the way up the chain.
...
// now get the GUID and build a SOAPElement
try {
SOAPEnvelope env = message.getAxisMessage().getSOAPEnvelope();
// next build header element with a name of GUID
SOAPHeaderElement element =
(SOAPHeaderElement)env.getHeader().addChildElement("GUID", "otn", "http://otn.oracle.com/owsmarticle");
element.addTextNode(context.getGUID()); // add the guid that is generated by OWSM
env.setDirty(true); // need to let OWSM know that we've changed the payload.
} catch (Exception e) {
logger.log(Level.SEVERE, "exception occurred while adding GUID header", e);
}
...
Finally log a success message and return a SUCCEEDED IResult. Logging should be done via special OWSM libraries. The steps you develop could be executed on many machines and so it might not be appropriate to write to your own log files or System.out/err. OWSM provides a ILogger class, which behaves very much like Log4J or Java's built in logging classes. Finally by calling AbstractStep.createMethod(...) in the super class you can generate a result to return to OWSM.
...
logger.log(Level.INFO, "successfully added GUID {" + context.getGUID() + "} to SOAP message.");
return createResult(IResult.SUCCEEDED);
...
The full source of this step is in the sample download zip.
Packaging Custom Steps
In order to deploy this custom step, you need to prepare two artifacts: a jar file of the class prepared above and a custom step template for OWSM. The step template is an XML descriptor that describes the class used, some information to display to administrators, and most important, the properties that can be used to configure your step. Below is an abbreviated example:
<csw:StepTemplate
..
name="GUIDStep"
...
>
<csw:Description>Custom step that adds GUID SOAP header</csw:Description>
<csw:Implementation>oracle.otn.guidstep.GUIDStep</csw:Implementation>
<csw:PropertyDefinitions>
<csw:PropertyDefinitionSet name="Basic Properties">
<csw:PropertyDefinition name="Enabled" type="boolean">
<csw:Description>If set to true, this step is enabled</csw:Description>
<csw:DefaultValue>
<csw:Absolute>true</csw:Absolute>
</csw:DefaultValue>
</csw:PropertyDefinition>
</csw:PropertyDefinitionSet>
</csw:PropertyDefinitions>
</csw:StepTemplate>
Deploying Custom Steps
To deploy a custom step you need to deploy the jar file and then upload the step template. To deploy the jar file simply place it in the $OWSM_HOME/lib/custom directory. Because your OWSM step could be deployed to multiple gateways and/or agents you need to complete this step on each server, which your policy pipeline will be enforced. After doing so you must restart the application server.
Next you need to upload the policy template. To do this first log in to the Web Services Manager console and click on the Steps link next to your enforcement point. Click the Add New Step button and then upload the XML step template.
Again, you need to do this step for each enforcement point. You now should be able to add this step to your policy pipelines.
Tips for Custom Step Development
One of the obstacles to developing custom steps is the code-deploy-test cycle. Typically a change the custom step requires you to restart OC4J to pick up the new classes. With the full Oracle SOA Suite installed, this can mean lots of waiting around for things to restart.
One way to speed this up is to use the Java Platform Debugging Architecture (JPDA). This allows JDeveloper to connect to OC4J, which has a couple benefits. First you can step through code and view variable values while your custom step runs. Second and most important, you can change code in JDeveloper and through the magic of hot-swap code deploy, JDeveloper will ship the code to the JVM and start using it right away. This is a huge speedup over building a jar, transferring it to the custom lib directory, and restarting the server.
To make this work, first you need to configure OC4J to listen for a JPDA debugger:
- Open Enterprise Manager (http://{hostname}:{port}/em) and click on the oc4j_soa instance.
- Click the Administration tab.
- Click on Server Properties.
- Under command-line options add the following line:
-Xrunjdwp:transport=dt_socket,server=y,address=9901,suspend=n. - Click Apply and then be sure to restart the OC4J server.
- Right-click on the project and choose Project Properties.
- Click the Run/Debug option.
- Click on the default run configuration and click Edit...
- Under the Launch Settings option make sure the Remote Debugging and Profiling checkbox is checked.
- Choose the Debug > Remote option.
- Enter the options as shown below:
- Click OK.
Now to connect to the OC4J instance simply right-click on the project and choose Start Remote Debugger:
The full source for this example is in the sample download zip.
Conclusion
Developing Web services can be complex. Even more complex can be designing Web services implementations that allow you to reuse common code and abstract common functionality. The decorator pattern is a flexible way to layer functionality onto your services without having to go through the costly code/test/deploy cycle. Developing custom steps and deploying them in policies simplifies your Web services project by freeing your developers to focus on business functionality and not boilerplate code.
Jason Jones ( jason.jones@zirous.com) is a System Architect for Zirous, an Oracle Partner. He specializes in Enterprise Java and SOA solutions and is a member of Oracle's SOA customer advisory board. Jason is an Oracle ACE Director and authors a blog on Java, SOA, and Oracle.