devxlogo

Build a SOAP-based Chat Application with Java Web Services, Part 2

Build a SOAP-based Chat Application with Java Web Services, Part 2

art 1 of this tutorial provided a light introduction to the world of Web services. You created a simple SOAP implementation of a Simple Chat server. You also created a client that consumes the chat server’s services. This second tutorial extends the original concept of the SimpleChat server with asynchronous messaging.

Asynchronous Invocation
For those new to distributed computing as well as Web services, asynchronous invocation is among the most valuable tools at a developer’s disposal when it comes to fine tuning and removing bottlenecks from the system. In the ChatService tutorial, the client application process sends its request to the service and waits for the response message before continuing. This type of synchronous behavior can result in high performance penalties if you’re expecting high messaging latency on your network system, either because of traffic congestion, call failures, or server-side delays in processing the response. A more efficient use of client side resources is to send a request and then move the client process along. Later, you can either poll for the response or handle it as a callback reduces traffic and makes. These and related approaches are called “asynchronous invocation.” For a more complete and very useful discussion of asynchronous invocation, see Jack Shirazi’s Java Performance Tuning.

In the context of the WASPJ framework, there are two levels of asynchrony, which are mutually orthogonal. The first one focuses on the client’s application logic, and is purely client-side efficiency driven. In this scenario, the client consumes a service’s business logic that has a long latency. The client doesn’t want to wait idle for the response to arrive. Instead, it wants to call the invocation framework in a non-blocking mode, letting the framework efficiently handle the long-running transaction and to then be notified when the result is ready. This dimension is called “API asynchrony”, because it is often established with a special non-blocking invocation APIs.

The second level focuses on the physical means of communication between the two parties, or, the “Transport asynchrony.” In the transport synchronous scenario, two parties access an open direct communication channel for exchange of messages. When using transport asynchrony, there’s no single channel for transmission of both the request and response messages. Instead, the user uses one connection (or some messaging service) to send the request. The service then processes the request, and uses another connection to send the response. A classical example of asynchronous transport communication is the exchange of emails. Transport asynchrony lessens the differences between how “client” and “server” are traditionally understood. The client itself must be able to behave like a server. It must have its own unique address where it listens for the (response) message to arrive.

In the end, these two aspects create four combinations. All of them have their use case. Sync/sync is obvious. Asynchronous API over synchronous transport can be used when it is affordable to keep the direct connection open for some (not exceedingly long) amount of time. Synchronous API over asynchronous transports is used when there’s no way to create a direct communication channel between the two parties (firewall, limited availability of one of the parties, missing physical direct link, etc.). Finally, the asynchronous API and asynchronous transport are used in the truly asynchronous loosely coupled environment, where the business transactions can take long time to complete. This demo is modeled in a fully asynchronous environment.

Sync/Async Advantages

 

API Sync

API Async

Trans. Sync

Simple and straight forward to implement

Saves Client resources but not network ones

Trans. Async

Useful when communication is unreliable or must be indirect (i.e. Firewall)

True loosely coupled solution where processes can be long running and traffic heavy

The Demo Application
The base line of this demo is the previous SimpleChatServer application. Please note that this demo isn’t focused on versioning of Web services and their APIs. Although versioning is a pretty exciting topic, its scope goes beyond this tutorial. This demo simply breaks backward compatibility with the old clients, and implement the service in a new Java package, using a new target namespace for the WSDL file. To do this, you need to create new clients (or modify the existing ones), but it avoids clashes with the existing SimpleChatServer.

A new method has been added to the IChatService interface in order to model long latency business logic in the chat server:

  ChatMessage[] getOrWaitforNewMessages(long afterId, long waitMillis)       throws NoMessageArrivedException;   

This method either returns messages whose id is larger than afterId (the newer ones) immediately, or waits until a new message is posted to the chat server. This is quite natural behavior for a chat server. The client doesn’t have to periodically poll for new messages, as in every fraction of second. Instead, it places its request, and gets back the result when new messages arrive.

This model does have its weak spots. For instance, I’ve added a maximum wait time parameter to the call. When no message arrives at the server within the specified amount of time, the NoMessageArrivedException is thrown and returned to the client. Despite the imperfection, this design is sufficient for this tutorial’s purposes. The source for the IChatService interface can be seen in Listing 1.

The new backend logic now contains the implementation of this new method. Hopefully, it’s simplicity compensates for other aspects. The source for this can be found in Listing 2. Please note that the service code doesn’t contain any code that would be specific to asynchronous invocation. The interesting stuff comes with the client.

Finding the Right Application Framework
You need a reliable, feature-rich platform for Web Services that lets you develop and deploy the asynchronous chat server and its client. In this demonstration, you’ll be using Systinet’s WASP (Web Applications and Services Platform) Server for Java.

The remaining part of the tutorial assumes that you have the WASP Developer for Eclipse 4.6 up and running. It internally contains a distribution of WASP Server for Java 4.6.1, which is a must for this tutorial. This tutorial will not work in WASP Developer 4.5.

Figure 1. Import sources.

Installing the Sources in Eclipse
Assuming that you have Eclipse with WASP Developer up and running, install the sample code. First of all, download the sources with the sample service code here.

  1. In Eclipse create a new project (File?>New?>Project…). Choose the Systinet Web Services/Web Service Project.
  2. In the following dialog enter the project name, AsyncChatServiceProject, and project location. Finish the wizard.
  3. From the File menu, choose the Import… command, and choose the Zip file method.
  4. In the following dialog choose the service code file downloaded earlier and click on the Finish button.

Your project should now contain the two packages with the source for the Asynchronous Chat Service. You can verify this by examining the Package Explore view.

Figure 2. The runner for the server.

Turning the Chat Server into a Web Service
There’s no surprise waiting here for those who visited the first tutorial. You publish the chat server implementation as a Web service using the WASP Server for Java runtime publishing API. With these two method calls, your Java class gets published as a Web Service:

public class ChatServerApp {     public static void main(String[] args) throws Exception {        Wasp.startServer("http://localhost:6060");        Registry.publish("/ChatServiceAsync/", ChatService.class);        System.out.println("Chat server V2 is up an running.")            }    }

The full source can be found in Listing 3

Build and Run the Chat Server
The chat server is now ready to run. After the build is complete (Project?>Rebuild All), you can start it. First, create a Web service runner. This runner ensures that the server has all the required libraries and that it is properly configured. To create the runner, select the ChatServerApp from the com.systinet.asyncchat.server.impl package and choose Run?>Run… from the IDE menu.

Figure 3. The output in the console.

The dialog that opens is the manager for all runners in the project. Double click on the WASP Java Application runner template. This creates a new runner called ChatServerApp. Always use this runner to run the ChatServerApp class. Press the Run button to start the chat server. You should see the console, normally at the bottom of the IDE window, with a message about a running server. If there’s any exception there, you either haven’t run the server through its runner, or another process is sitting at port 6060. Please make sure that no process is listening on this port and that you use the correct runner, and run the server one more time. It is essential to have this server running for the rest of the tutorial.

As a reminder, the server is up and running now. Its location is:

        http://localhost:6060/ChatServiceAsync/.

The location of the dynamically generated WSDL document is:

         http://localhost:6060/ChatServiceAsync/wsdl. 

The Client
Now that the server side is up and running, it’s time for the client. Instead of making a huge jump and having the asynchronous client talking to your server instantly, proceed with smaller steps. Start with a simple synchronous client, that will later evolve into a fully asynchronous beast.

Figure 4. The Web service wizard.

Step I: The Synchronous Client
Use WASP Developer’s capabilities to generate working client code from the Web service’s WSDL file. The wizard requires only that you specify the service’s WSDL description.

First, create a new package for the client. From the menu, choose the File?>New?>Package. In the New Package dialog enter the package name com.systinet.asyncchat.client, and press the Finish button. Now it’s time for the Web Service Wizard. Go to the newly created package, and from the menu choose File?>New?>Other…. The New dialog now opens. In the left hand bar, choose Systinet Web Services. A set of all available wizards is displayed in the right hand bar. Choose the “Web Service Client from Internet WSDL” item and proceed to the next window.

In the next window, make sure that the com.systinet.asyncchat.client package is selected, and proceed to the next one.Now, fill-in the address of the async chat service’s WSDL file. It’s:

http://localhost:6060/ChatServiceAsync/wsdl. 

Press the Next button.

The WSDL compiler utility lets you base your client on any of the service ports found in the WSDL file. You now have the chance to choose one of the services described in this WSDL file. Since the Chat Service has just one, simply press the Finish button. If the wizard asks you to overwrite an existing class, please say No (only the exception class should be existing in your project).

If you get an error message that the Web Service client cannot be created, you probably do not have the server running. Please return to the server, start it, and repeat the client generation steps.You now have created several files in two new packages:

  • com.systinet.asyncchat.client.iface
  • com.systinet.asyncchat.client.iface.struct.

The first package contains the interface definition, ChatService.java, and a support file used by the WASP framework ChatService.xmap. The second package contains additional structures that are used in the ChatService.java interfaceChatMessage.java and ChatPerson.java.

If you inspect the generated classes, they should be familiar to you. They are not exact copies of the original ChatService interface classes, but they exactly represent its API. The ChatService.java files contains a handful of previously unseen methods, like beginPostMessage or endPostMessage. These methods are used for the asynchronous invocation of the Web service. This step, however, focuses on the synchronous methods instead.

WASP Developer has generated a simple client template. This template is ready for extensions that will do the actual invocation of Web services. If you look at it, the main method contains this code:

    ChatService service;    String wsdlURI = "http://localhost:6060/ChatServiceAsync/wsdl";    String serviceURI = "http://localhost:6060/ChatServiceAsync/";    // lookup service    ServiceClient serviceClient = ServiceClient.create(wsdlURI);    serviceClient.setServiceURL(serviceURI);    service = ChatService)serviceClient.createProxy(ChatService.class);    // now, call the methods on your Web Service interface    //eg. service.getMessage();

This code creates a proxy that remotely calls the Web service and assigns it to the service object. For details, please check Part 1 of the tutorial. Let’s make the client call our new web service’s method synchronously. The source of the extended autogenerated code is in Listing 4.

Figure 5. The runner for the client.

Now it’s time to run the initial client. First, create a new runner. Select the ChatServiceClient class, and from the Menu, click the “Run?>Run ..” item. Double click on the WASP Java Application item. A new runner called ChatServiceClient is created and ready for use with the client. Always use this runner for running the client. Press the Run button to start the client.

What has just happened? The client first sends a message to the server. Then it reads all messages from the server. In the last step, it waits for new messages to arrive within ten seconds. As there are no new messages on the server, the timeout exception is thrown at the server side and transparently transferred and rethrown on the client side. If you manage to run the client again and run another one within ten seconds, the first one should print the message that the second one sends instead of throwing an exception. Intermezzo: SOAP Faults
Before you get to the async client, let’s make a short detour into the realm of exception propagation. You’ve just seen it in action. SOAP carries an abstraction for fault states on the server side. There’s a special message type in SOAP called a SOAP Fault that tells the client that something went terribly wrong on the server side. The description of SOAP faults can be placed into the service’s WSDL to tell the clients what kind of errors to expect.

The interface of the service was enhanced with a method getOrWaitforNewMessages, which throws your custom NoMessageArrivedException. When WASP deploys an application’s service classes as Web services, it stores information about its methods’ exceptions into the WSDL as SOAP Faults. When you create a client using the wizard, the wizard reads the SOAP Faults from the WSDL and generates the exception classes. In this instance, it generated the NoMessageArrivedException. During runtime, all the SOAP plumbing is handled by WASPJ. When an exception is raised in the service, the WASPJ server infrastructure sends back a SOAP Fault. The WASPJ client infrastructure receives the fault and raises the exception in the client code.

All together, the SOAP specification lets you transfer error states from the service to the client. WASP Server for Java’s smart SOAP runtime can map generic SOAP faults right into Java exceptions transparently, so you don’t have to be concerned about the plumbing at all. All you need to know is the service interface. Simply be ready for exceptions when they come.

Step II: Asynchronous API
The asynchronous API of any messaging runtime should let the client fire the request, forget about it, do other work, and either be notified when the response arrives or wait until it arrives. We are running into the realm of multithreaded applications, synchronization etc., which inevitably leads into more complicated code samples that are harder to read. I assume that your knowledge of Java also covers multithreaded programming.

The WASPJ Asynchronous invocation API provides two basic patterns of response arrival processing: callbacks (listeners) and polling. Callbacks are user-registered listeners, that are called by the framework when the response comes back from the server. The polling API lets the client code check whether the response has arrived already. I personally prefer callbacks (although there are use cases where polling is a better strategy), and so this tutorial shows the usage of callbacks.

Before jumping into the updated source code, let me present it in outline. You’ll initiate the asynchronous invocation of the service, and register a callback that gets called when the response is returned. Later on, before the response arrives, you’ll process other code?for simplicity, a loop that waits until the response arrives. Once it has the response, you’ll break the loop and finish the client process. Please note that the usage of the async API doesn’t say anything about the way the message is sent/received on the transport level at this moment. You’ll be using an async API over a sync transport for now.

The full listing of the updated client is in Listing 5 (See also ChatServiceClientAPIAsync.java in DEMO_HOME/src). Here’s a snippet of the main method of the client:

service = (ChatService)serviceClient.createProxy(ChatService.class);// now, call the methods on your Web Service interface// let's start with some synchronous calls, print the existing messages    long lastMessageId;    ChatMessage msgs[];    service.postMessage("Enter", "Sandman");    msgs = service.getAllMessages();    lastMessageId = msgs[msgs.length - 1].id;    printMessages(msgs);// now let's wait for another messages to arrive    System.out.println(">>> Client: Making the asynchronous call, " +         "waiting for response to arrive...");//1.      initiate the async invocation; //        read new messages from the server with timeout 10s    AsyncConversation async =        service.beginGetOrWaitforNewMessages(lastMessageId, 10000);//2.      setup the callback that will be called when the response arrives    MyCallbackClass callback = new MyCallbackClass(service);    async.setCallback(callback);//3.      the following dumb code demonstrates that //        the client keeps running//        my callback decides if it's been enough already    while (!finishedWork) {//The client now either sleeps (read: "does other important work");        System.out.println("    Client: ... zzzzZZZ ... " +                "doing other work ... zzZZ ...");        Thread.sleep(2000);        System.out.println("    Client: " +                "Sending another post to the server.");        service.postMessage("Wake Up!", "Sandman");    }    System.out.println("--- Client: All work done, exiting.");
  1. The method onResponse gets called whenever a response from the server arrives. In the first step, read the result from the service by calling the endGetOrWaitforNewMessages method on the service proxy. This method either returns the new messages or rethrows our service’s exception.
  2. When the response is processed, tell the framework not to wait for more messages to come. WASPJ is able to process more responses for a single asynchronous call. When finished, set the finishedWork to true to make the main client thread escape from the loop.

And that’s it! Run the client using the runner that you’ve already created and see the results:

# Thu Sep 18 19:38:26 EDT 2003 Sandman > Enter# Thu Sep 18 20:02:06 EDT 2003 Sandman > Enter>>> Client: Making the asynchronous call,      waiting for response to arrive...    Client: ... zzzzZZZ ... doing other work ... zzZZ ...    Client: Sending another post to the server.    Client: ... zzzzZZZ ... doing other work ... zzZZ ...<<< Callback: The asynchronous response from      the ChatService has arrived!# Thu Sep 18 20:02:08 EDT 2003 Sandman > Wake Up!--- Callback: Leaving!    Client: Sending another post to the server.--- Client: All work done, exiting.

The actual chat conversation may be different for you, depending on the number of times you already run your clients. As you can see, the client sends the async request to the server (the message that starts with >>>). Then, the server does other interesting work (zzzZZZ) and sends a new message to the server. At that moment, the response is returned from the server and sent to the callback (the <<< message). At the end, the callback sets finishedWork to true, closes the conversation and leaves. The client leaves the loop as soon as it gets the chance to do so, and exits.

This client code used the asynchronous API, but on the transport level, the call was still synchronous. The http connection opened by the client remained open all the time until the response was returned. The server has no notion about the client’s asynchronicity at all in this use case.

Step III: Asynchronous Transport
The Async API alone didn’t make any requirements on the server side. For it, the call was like any other ordinary synchronous call. Asynchronous transport requires the server to contain a logic that doesn’t try to send the response over the connection opened by the client. Instead, this logic closes the request connection immediately, and opens a new one to the client once the response message is ready.

Obviously, the client must tell the server to behave differently in order to make a truly asynchronous transport call. Some metadata must be sent along with the request message, and the server must be able to detect it, read it, and act accordingly. WASP Server for Java uses SOAP Headers to pass metadata between parties. The solution is transport independent (imagine usage of transport protocols that don’t support any extra data to be sent with the payload, like ftp, plain file on disk, RDB table etc.); and builds on existing industry standards. The information tells the server that the client’s address for routing the response message is encoded via the WS-Routing SOAP Header in WASPJ. WS-Routing is a specification maintained by a couple of large Web services players (Microsoft, IBM and others) and defines the routing of SOAP messages. Here, WS-Routing tells the server to which address it should deliver the response message. Because of the widespread support of this specification, this solution is interoperable with frameworks that support it?like .NET.

The actual last step is to tell the SOAP framework to use asynchronous transports. Do this by adding this line to the client code:

serviceClient.setAsyncTransport("http"); 

This method tells WASPJ to make all forthcoming calls transport asynchronous. Use http as the response message protocol here because its usage doesn’t need any additional configuration. You could make the response come thru POP3, JMS, whatever, but it would require a more complex setup.

When using the http case, WASPJ starts a standalone http server on the client side. The address of this client’s server along with the identification of the invocation (the correlation information) is sent to the service in the form of WS-Routing SOAP Header. The server reads it, finds out it needs to use an asynchronous response, and uses this information for sending the response back to the client. Please note that this is a frameworks matter only, you don’t have to make the smallest change to the existing implementation of the Web service! The full source of this truly asynchronous client is in Listing 6 (See also ServiceClientTransAsync.java in DEMO_HOME/src ).

    /**    * @param args the command line arguments    */public static void main (String args[]) throws Exception {    ChatService service;    String wsdlURI = "http://localohost:6060/ChatServiceAsync/wsdl";    String serviceURI = "http://localhost:6060/ChatServiceAsync/";    // lookup service    ServiceClient serviceClient = ServiceClient.create(wsdlURI);    serviceClient.setServiceURL(serviceURI);    service = (ChatService)serviceClient.createProxy(ChatService.class);// now, call the methods on your Web Service interface// let's start with some synchronous calls, print the existing messages    long lastMessageId;    ChatMessage msgs[];    service.postMessage("Enter", "Sandman");    msgs = service.getAllMessages();    lastMessageId = msgs[msgs.length - 1].id;    printMessages(msgs);    //now let's wait for another messages to arrive    System.out.println(">>> Client: Making the asynchronous call, " + "          waiting for response to arrive...");//1.         turn on the transport asynchrony    serviceClient.setAsyncTransport("http");//initiate the async invocation; //read new messages from the server with timeout 10s    AsyncConversation async =         service.beginGetOrWaitforNewMessages(lastMessageId, 10000);//setup the callback that will be called when the response arrives    MyCallbackClass callback = new MyCallbackClass(service);    async.setCallback(callback);//the following dumb code demonstrates that the client keeps running//my callback decides if it's been enough already    while (!finishedWork) {//The client now either sleeps (read "does other important work");       System.out.println("    Client: ... zzzzZZZ " +                   "... doing other work ... zzZZ ...");       Thread.sleep(2000);       System.out.println("    Client: " +         "Sending another post to the server.");        service.postMessage("Wake Up!", "Sandman");    }//2.        clean up, release all resources, stop the http server            Wasp.destroy();    System.out.println("--- Client: All work done, exiting.");}
  1. Tell the WASPJ framework to use transport asynchrony for all forthcoming calls.
  2. As the server was started in the client transparently, you now have to shut it down (otherwise there are running threads left and the client never shuts down completely).

And that’s it! You now have a fully asynchronous client implemented. Start it and see if it works. Please note that the client’s internal WASPJ server is running on port 7070. If this port is already in use, the client-side server (haven’t I said that the differences between clients and server diminish under asynchronous communication? What a strange-looking term: “client-side server”) won’t start and the demo will not work.

If you still yearn for more code, the final version of the client?with multiple calls and multiple responses?can be found in Listing 7. See also ChatServiceClientBeastAsync.java in DEMO_HOME/src. Don’t blame me if you cannot read this code easily! You’ve been warned!

Asynchronous Reality
Asynchronous invocation in the world of Web services is a reality. With the appropriate tools, there’s nothing to fear. Standards are already available, and allow you to build asynchronous heterogenous solutions that are mutually interoperable?and more standards are coming soon. In the near future, we will see a rise of asynchronous messaging, as it is one of the building blocks of a future where documents (not RPC calls) will be the major transport vehicle for business-to-business integration.

See also  Custom Java Web Development - The Heartbeat of Modern Web Development
devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist