The truth is that if you leave something on the internet long enough someone will take a copy of it. This also has a complete copy of my blog that was previously hosted on orablogs.
Shame all this stuff will fall out of the google index though.....
Worth knowing that latest JDK 6 contains a later version of the web service APIs, so you no longer have to play around with endorsed libraries to replace the bundled versions. At least until the next version of JAX-WS is released....
In my previous post I looked at a proposal to make it easier to implement asynchronous web services. There still remains the task of generating a suitable client to work with this service. Generally this will take the form of a proxy to invoke the "initialization" port type and a deployed web service that implements the "response"/"callback" port type. In general you would expect the async client to be deployed to a EE container to be properly managed.
Previously we would have made you generate the proxy and response port type manually; but the "Web Service Proxy" wizard in JDeveloper will now offer to generate the response service for you. In TP3 it will recognize any WSDL that contains a more than one port types that are related using a partnerLinkType as possibly an asynchronous service. (See here for an example) Once recognize the wizard give you give you the option to specify the package to generate the response service to:
You can of course un-tick the box and not generate a callback service if you want to. If you continue with the async case you will get both a skeleton client class and a skeleton service implementation is generated at the same time:
Not that in both the client we are generating boilerplate code to manage the WS-Addressing headers used to correlate the two different messages. This code is using the "AddressingHelper" class which is an oracle specific class; but note that it makes working with the headers so much easier. I will look into providing a non-oracle specific code example at some point when I get a few minutes together.
Unfortunately it appears that the web.xml and the correct deployment descriptors are not generated in the build that went out as TP3 but those are relatively easy to fix by hand or using the quick fixes in the code editor.
I have recently spent quite a bit of time pondering asynchronous web services. By this I mean in the simplest case a service where the response is returned on a separate connection and not the API level asynchronicity that was added in JAX-WS. It is entirely possible to implement this kind of service using the APIs provided by JAX-WS but it is not as fun or easy as it could be.
With my colleague Manoj Kumar, who to be fair does all the hard core server side bits, we have been thinking about have to make this process far less complicated for the developer by using declarative annotations. In particular we wanted to make sure that any process handling is entirely dealt with by the container. Unfortunately the implementation for this didn't make its way into the most recent TP3 release of JDeveloper 11; but the associated UI and annotations are in there. Of course we cannot promise that this feature will ever make it into a future product; but we would welcome your feedback on our thoughts so far.
Currently this proposal uses oracle specific annotations; but we have been working on them with a mind to ensuring that perhaps one day this could become part of the JAX-WS standard. This seems to be a reasonably useful scenario and therefore worthwhile including in the standard.
Lets look at a simple hello world example so you can have an idea of what I am talking about:
@WebService @AsyncWebService public class Hello { public String sayHello(String name) { return "Hello " + name; } }
So when this is deployed the generated WSDL will look rather different from the case without the @AsyncWebService. There will be two port types elements rather than the single would you would normally expect. The first will be the "Hello" port type which has one operation that takes the name as a parameter but doesn't have a return value. The second port type will take a name something like "HelloResponse" and will again have one operation, called perhaps "sayHelloResponse", which with have the return value as its parameter. (Note we haven't nailed down the name mapping rules yet hence I am being a little bit vague)
There will be of course bindings for each port type; but only the "Hello" port type will be surfaced as a port on a service as the server will only implement the initiation part of the process. The response port type will of course need to be implemented and published by the client.
So the resultant WSDL will look something like this the following. Notice that there is a partnerLinkType at the end of the WSDL. This is useful as it makes it much easier for tools to associate the two parts of the service. (Particularly when interacting with BPEL processes)
<?xml version="1.0" encoding="UTF-8" ?> <definitions name="HelloService" targetNamespace="http://model/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://model/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" > <types> <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://model/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://model/"> <xsd:complexType name="sayHello"> <xsd:sequence> <xsd:element name="arg0" type="xsd:string"/> </xsd:sequence> </xsd:complexType> <xsd:element name="sayHello" type="tns:sayHello"/> <xsd:complexType name="response"> <xsd:sequence> <xsd:element name="arg0" type="xsd:string"/> </xsd:sequence> </xsd:complexType> <xsd:element name="response" type="tns:response"/> </schema> </types> <message name="sayHelloInput"> <part name="parameters" element="tns:sayHello"/> </message> <message name="responseInput"> <part name="parameters" element="tns:response"/> </message> <portType name="Hello"> <operation name="sayHello"> <input message="tns:sayHelloInput" xmlns:ns1="http://www.w3.org/2006/05/addressing/wsdl" ns1:Action=""/> </operation> </portType> <portType name="HelloResponse"> <operation name="sayHelloResponse"> <input message="tns:responseInput" xmlns:ns1="http://www.w3.org/2006/05/addressing/wsdl" ns1:Action=""/> </operation> </portType> <binding name="HelloSoap" type="tns:Hello"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="sayHello"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> </operation> </binding> <binding name="HelloResponseSoap" type="tns:HelloResponse"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="sayHelloResponse"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> </operation> </binding> <service name="Hello"> <port name="HelloPort" binding="tns:HelloSoap"> <soap:address location="http://localhost:8988/Application1-Model-context-root/mathsserviceport"/> </port> </service> <partnerLinkType name="HelloService" xmlns="http://schemas.xmlsoap.org/ws/2003/05/partner-link/"> <role name="In"> <portType name="tns:Hello"/> </role> <role name="Out"> <portType name="tns:HelloResponse"/> </role> </partnerLinkType> </definitions>
So how is this implemented under the covers? In order to allow the initial call to Hello.sayHello() to return immediately we need to defer the processing of the call to the web service to another process / thread. Starting a native java thread is an obvious no-no when running in a managed JEE container but there are a number of alternatives that do work better in the EE world.
The implementation that we have been working on publishes the request onto a JMS queue. This request is then "consumed" by a message driven bean at some later time. The MDB is then responsible for calling the business logic and generating a suitable response message and sending it to the client. This is all transparent to the user as they only need to declare that the service is asynchronous. There might be specific features for a given implementation, for example you might want to specify a more secure and reliable queue that the default one we provide, but the most basic case would just work. In our implementation we provide a "@AsynchronousWebServiceQueue" annotation that allows the developer to specify the JNDI name of the JMS queue or connection factory.
Lets take a look at a sequence diagram show the full life cycle of a particular invocation. The client publishes the callback service first of all. It then invokes the initiation service and provides correlation and reply-to information in the form of WS-Addressing headers. This then causes a message to be posted on to JMS queue which is than consumed by a MDB. The MDB goes on to call the business logic in the normal way. When the process is finished the MDB uses the information stored in the WS-Addressing "Reply-To" header to post a response to the correct location.
Of course one message in and one message out is a rather simplistic example. For example you might want to update the client with some kind of progress update during a process. In our proposal you can do this using using a separate callback interface.
@WebService @AsyncWebService public class InsuranceClient { @CallbackRef InsuranceCallback callback; public Result processClaim(Claim claim) { callback.updateStatus("Stared Processing"); // Do something callback.updateStatus("Done something"); // Some something else callback.updateStatus("Finished Processing"); // Finally return return result; } @WebService public interface InsuranceCallback { public void updateStatus(String status); } }
In this case the generated WSDL with have a response port type that will contain both the "processClaimReponse" and "updateStatus" operations. The callback field will be injected with the correct service instance based on the contents of the WS-Addressing headers.
You can take this example one step further and make the business logic method return void. In this case the implementor is wholly responsible for managing the callbacks. This can be useful in the case where the response depends on the output of the process.
@WebService @AsyncWebService public class InsuranceClient { @CallbackRef InsuranceCallback callback; public void processClaim(Claim claim) { // Process claim if (success) { callback.claimRejected(...); } else { callback.claimAccepted(...); } } @WebService public interface InsuranceCallback { public void claimAccepted(...); public void claimRejected(...); } }
As you can see this simple annotation model can cover a variety of different interaction models. There are some important areas that we are still working on though: in particular how we handle WS-* policies on the response/callback messages and how we deal with faults are still works in progress. We also have a range of annotation for controlling the response port type and operations names but I have left them out here for reasons of clarity but can be easily found using the property inspector on the code in JDeveloper.
Finally it is worth noting that BEA has a similar design in there WebLogic product. From what I understand though it appears that there implementation only provides the callback injection and doesn't appear to offer the same process management as in our proposal. This might be due to a misunderstanding from myself of there documentation. I do personally think that hiding the process details is the most powerful aspect of our proposal.
I would be interested to hear about other implementations and feedback on this proposal.
The views expressed on this blog are my own and do not necessarily reflect the views of Oracle.