Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

SOA adventures, Part 1: Ease Web services invocation with dynamic decoupling

Execute a solid Web services invocation approach

Mark M. Davydov, Ph.D., is an internationally-known expert in software engineering and systems architecture, including SOA. Dr. Davydov is the author of numerous highly acclaimed articles in computer-related publications. His 2001 book Corporate Portals and e-Business Integration -- A Manager's Guide, McGraw-Hill Professional Publishing, introduced many ideas that influenced the progression of Service-Oriented Architecture and the Web services model.

Summary:  Learn how to use Web Service Adapters using Dynamic Proxy Patterns for dynamic decoupling. With proper use of this mechanism, you can provide the needed levels of abstraction in order to facilitate proper Service-Oriented Architecture (SOA) implementations and service reuse.

Date:  29 Apr 2005
Level:  Intermediate
Also available in:   Chinese

Activity:  8279 views
Comments:  

Introduction

An idealized requirement in Service-Oriented Architecture (SOA)-based system design is that of decoupling between service consumers and service providers. Decoupling can take various forms ranging from static, where decoupling is enabled by requester-side stubs at compile time, up to full dynamic decoupling, where decoupling is completely encapsulated and established at run-time in all service invocations. Clearly, full dynamic decoupling is a stringent demand. If a service is dynamically decoupled, then changes in the service characteristics leads to a modification in that service implementation, but all other system elements, especially invocation mechanics, remain constant. The advantages of such a design are intuitively clear.

The increasing expectations and demands for greater functionality and capabilities from SOA and Web services often result in upsurged software complexity. The reality of today?s SOA implementations is that, although functionality modularization was planned with services, those have often been coupled to a single middleware or communication protocol (such as MOM-based JMS, HTTP/SOAP, and so on). In most organizations, developers focus, to some degree, on static decoupling (often referred to as binding) when extending functionality in the applications, and that forces the creation of separate clients for every Web service, instead of having a single client capable of accessing multiple services at one time.

Limitations derived from static binding are significant -- static binding in the code prevents reuse of services across the enterprise in different applications. In short, even through the need to abstract and encapsulate the different middleware and protocols used to perform the interactions between service components involved in the service consumer and service provider interactions was popularized in literature, architectures with encapsulated Web services interfaces have not been widely implemented. One of the primary reasons for this situation is that, although several mechanisms for dynamic binding have been designed to meet this limitation, considerable misconceptions about actual techniques associated with dynamic binding still exist.


Anatomy of Web services invocation

Volumes have been written about one of the most important conceptual notions of SOA -- service interfaces (in other words, invoked functions) and how they should be located and invoked at run-time. In other words, Web service invocation ought to be of late binding. It is necessary to pin-point that Web services invocation is engineered as the traditional method of Remote Procedure Call (RPC) that allows one application to invoke a function published by a second application. RPC works as follows (as depicted in Figure 1):

  • The function from the invoked application appears as a local function to the invoking application.
  • Localization transparency is achieved through providing a function stub to the invoking application.
  • The function stub, when called, accesses a middleware layer that transports the call and its associated data to the invoked application.

Figure 1. RPC-based Web services invocations
RPC-based Web services invocations

In the case of Web services, the generation of stub code is typically automated and is based on consuming an interface description of the invoked function, expressed through Web Services Description Language (WSDL), and creating the stub shell. Moreover, typically, the stubs are generated during the coding stage of application development because the stubs are predominantly called directly from the application code, and, as a result, have to be resolved at compile time (in other words, the so-called early binding).

Most application developers, and mainly Java developers, believe that such usage of stubs is a necessity because it is not clear to them how to handle complex data types returned from the invoked service. Sure, the data coming from the invoked application function must be separated from the SOAP message, and, when using Java, it is obligatory to have a corresponding (and compatible) class that implements the serializable interface. Obviously, the same stub generation tool is used to generate those required classes.

The first moral in this paper is that, broadly speaking, as often it is the case, automation is a "two-faced" story. On the one side, automation allowed a speedy adaptation of Web services in the industry because, with automation tools, the activities of most application developers do not occur at the level of WSDL itself. On the other side, unfortunately, the marketplace has yet to yield a predominate tool that allows you to choose which form of invocation is needed -- explicit stubbing (the stubs are resolved at compile time) or implicit stubbing (the stubs are resolved at run-time). Implicit stubbing is often referred to as stubless invocation of Web services.

Most Java-based Web services tools provide some capability for late, run-time binding to Web services interfaces. Two examples include the Glue product from webMethods or the JAX-RPC package from the J2EE Web Services Developer toolkit. However, such attempts are mostly work-arounds that result in software that is deeply ingrained with vendor-specific logic. Moreover, the dependency on the vendor-specific code is not the only problem. In many cases, vendor proprietary solutions are creating substantial manageability and maintainability issues.

Let?s re-iterate the cited points by reviewing, for example, the webMethods Glue approach for late binding. These days, Glue is a widely used tool. It might seem that Glue is doing everything right, since it is using a proxy for the Web services interface. However, just using a proxy by itself does not eliminate the problems. The following Java code snippets illustrate a typical negative situation in their case:


Listing 1. lateBindingGlue
				
public class lateBindingGlue 
{ 
public Document serviceInvocationGlue() 
throws Throwable { 
String wsdlName = "http://?../Service.wsdl?WSDL"; 
String operation = "??.."; 
String args[] = {  }; 
/* first, Glue creates a SOAP interceptor */ 
SOAPInterceptor responseHandler = new SOAPInterceptor(); 
/* second, Glue registers the SOAP interceptor to catch incoming responses */ 
ApplicationContext.addInboundSoapResponseInterceptor( 
(ISOAPInterceptor)responseHandler ); 
try { 
/* third, Glue gets a proxy to the Web service through its WSDL */ 
IProxy proxy = Registry.bind( wsdlName ); 
/* here, service's operation is invoked through a proxy */ 
proxy.invoke( operation, args ); 
} catch( java.rmi.UnmarshalException e ) { 
// Glue is catching the UnmarshalException that is perfectly expected 
} 
/* forth, Glue generates an XML document containing the SOAP body, */
/* and passes the whole document for parsing  */ 
 return new Document( responseHandler.getResponse() ); 
} 
} 
class SOAPInterceptor implements ISOAPInterceptor { 
private Element soapBody; 
public void intercept( SOAPMessage message, 
Context messageContext ) { 
try { 
soapBody = message.getBody(); 
} 
catch( Exception e ){ 
 System.err.println( e.toString()); 
} 
} 
public Element getResponse(){ 
 return soapBody; 
} 
} 
       

You can learn a number of things from the example above. First, it is clear that the code introduced by Glue is a two-layer code. One layer "wipes out" the negative effect of SOAP-separating by intercepting this particular application exception using Java exception handling and a specialized component -- SOAP interceptor. The second layer delivers results of the service invocation to the invoking application as a whole XML document. Then, the XML document is parsed out using something like a DOM structure. Evidently, such a multi-tiered approach can?t be ported "safely" to other Web services tools. But, more importantly, although the technique of intercepting application exceptions is well-known, is it really appropriate in case of Web services invocations?

Generally speaking, exceptions are an extremely useful feature of the Java language, especially as a means to have an easy way of detecting locations of erroneous, unplanned programming actions like using a null pointer. Using this technique as a planned action of control instead of writing a piece of if-then logic that tests whether a pointer actually was null significantly complicates manageability and maintainability of the code. Clearly, SOAP separating is not the same as a null pointer. You can?t test it out with a simple if-then code. Nevertheless, the negative affect is the same -- throwing an exception and catching it is a very expensive technique, especially if you need to obtain a rate of thousands of Web services invocations per second. In other words, run-time exceptions should be reserved for the unexpected cases exclusively as a "line of defense" against software bugs.

I could go on and point out many more reasons why the SOAP-separating issue should not be handled through a Java Exception API. But, let?s move on to another issue highlighted by the cited example. Since the invoking application needs to parse the XML directly, it has to have access to the XSD definition for the response message, and that, in turn, must be part of the provided WSDL file (and consumed by the stub-generating tool). The end result here is that even some form of late binding exists in the cited example; the tight coupling between Web services partners is still present. In other words, the client of a Web service must have access to the WSDL file, and, in most cases, it has to be a complete (not a partial) WSDL file.

There is one more issue that relates to using JAX-RPC APIs. It is very important to remember that in Java language, since data types in XML schema do not map exactly to the data types in the Java language, the service endpoint interface generated from the WSDL at the invoked application's side could be different from the interface that the JAX-RPC compiler generated the WSDL from, at the invoking application?s side. Also, the generated service interface will depend on which SOAP encoding style is used. For example, using the document-literal encoding imposes wrapped versions of each method class, and that, in turn, creates additional manageability and maintainability issues.


Free application logic from the burden of Web services programming artifacts: Best practice design patterns

With all that was said so far, you can easily think in terms of using any kind of dynamic invocation techniques (such as dynamic proxies for late binding) like, unfortunately, many developers do. One of the most common misconceptions in the area of Web services invocations is that the benefits of using dynamic invocations instead of generated static stubs are not so great, and it's probably best to stick with generated stubs as the primary method for Web service invocations.

At last, our discussion has arrived at the second, and the most important, moral in this paper -- it is not actual programming techniques that ensure that there are no depencies on the service programming artifacts (like WSDL), but instead, the value is in high-level design. This is necessary for reaping real benefits of SOA because as the service evolves, the service consumer code won't be impacted at all. In spite of everything, SOA-based applications have to be more concentrated on providing resilient architecture and less concentrated on the common communication protocols, such as SOAP. The boundary between services and applications should not be rigid. An application to one end-user can be viewed as a service to another end-user. The entire enterprise IT environment should be designed to be highly modular, allowing developers to pick and choose a collection of services and applications that suits their needs.

In a good SOA design, the application logic of the Web services consumer can be completely decoupled from the service artifacts using two fundamental architectural principles:

  • "Stay adaptable" -- create a way to support multiple interface inheritance (in theory, this would be unlimited) while exploiting a limited number of concrete implementations (in object-oriented design, it's often the case that an anonymous inner class is used as the object adapter, allowing customization of behavior with respect to the context, actually, of being embedded within a subclass).
  • "Use the so-called Hollywood principle: Don't call us, we'll call you" -- the Web services invocation model should only specify discovery through the dynamic proxy framework and assume that the client has no prior knowledge of the services; therefore, Web services providers should be able advertise their services in XML documents at run-time by asserting stub-like information into those documents. That actually provides a way to enable invocation in addition to discovery (in other words, well beyond UDDI), in an open and extensible way.

As a result, the ultimate design goal is providing to the Web services consumer application a set of classes that make up a reusable adapter layer engineered according to the principles cited above. Such an adapter layer encapsulates code that uses the stub and its generated classes. The public API of the adapter does not expose any of the stub classes; it instead maps them to classes that the Web service consumer application understands.

To achieve the maximum level of flexibility and extensibility for the adapter, the overall class structure of the adapter is constructed by using a composition of the following design patterns:

  • Dynamic Proxy is a structural pattern that defines a set of classes and method invocations which are dispatched to an invocation handler that is specified when the dynamic proxy classes are generated. Using dynamic proxies is the key to simulating multiple implementation inheritance within Java programming. With dynamic proxies, a custom InvocationHandler is constructed with a set of classes that represent the superclasses of the subclass to be synthesized; the interface(s) of that subclass will be the union of the interfaces that these superclasses implement.
  • Adapter is a structural pattern that influences the creation of a class hierarchy (as inheritance hierarchies) by decoupling an abstraction from the implementation so the two can vary independently. Such decoupling avoids a permanent binding between an abstraction and its implementation, which allows the implementation to be selected at run-time. The invoking application's class, which acts as the Web services client, deals only with the abstraction.
  • Service Configurator is a behavioral pattern that lets you alter the capabilities of the adapter over time, and to add or remove additional functionalities, thus changing the specification of the invocation framework. For example, if a Web services provider brings a new protocol (such as SOAP over RMI) it would need only to "advertise" the new transport capabilities. Therefore, the adapter instantiates this new service capability and any implementation of this invocation functionality. Using some meta data representation as a result, there must be a way of referring to an interface that provides a class of functionality independent of particular specification and implementation.
  • Factory Method is a structural pattern that lets a class defer instantiation to subclasses. In the case of Web services invocations, both the local and remote implementation classes have to be subclasses in order to realize their service-specific implementations. The invoking application that wants to access the service will get a handle to this factory and makes a call to the service. Therefore, the factory encapsulates the knowledge of which implementation it has to use to access the service, depending on the information from the Service Configurator.
  • Decorator is a structural pattern that defines a wrapper for caching, publishing, and exchanging service assertions used for connecting to appropriate services and re-utilizing proxy classes. By using the Decorator pattern, it is simple to separate the code that performs invocations from the code that provides caching itself.

Key implementation guidelines

Figure 2 illustrates the conceptual model of a "Hollywood"-type adapter, engineered using the above cited design principles. Figure 3 is a sequence diagram that shows essential interactions when using the adapter.


Figure 2. Conceptual model of the Web services adapter
Conceptual model of the Web services adapter

Figure 3. Sequence diagram involving the Web services adapter
Sequence diagram involving the Web services adapter

Creating a service using the adapter includes creating a service class with the service descriptor that represents the SOAP service. This descriptor is included as a member of the service class. The service class is deployed as a SOAP service, for example, using Axis from Open Source Foundation.

The service descriptor can contain:

  • Class Name: an element containing the fully qualified class name of the service implementation.
  • Name: an element containing the name for the service.
  • Version: an element containing the version number of the service.
  • Creator: an element containing the name of the creator of the service.
  • AssertURI: an element containing the URI that points to an XML document that includes service assertions (specifications).
  • Desc: an element containing a description of the service.

Now, a couple notes about Axis. Using Axis as a SOAP engine is very helpful for implementing loose coupling of Web services for the following reasons:

  1. Because Axis defines a message-processing node that can be packaged for use by both service requestors (clients) and service providers (servers), the adapter can use a message processing node to deploy the SOAP service using a deployment descriptor as a Web Services Deployment Descriptor (WSDD).
  2. Services in Axis are typically instances of the SOAP service, which could contain the request and response chains, but must contain a provider that is the actual service class. As a result, the processing of the SOAP service is done by passing a message context to the message-processing node.
  3. The Axis client-side processing can be used to construct a call object by creating an instance of the call factory with the service descriptor and the XML assertion document, which contains the service details. The properties of the call object are set to use the associated target service. The call object is then created by invoking the Service.createCall factory method. Once the call is set-up, the Call.SetOperation is specified with the method name to be invoked. Then, Call.invoke is called with the associated request message, which drives the AxisClient.invoke, processes the resultant message context, and returns the response message to the client.

In the design of the adapter, special considerations have to be given to performance. Using dynamic proxy classes has performance implications. Direct method calls on objects are faster than method calls on proxy classes. However, it should not be a choice between robust architecture and performance. This is why the presented adapter implements caching wrappers. With caching, the performance difference between using static stubs and adapter-based solutions is fairly minor. In terms of how to implement caching, one of the potential solutions -- memoization -- has to be mentioned. Memoization is a technique widely used in functional programming languages like Lisp, Python, and Perl for giving functions a memory of previously computed values. Memoizing a function adds a transparent caching wrapper to the function, so that function values that have already been obtained are returned from a cache rather than being rebuilt each time. Memoization can provide significant performance gains for dynamic proxy calls.

Wrapping up our discussion about the adapter described here, it is very important to highlight that such a design allows for supporting both local and remote service implementations. A service class that will be local to the adapter and the remote Web service are becoming substitutes for each other, because both a service class and a proxy class to access the remote Web service implement the same interface. The local service class will implement the single method getFunction() similar to the Web services remote implementation returning the result of a function. The following Java snippets illustrate this point further:


Listing 2. Local service class and its interfaces
				
public class LocalServiceImpl implements IService
{
/* get the service results */
public ?.. getFunction() 
{
???..
return ????;
}
}


The proxy class that implements the IService takes a single method getFunction(), but overrides the method with the code that needed to access the remote Web service. This code represents the proxy code needed to access any Web service deployed within the adapter.


Listing 3. Remote Service
				

public class RemoteServiceImpl implements IService 
{
/* get the service outputs */
/* outputs are from the web service running in the location */
/* mentioned in the serviceURI */

public ?.. getFunction()
{
adapter.examples.Function service = null;
String serviceURI = "http://??../Function/";
String wsdlURI = "http://localhost:8080/Function/";
Try
{
// init the lookup
WebServiceLookup lookup = (WebServiceLookup)
???.
// get the instance of the Web services interface
// from the lookup
service = (adapter.examples.Function)
lookup.lookup(wsdlURI,
adapter.examples.Function.class,
serviceURI);
}
catch (Exception e) 
{
e.printStackTrace();
}
// now, call the methods on your Web services interface
return service.getFunction();
}



Conclusion

Clearly, using static stubs and early binding of Web services is the easiest form of Web services invocations. But with simplicity come significant drawbacks. Unlike a tightly coupled approach, using the adapter approach presented in this paper leaves you with Web services invocation code that is highly reusable and configurable. Because Web services invocations are all channeled through a common adapter in which service descriptors are deployed, you could decide dynamically what service is invoked -- doing this in the deployed code or even at run-time.

Also, organizations that might look for a commercial product as an alternative to custom solutions for loosely coupled invocations of Web services should investigate Enterprise Service Bus (ESB) technologies (see Resources) that offer options similar to the capabilities cited above for connecting Web services-enabled applications within and across enterprises, with a set of features enabling management and monitoring of interactions between connected applications.


Resources

About the author

Mark Davydov

Mark M. Davydov, Ph.D., is an internationally-known expert in software engineering and systems architecture, including SOA. Dr. Davydov is the author of numerous highly acclaimed articles in computer-related publications. His 2001 book Corporate Portals and e-Business Integration -- A Manager's Guide, McGraw-Hill Professional Publishing, introduced many ideas that influenced the progression of Service-Oriented Architecture and the Web services model.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and Web services
ArticleID=82275
ArticleTitle=SOA adventures, Part 1: Ease Web services invocation with dynamic decoupling
publish-date=04292005
author1-email=markdavydov@netscape.net
author1-email-cc=

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Try IBM PureSystems. No charge.

Special offers