HomeDigital EditionSys-Con RadioSearch Web Services Cd
B2B Beginning WS Business Process Management Case Studies Content Management Distributing Computing e-Business Electronic Data Interchange Enterprise Industry Insight Integration Interviews Java & Web Services .NET Portal Product Reviews Scalability & Performance Security SOAP Source Code UDDI Wireless WS Standards WS Tips & Techniques WSDL WS Editorials XML

Source Code for this article

Why is it so hard to build applications that integrate enterprise systems? One problem is that the platforms that integrate diverse systems are more difficult to learn than they need to be. Another problem is that even after integrated applications are built and working, the systems are brittle, hard to change, and expensive to maintain.

It should be easier.

This article is about Java Web Services (JWS), a standard under development that simplifies these problems. The JWS standard makes it possible to put together more robust, less brittle, integrated systems using Web services standards, including SOAP and WSDL, yet without requiring you to learn any complicated interfaces. As a result, JWS has broad appeal to sophisticated enterprise Java developers as well as programmers who don't even know Java.

What is JWS? This article introduces the standard, provides several examples demonstrating its capabilities, and discusses how to use JWS to build robust solutions.

A Simple Example
First, an example:

class HelloWorld {
/** @jws:operation */
public String hello(String name) {
return "Hello, " + name;
}
}

This simple program is a complete Web service in JWS. No kidding!

To use it, just save it as a file called HelloWorld.jws in a Web application directory on a JWS-enabled J2EE Web server, just as you would save a JSP page. The JWS server performs the necessary code generation, compilation, and deployment steps to take the JWS file and provide a fully functional Web service. Your Web service is immediately made available at http:// myserver/myapp/HelloWorld.jws (where myserver and myapp are the names of your server and application). You can then exchange standard SOAP messages with the Web service, and you can use a Web browser to test your Web service with an automatically generated testing UI. Additionally, the JWS server generates a downloadable WSDL contract describing the precise wire format supported by your service, some Java code to be used by clients, and so on.

Modify the JWS file a bit, and the Web service is automatically updated, with new WSDL contracts, the ability to accept new SOAP messages, etc. Building a Web service with JWS is as quick and simple as working with JSP.

What's going on in the "Hello World" example?

A JWS file is an ordinary, compilable Java file with some special Javadoc annotations in the comment blocks. The @jws:operation annotation is not an ordinary comment just for documentation. It tells the JWS container that the hello method is supposed to be an exposed operation of the Web service that should be made accessible via SOAP and described through WSDL. Removing the @jws:operation annotation would remove the method from the external public contract of the Web service, making it a private internal method. Adding @jws:operation to other methods would add those methods to the external contract.

There are about two dozen other @jws: annotations you can use to control aspects of your Web service without manually calling any complicated APIs. These annotations are designed to be easy to edit with visual modeling tools. For example, BEA WebLogic Workshop includes a Web services design tool that represents a Web service graphically. Modifying the graphical diagram in the tool modifies the values of the Javadoc annotations in the underlying JWS file. Also, since the annotations are just Javadoc comments, they are easy to work with in any standard source code editor.

Let's discuss some of these other annotations.

The Three Principles of Building Robust Web Services
In the first example, you can see how easy it is to expose Java logic as a Web service. So why isn't that all there is to JWS? Why would you need any other annotations beyond @jws: operation?

The answer is that building a robust Web service involves more than just exchanging SOAP messages and WSDL files. Web services technology is designed to allow durable, maintainable integration between systems. But simplistic use of new Web services technology results in fragile integration just as with older integration technologies. To exploit Web services fully, they need to be engineered with the following three principles in mind:

1. Loose coupling: Interfaces must be separated from implementation.
2. Asynchronous operation: Distant systems must not block one another.
3. Rapid integration: Relevant data and applications must be easy to connect.

Loose Coupling
Anybody who has maintained a complex integrated system knows what it's like to be trapped by tight coupling: once the information flow between parts of the system becomes too intricate, it becomes nearly impossible ­ or at least, very expensive and risky ­ to change any single part of the system. One of the goals of Web services technology is to break the gridlock of tight coupling by providing tools to simplify, clarify, and rationalize the information flow between systems.

Robust, maintainable loose coupling is grounded in two bits of wisdom:

1. Public contracts and private implementations evolve on different schedules.
2. Chunky messages are easier to maintain than fine-grained call sequences.

With automatic WSDL and stub-generation tools (such as the capabilities that are built into basic JWS usage in the first example above), it is actually very easy to fall into the tight-coupling trap of programming public contracts in the same way as private implementations. Since automatic WSDL and stub-generation tools work by directly generating a message shape from a function signature (or vice versa), when you change your implementation code, it changes your public contract at the same time. These tools also make it easy to use numerous, fine-grained messages, but they don't make it as easy to deal with more maintainable coarse-grained messages. So using WSDL generators can be extremely brittle.

When you're concerned about maintainability, you need your tools to help you work with robust, chunky, public message contracts that can continue to work even when your underlying implementation changes.

Example of Loose Coupling:
XML Maps in JWS

Here's an example of loose coupling, using a feature of JWS called XML maps. Suppose you have a public message format that's designed for submitting purchase orders to an ordering system. Since the message format is public, it will have been designed to be coarse-grained, permanent, and evolvable. A sample message might look like the purchase order XML in Listing 1.

Implementation data structures, on the other hand, are tied to the particular details of the program and how it is implemented. For example, our implementation may track customers using only the customer number, not the name, and it may track ordered items using the catalog number, not the description. We might not even have any single data structure representing a purchase order; instead, we may just pass around arrays of line items.

Listing 2 shows a simple JWS example that adapts between our public purchase order contract and our private purchase order implementation.

The Javadoc comment annotating the submitOrder method in Listing 2 includes the following code:

*  @jws:parameter-xml xml-map::
*   <purchaseOrder>
*   <customerInformation>
*    <customerNumber>{customerNo}</customerNumber>

(In the full listing the rest of the XML is available.) The XML message inside the annotation is an XML map. It is a picture of the relevant parts of the public input message, and it contains curly braces that correspond to places in the XML message where data should be bound to a Java parameter or field. As you can see, when you use an XML map in @jws:parameter-xml, you can see and control the public message format that is consumed by our private implementation.

By allowing you to make the message format explicit, XML maps provide a key advantage over the use of traditional WSDL generation or proxy generation techniques. With ordinary WSDL generation, you don't control the public message format explicitly: instead, when you need to change your implementation signatures, the message format is automatically changed by the system. If your WSDL generator happens to choose a message format in a way that breaks compatibility, then it will have broken your integration.

Additionally, XML maps make it easy to evolve either the implementation or the public contract without breaking the system. For example, if we decide to enhance the Java implementation by validating prices and descriptions of catalog items, we could add additional fields to our private LineItems data structure in Java and add additional lines to the input XML map to extract information from the <price> and <description> tags that are present in the public message. Or if we want to evolve the public contract by adding additional tags in the <customerInformation> section, we could do that without perturbing the existing implementations that don't need to use the extra information. XML maps allow the public contract to evolve on a different schedule from the private implementation.

XML maps have other features that we haven't shown here: they can be used for output as well as input, and they can deal with more complicated data structures or even invoke procedural code to do nontrivial data transformations when needed.

XML maps in JWS make it very easy to implement loosely coupled systems. They are a simple tool that maintains and maps between a separate public Web services contract and private implementation.

Asynchronous Operation
When integrating high-volume systems, your goal is to make the most of available bandwidth and maximize overall throughput without sacrificing latency and reliability. To achieve this, it is extremely important that the integrated systems cooperate asynchronously. That is, distant systems should never block on each other waiting for a response.

This principle is important because when you integrate databases, Web servers, and enterprise application servers, some systems are always much faster or much slower to respond than others. There are latency differences because some systems are transactional and others aren't. Other latency differences arise when some systems work in a batch and others don't, or when some systems need to interact with people and others don't.

With different systems operating on different time scales, the challenge is to be able to manage conversations between systems without allowing any one system to block another. If a client blocks waiting for a response, the latency and reliability of the client will be tied to the latency and reliability of the server: if the server is slow, the client will be slow; and if the server is down, the client will be down. In other words, blocking clients by forcing them to wait for servers makes your whole system as slow and as unreliable as your weakest link. Blocking also wastes valuable threads, connections, and other cached resources by locking them up while idle: blocking in the face of latency makes it impossible to scale up overall throughput.

There are a few basic techniques that can be used to manage conversations and avoid blocking:

  • Queuing: Instead of processing a message right away, queue it to process later.
  • Polling: The client sends correlated messages to check status later.
  • Callbacks: The server sends correlated messages back to the client to report status.

    Since queues can continue to operate even when senders or listeners slow down or stop, queuing is useful for smoothing out spikes in load, and for improving the overall reliability of a system that may have unreliable components. JWS provides several queuing mechanisms, including buffering for HTTP messages and binding Web services directly to a JMS queue.

    JWS Web services can be bound directly to JMS message queues for use in transporting either SOAP messages or unenveloped XML messages. That is, JWS can treat a J2EE Java Message Service queue as an additional protocol for Web services transport ­ and one with all the desirable properties of a reliable message queue. This capability enables JWS developers to easily leverage the J2EE mechanism for asynchronous messaging.

    Servers that avoid blocking the client need a way to get information to the client later. This is done either by sending callbacks to the original client, or by letting the client poll for information. In general, callbacks have more desirable properties than polling, since a client doesn't waste time looking for a result that isn't ready yet. However, callbacks require that clients act as servers, and if this isn't possible you must use polling.

    Example of Asynchrony: Queuing and Callbacks in JWS
    Let's take a look at an example that includes an asynchronous callback that gets some credit information for a specific purchase.

    Here's what we want to do when a credit check arrives:

    1. Quickly queue the message and release the client.
    2. Without blocking the client, determine whether the credit charge is valid.
    3. Send a callback indicating whether the charge is valid or not.
    This implementation shouldn't tie up clients even if there is a heavy load and the server needs a lot of time to validate messages.

    This is a simple two-message conversation with one inbound call and one callback. The conversation could easily be extended to include subsequent messages to be exchanged over a period of time, such as "reserve charge," "commit charge," and "expire reservation."

    Listing 3 has the JWS code example to implement the asynchronous conversation. We've already seen how to define incoming operations, but how are callbacks defined? The JWS file defines an inner interface called Callback whose methods are the callback methods. In the main JWS class, a member variable called callback, which is an instance of the inner Callback interface, is also declared.

    The following definition declares the callback operation for the service (the code below is valid but simplified; if you look at the full listing, you'll see that it adds some additional annotations for extra functionality):

    interface Callback {
    /** @jws:conversation phase=finish */
    void sendResult(boolean allowed);
    }

    In order to actually send callbacks, we first define the following field in the main JWS class:

    Callback callback;

    We then call code such as the following in one of the Web service methods:

    callback.sendResult(true);

    The above call sends a callback message back to the client associated with the proper conversation ID.

    Notice that the callback object is automatically hooked up to the correct client on your behalf: one of the features of JWS is to automatically manage callback plumbing. Before any of your code runs, a callback object supporting your Callback interface is created, initialized, and hooked up so that calling it will send messages back to the proper client instance. What would ordinarily be a lot of manual setup is all done automatically.

    In the example, we've queued incoming messages using JMS as a transport for SOAP messages. This code has a couple relevant annotations above the declaration of the processCharge method:

    * @jws:conversation phase=start
    * @jws:protocol jms-soap=true http-soap=false

    The @jws:conversation phase=start annotation indicates that sending the message starts a conversation that consists of a sequence of messages sent over time. When you send a message to a method marked as a start message, the client and server establish a new conversation ID that it used to correlate all the messages in the sequence.

    The @jws:protocol jms-soap=true annotation binds the Web service to a JMS queue. In addition to being able to provide Web services on HTTP, JWS includes the capability to provide Web services on a JMS queue. (It can also be configured to handle raw XML messages that aren't in SOAP envelopes.) When a client message is sent to the method that is listening on JMS, it doesn't wait for the server to do any work. The message will be processed later using a message-driven bean that consumes messages at a rate determined by how quickly the server can do the credit-validation work.

    In this example, we've seen how JWS supports JMS as a transport for binding Web services messages. JWS also provides support for a JMS control that enables a JWS file to communicate with existing messaging systems (e.g., an integration broker) using either the point-to-point or publish/subscribe paradigms. The JMS control is just one of a collection of controls that can be used to integrate a JWS service with other enterprise resources.

    Rapid Integration
    One of the goals of JWS is to make it easy to build Web services that connect to other resources, such as databases, other Web services, EJBs, or enterprise application logic. To facilitate this, JWS supports a clever mechanism called JWS Controls.

    JWS Controls provide a standard programming framework that allows developers to communicate with various heterogeneous resources. They eliminate the need to learn a set of sophisticated APIs to access and integrate with a range of different types of enterprise applications and services. Each JWS Control has default behavior associated with it, and using the JWS annotation syntax you can easily customize control properties and override default behavior. Since JWS Controls hide the complex class hierarchies and APIs normally used to access enterprise resources, they allow any developer who understands basic programming to fully leverage these resources.

    The example in Listing 3 uses a database control to implement its logic. The control field declaration in the example looks like this:

    /**
    * @jws:control
    */
    CustomerCreditDatabase creditDB;

    In a JWS file, any field that is annotated as a @jws:control is automatically managed. So the details of how the creditDB control object is created, hooked up, and shut down are left to the system. For example, the control automatically manages the database connection and transaction context for you. You don't have to worry about preparing the statement, closing the database connection, dealing with result sets, or other details of JDBC; the database control and the JWS container will make sure the database dance is done correctly. As a JWS programmer, all you need to do is call the control within your Web services methods, as follows

    double currentLimit;
    currentLimit = creditDB.getLimit(customerNo);

    Use of other controls is just as simple: a JWS programmer can access most enterprise resources using simple, procedural Java.

    Much of the magic of controls is in how they are customized. Developers and vendors can define their own controls. For example, the BEA WebLogic Workshop JWS control package includes the following base controls:

    • ServiceControl: A highly customizable Web service proxy
    • DatabaseControl: Provides JDBC functionality
    • EJBControl: Provides access to session and entity beans
    • TimerControl: Can wake services at specified intervals
    • JMSControl: For listening, sending, and pub/sub on JMS queues and topics
    • ApplicationViewControl: Provides access to the J2EE connector architecture
    Customized controls, such as Customer CreditDatabase, that derive from base controls are defined using a .ctrl file that defines the desired control interface in Java along with some Javadoc annotations. The annotations make customization easy, and they allow the details of the implementation of the customized control to be left up to the base control.

    Listing 4 (Listings 4 thru 6 can be found below) shows the Customer Credit Database control, which is based on the Database Control. Since the Database Control has been customized using annotations such as @jws:sql, all the ordinary JDBC plumbing code for communicating with a database is handled automatically. While the customized control is concise and easy to maintain, its automatic implementation is also meticulously correct.

    Another interesting example, shown in Listing 5, is a ChargeCheckjControl that can be used to access the service from Listing 3 remotely. It is based on the Service Control. The JWS Web Service Control is a highly customizable Web service proxy. The customization allows you to explicitly manage aspects of the proxy, such as the XML messages, shapes and buffering.

    The JWS Control model is designed to be extensible in the future, so new types of controls can be added over time. As you can see, controls can simplify integration problems and reduce the cost of development and maintenance of a Web service.

    Putting it Together
    Listing 6 shows one final example: here, we've modified OrderingSystem. jws to operate asynchronously using a conversational sequence of messages. We've also chained the use of the asynchronous Web service ChargeCh eck.jws through the ChargeCheck Control object called chargeCheck.

    In this final example you can see how the JWS callback model works between two Web services (one Web service generating the callback and the other receiving the callback): conversational callbacks from charg eCheck are automatically routed to a callback handler method in Ordering System.jws based on a simple naming convention. Again, the JWS container is responsible for doing all the callback routing and plumbing for you; you just supply a method that follows the proper naming convention. The callback handler to listen for sendResult events from chargeCheck is written as follows:

    void chargeCheck_sendResult(boolean ok)
    {
    if (ok) callback.sendAck();
    else callback.sendError("Not enough credit.");
    }

    When the callback handler gets a message indicating whether or not the credit has been approved, the code sends a callback to the original client. The technique of using asynchronous callbacks allows long chains of computations to be put together without blocking the original client.

    Summary
    JWS makes it easy to solve knotty integration problems using a simple service-oriented architecture. In a couple hundred lines of code, we have defined two Web services and two custom JWS Controls. The Web services exchange SOAP messages over HTTP as well as JMS, and they expose WSDL contracts. They manage asynchrony with sequences of conversational messages, they interact with a database, they use queues for handling load, and they use custom XML serialization to improve robustness of the wire contract. And the code is simple.

  • By annotating ordinary Java code with special JWS Javadoc comments, we can use advanced Web services and J2EE technology without using complicated APIs.
  • JWS supports automatically generated XML message shapes, but it also facilitates loose coupling by allowing the XML messages to be specified explicitly.
  • JWS facilitates asynchronous integration by managing conversational sequences of messages and callbacks, and by providing message buffering.
  • The JWS Control model allows us to easily connect to external resources such as databases, enterprise applications, EJBs, and Web services.

    Since JWS is ordinary Java code running in a J2EE container, you always have access to the full power of standard J2EE functionality whenever it is needed. But JWS doesn't require use of complicated XML, J2EE, or Web services APIs. The power and robustness of JWS is fully accessible through Java code that is simple, short, and easy to write and maintain.

    Author Bio
    David Bau is a program manager at BEA Systems. He is one of the principal designers of JWS and WebLogic Workshop. David previously worked for Crossgain Corporation and Microsoft, and has helped to develop several successful software platforms, including Internet Explorer and ASP.NET. david.bau@bea.com

    JWS: Web Services in Java, by David Bau
    WSJ Vol 02 Issue 04 - pg.40

    	
    
    
    Listing 1: An example purchase order
    
    (SOAP envelope not shown)
    <purchaseOrder>
      <customerInformation>
        <customerName>Acme Manufacturing
         Inc</customerName>
        <customerNumber>332</customerNumber>
      </customerInformation>
      <basket>
        <lineitem>
          <description>High Temperature
           Hex Bolt #66</description>
          <price>0.47</price>
          <catalogNumber>17466</catalogNumber>
          <quantity>220</quantity>
        </lineitem>
        <lineitem>
          <description>High Temperature
           Hex Nut #66</description>
          <price>0.47</price>
          <catalogNumber>18266</catalogNumber>
          <quantity>200</quantity>
        </lineitem>
      </basket>
    </purchaseOrder>
    
    
    Listing 2: OrderingSystem.jws Web Service class OrderingSystem
    
    {
        /**
         * @jws:operation
         * @jws:parameter-xml xml-map::
         * <purchaseOrder>
         *   <customerInformation>
         *     <customerNumber>{customerNo}</customerNumber>
         *   </customerInformation>
         *   <basket>
         *     <lineitem xm:multiple="item in items">
         *       <catalogNumber>{item.catNumber}</catalogNumber>
         *       <quantity>{item.quantity}</quantity>
         *     </lineitem>
         *   </basket>
         * </purchaseOrder>
         * ::
         */
        public void submitOrder(int
         customerNo, LineItems[] items)
        {
            // logic omitted for now
        }
    
        static public class LineItems
        {
             public int catNumber;
             public int quantity;
        }
    }
    
    
    
    Listing 3: ChargeCheck.jws Web Service class ChargeCheck
    
    {
        Callback callback;
    
        /**
         * @jws:control
         */
        CustomerCreditDatabase creditDB;
    
        /**
         * @jws:operation
         * @jws:conversation phase=start
         * @jws:protocol jms-soap=true
         * @jws:parameter-xml xml-map::
         *     <request-charge>
         *       <customer>{customerNo}</customer>
         *       <amount>{amount}</amount>
         *     </request-charge>
         * ::
         */
        public void processCharge
         (int customerNo, double amount)
        {
            double currentLimit;
            currentLimit = creditDB.
             getLimit(customerNo);
            if (amount < currentLimit)
            {
               creditDB.setLimit(customerNo,
                        currentLimit ­ amount);
               callback.sendResult(true);
            }
            else
            {
               callback.sendResult(false);
            }
        }
    
        interface Callback
        {
            /**
             * @jws:conversation
                phase=finish
             * @jws:protocol
                 jms-soap=true
             * @jws:parameter-xml
                xml-map::
             *     <charge allow="{allowed}"/>
             * ::
             */
            void sendResult
             (boolean allowed);
        }
    }
    
    Listing 4: CustomerCreditDatabase.ctrl
    
    import weblogic.jws.control.DatabaseControl;
    
    /** @jws:connection datasource-jndi-name="db.customercredit" */
    interface CustomerCreditDatabase extends DatabaseControl
    {
        /**
         * @jws:sql statement=
         *      "select curlimit from credit where custno = {num}"
         */
        double getLimit(int num);
    
        /**
         * @jws:sql statement=
         *      "update credit set curlimit={amt} where custno = {num}"
         */
        double setLimit(int num, double amount);
    }
    
    
    Listing 5: ChargeCheckControl.ctrl
    
    import weblogic.jws.control.ServiceControl;
    
    /**
     * @jws:location http-url="http://checkserver/ChargeCheck.jws"
     * @jws:protocol jms-soap=true http-soap=false
     */
    interface ChargeCheckControl extends ServiceControl
    {
        /**
         * @jws:parameter-xml xml-map::
         *     <request-charge>
         *       <customer>{customerNo}</customer>
         *       <amount>{amount}</amount>
         *     </request-charge>
         * ::
         */
        public void processCharge(int customerNo, double amount);
    
        interface Callback
        {
            /**
             * @jws:parameter-xml xml-map::
             *     <charge allow="{allowed}"/>
             * ::
             */
            void sendResult(boolean allowed);
        }
    }
    
    
    Listing 6: OrderingSystem.jws (integrated)
    
    class OrderingSystem
    {
        Callback callback;
    
        /**
         * @jws:control
         */
        ChargeCheckControl chargeCheck;
    
        /**
         * @jws:operation
         * @jws:conversation phase=start
         * @jws:message-buffer enable=true
         * @jws:input xml-map::
         * <purchaseOrder>
         *   <customerInformation>
         *     <customerNumber>{customerNo}</customerNumber>
         *   </customerInformation>
         *   <basket>
         *     <lineitem xm:multiple="item in items">
         *       <catalogNumber>{item.catNumber}</catalogNumber>
         *       <quantity>{item.quantity}</quantity>
         *       <price>{item.price}</price>
         *     </lineitem>
         *   </basket>
         * </purchaseOrder>
         * ::
         */
        public void submitOrder(int customerNo, LineItems[] items)
        {
            double amount = 0.0;
            for (int i = 0; i < items.length; i++)
                amount += items[i].price;
            chargeCheck.processCharge(customerNo, amount);
        }
    
        void chargeCheck_sendResult(boolean ok)
        {
            if (ok) callback.sendAck();
            else callback.sendError("Not enough credit.");
        }
    
        static public class LineItems
        {
             public int catNumber;
             public int quantity;
             public int price;
        }
    
        interface Callback
        {
            /**
             * @jws:message-buffer enable=true retryDelay="3 min"
             * @jws:conversation phase=finish
             * @jws:output xml-map::
             * <acknowledgement>
             *    <message>Purchase Order is OK</message>
             * </acknowledgement>
             * ::
             */
            void sendAck();
    
            /**
             * @jws:message-buffer enable=true retryDelay="3 min"
             * @jws:conversation phase=finish
             * @jws:output xml-map::
             * <error>
             *    <problem>{message}</problem>
             * </error>
             * ::
             */
            void sendError(String problem);
        }
    }
    
    
    

    All Rights Reserved
    Copyright ©  2004 SYS-CON Media, Inc.

      E-mail: info@sys-con.com

    Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.