Table of Contents
This chapter describes the Provider service endpoint interface and client dispatch interface.
A web service and its client communicate with each other through an XML-based messaging. The JAX-WS service endpoint interface provides a high-level abstraction that conceals the complicated conversion processes from a Java object to XML messages and vice versa. However, sometimes a developer may want to process XML message exchanges between a service and client purely at the message level without the help of a complicated programming model such as a SOAP message handler.
JEUS 8 web services implement the Provider interface for the service endpoint and a Dispatch interface for the client in order to access a message at the message level. The two interfaces enable a web service or client developer to deal with a message at the XML message level.
This chapter describes the two interface types.
This section describes the Provider interface.
The Provider interface is used by a web service endpoint to handle an XML document at the XML message level. By doing this, the endpoint can access a message or message payload through a low-level Generic API. 'Generic' is a new function provided by Java SE 5.
The following shows how to use the Provider interface.
The @javax.xml.ws.WebServiceProvider annotation must be included.
The class must implement the Provider interfaces, javax.xml.transform.Source, javax.xml.soap.SOAPMessage, and javax.activation.DataSource.
Use a message payload by implementing the Provider<javax.xml.transform.Source>.
To handle a message payload as a source object, set the @ServiceMode annotation to 'Service.Mode.PAYLOAD'.
This setting can be omitted if the @WebServiceProvider annotation has been declared as it will be automatically set to the default value.
@WebServiceProvider public class ProviderImpl implements Provider<Source> { public Source invoke(Source source) { ... } }
Use a message by implementing the Provider<javax.xml.soap.SOAPMessage>
To handle a message as a SOAPMessage object, set the @ServiceMode annotation to 'Service.Mode.Message'.
@WebServiceProvider @ServiceMode(value=Service.Mode.MESSAGE) public class ProviderImpl implements Provider<SOAPMessage> { public SOAPMessage invoke(SOAPMessage msg) { ... } }
Use a message by implementing the Provider<javax.activation.DataSource>
To handle a message as a source object, set the @ServiceMode annotation to 'Service.Mode.Message'. If the requested message is a SOAP message, only the SOAPPart excluding the Attachment is transmitted to the Source object. If the return value is null, it means that the web service is one-way.
@WebServiceProvider
@ServiceMode(value=Service.Mode.MESSAGE)
public class ProviderImpl implements Provider<Source> {
public Source invoke(Source source) {
...
return null;
}
}
The @javax.xml.ws.ServiceMode annotation determines whether the endpoint will access a message (Service.Mode.MESSAGE) or the payload (Service.Mode.PAYLOAD).
If the @javax.xml.ws.ServiceMode annotation is not configured, by default it is set to only handle payload.
When creating a web service endpoint interface, since a service implementation class that implements the Provider interface does not provide any information about the endpoint interface, the web service must be created through the WSDL file.
When creating a web service through an implementation class that implements the Provider interface, some portable artifact ports obtained from WSDL are not actually used by the implementation class. Thus, an external binding declaration can be used to prevent those ports from being created when the portable artifacts are created through WSDL using the wsimport tool.
The following example shows a service implementation class that handles the payload part of a message by implementing the <Provider>Source interface.
[Example 8.1] << AddnumbersImpl.java >>
@ServiceMode(value = Service.Mode.PAYLOAD) @WebServiceProvider(wsdlLocation = "WEB-INF/wsdl/AddNumbers.wsdl", targetNamespace = "http://tmaxsoft.com", serviceName = "AddNumbersService", portName = "AddNumbersPort") public class AddNumbersImpl implements Provider<Source> { public Source invoke(Source source) { try { DOMResult dom = new DOMResult(); Transformer trans = TransformerFactory.newInstance().newTransformer(); trans.transform(source, dom); Node node = dom.getNode(); Node root = node.getFirstChild(); Node first = root.getFirstChild(); int number1 = Integer.decode(first.getFirstChild().getNodeValue()); Node second = first.getNextSibling(); int number2 = Integer.decode(second.getFirstChild().getNodeValue()); int sum = number1 + number2; String body = "<ns:addNumbersResponse xmlns:ns=\"http://tmaxsoft.com\"><ns:return>" + sum + "</ns:return></ns:addNumbersResponse>"; Source sumsource = new StreamSource(new ByteArrayInputStream(body.getBytes())); return sumsource; } catch (Exception e) { throw new RuntimeException(e); } } }
In the previous example, the AddNumbersImpl class implements the <Provider>Source interface through the @WebServiceProvider annotation, and handles the message payload through the @ServiceMode annotation.
The following shows how to execute a web service, that implements the Provider interface, by using the implemented classes and other configuration files in this section.
Create a web service by implementing the Provider interface, and deploy it to JEUS by using the following command.
$ ant deploy
Since the client is processed using the wsimport tool, it can only be built after the service has been deployed.
After creating the client, call the service. The console outputs the details of the message exchanges and the web service that implements the Provider interface.
$ ant run ... run: [java] ############################################## [java] ### JAX-WS Webservices examples - Provider ### [java] ############################################## [java] Testing Provider webservices... [java] Success! ... BUILD SUCCESSFUL ... ---[HTTP request]--- Host: localhost:8088 Content-length: 199 Content-type: text/xml; charset=utf-8 Accept: text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 Connection: keep-alive Soapaction: "" User-agent: JAX-WS RI 2.2 - JEUS 8 <?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envel ope/"><S:Body><addNumbers xmlns="http://tmaxsoft.com"><arg0>10</arg0><arg1>20</a rg1></addNumbers></S:Body></S:Envelope> -------------------- ---[HTTP response 200]--- <?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envel ope/"><S:Body><ns:addNumbersResponse xmlns:ns="http://tmaxsoft.com"><ns:return>3 0</ns:return></ns:addNumbersResponse></S:Body></S:Envelope> -------------------- ...
This section describes the client dispatch interface.
A client application can handle an XML document at the message level, in symmetry with the Provider interface of the service endpoint.
As in the Provide interface for a server, some portable artifact ports obtained from WSDL are not actually used by the service implementation class in a client application that implements the client dispatch interface. Thus, an external binding declaration must be used to prevent those ports from being created when creating the portable artifacts with WSDL by using the wsimport tool.
The following shows how a dispatch interface is used.
Implement the javax.xml.transform.Source, javax.xml.soap.SOAPMessage, and javax.activation.DataSource interfaces.
The Provider interface of the service endpoint handles a message payload by implementing Provider<Source> and a message by implementing Provider<SOAPMessage> or Provider<Source>. Likewise, a client dispatch interface can form a message by using the SOAPMessage, Source, or JAXB object.
Create a service object in order to create a dispatch object (invoke a dynamic service operation).
The service object (javax.xml.ws.Service) plays the role of a factory that creates a dynamic service object. Dynamic service object creation is allocating various binding data of the actual service at runtime in order to create a service object without WSDL.
The following is an example code used to create a dynamic service.
Service service = Service.createService(QName serviceQName); Service service = Service.createService(URL wsdlLocation, QName serviceQName);
For the created service object, a particular port or endpoint to which a dispatch object is bound to can be added by using the addPort() method, as in the following example. The client application can be invoked through the added port.
Note that the following methods do not work when the wsdlLocation data is used to dynamically create the service object. This is because the service has already added the port by using the wsdlLocation data.
service.addPort(QName portName, String SOAPBinding.SOAP11HTTP_BINDING, String endpointAddress); service.addPort(QName portName, String SOAPBinding.SOAP12HTTP_BINDING, String endpointAddress); service.addPort(QName portName, String HTTPBinding.HTTP_BINDING, String endpointAddress);
The previous example shows how to add a port by using the port name and endpoint address for SOAP 1.1, SOAP 1.2 and XML/HTTP bindings, respectively.
The aforementioned methods cannot be used with a service object created from a WSDL file. This is because a service object created from portable artifacts already has information about the port binding, port name, or endpoint address, when the service is created from a WSDL file by using the wsimport tool.
The following is an example code for creating a service object from portable artifacts through a general WSDL file by using the wsimport tool.
Service service = new AddNumbersService();
Creating a dispatch object
If the service object was created dynamically or from a WSDL file, a dispatch object is generated by using the two methods of the following service class.
Dispatch dispatch = service.createDispatch(QName portName, Class clazz, Service.Mode mode); Dispatch dispatch = service.createDispatch(QName portName, JAXBContext jaxbcontext, Service.Mode mode);
A dispatch object can use the javax.xml.transform.Source or JAXB data binding object. In both cases, the service object uses the createDispatch method and the Service.Mode.PAYLOAD or Service.Mode.MESSAGE mode to create the dispatch object.
In order for a dispatch object to be able to use the javax.xml.soap.SOAPMessage object, the dispatch object must be created from the service object by using the createDispatch method and the Service.Mode.MESSAGE mode.
The following is an example of a client web service that implements the dispatch interface from a WSDL document of a remote web service.
[Example 8.2] << AddNumbersClient.java >>
public class AddNumbersClient { public static void main(String[] args) { try { Service service = new AddNumbersService(); String request = "<addNumbers xmlns=\"http://tmaxsoft.com\"> <arg0>10</arg0><arg1>20</arg1></addNumbers>"; QName portQName = new QName("http://tmaxsoft.com", "AddNumbersPort"); Dispatch<Source> sourceDispatch = service.createDispatch(portQName, Source.class, Service.Mode.PAYLOAD); System.out.println("##############################################"); System.out.println("### JAX-WS Webservices examples - Dispatch ###"); System.out.println("##############################################"); System.out.println("Testing Dispatch webservices..."); Source result = sourceDispatch.invoke(new StreamSource(new StringReader(request))); DOMResult dom = new DOMResult(); Transformer trans = TransformerFactory.newInstance().newTransformer(); trans.transform(result, dom); Node node = dom.getNode(); Node root = node.getFirstChild(); Node first = root.getFirstChild(); int sum = Integer.decode(first.getFirstChild().getNodeValue()); if (sum == 30) { System.out.println("Success!"); } else { System.out.println("Fail!"); } } catch (ProtocolException jex) { jex.printStackTrace(); } catch (TransformerException e) { e.printStackTrace(); } } }
As shown in the previous example, the class, AddNumbersClient, obtains a sourceDispatch object of the type Dispatch<Source>, by using the createDispatch() method of the service object created from WSDL.
The following shows how to execute a web service that implements a dispatch interface by using the implemented classes and other configuration files in this section.
Create the web service and deploy it to JEUS by using the following command.
$ ant deploy
Once the previous step is completed successfully, build and call the client. Since the client is processed by using the wsimport tool, it can only be built after the service has been deployed.
As shown in the following example, create a client and invoke the service. The console outputs the details of the message exchanges and the web service that implements the Dispatch interface.
$ ant run ... run: [java] ############################################## [java] ### JAX-WS Webservices examples - Dispatch ### [java] ############################################## [java] Testing Dispatch webservices... [java] Success! ... BUILD SUCCESSFUL ... ---[HTTP request]--- Host: localhost:8088 Content-length: 199 Content-type: text/xml; charset=utf-8 Accept: text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 Connection: keep-alive Soapaction: "" User-agent: JAX-WS RI 2.2 - JEUS 8 <?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envel ope/"><S:Body><addNumbers xmlns="http://tmaxsoft.com"><arg0>10</arg0><arg1>20</a rg1></addNumbers></S:Body></S:Envelope> -------------------- ---[HTTP response 200]--- <?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envel ope/"><S:Body><addNumbersResponse xmlns="http://tmaxsoft.com"><return>30</return ></addNumbersResponse></S:Body></S:Envelope> -------------------- ...
When exchanging messages in a web service, an XML message is sent over HTTP instead of a SOAP message. Such web service is called a RESTful web service. A service and client of a RESTful web service typically use a provider and dispatch interfaces. For more information about the two interfaces, refer to "8.2. Provider Interface for Service Endpoint" and "8.3. Client Dispatch Interface".
This section describes XML/HTTP bindings provided by JEUS web service to support RESTful web services.
The following are the main features of a RESTful web service.
Can be transmitted to the GET Request of HTTP protocol or an endpoint.
Gets information about the query string and path of the required HTTP request through the standard properties, MessageContext.QUERY_STRING and MessageContext.PATH_INFO.
A RESTful web service does not create a web service and client through a WSDL document. The provider and consumer of a RESTful web service creates the service through a pre-configured schema of the XML document. The provider creates a service class by using the wsgen tool, and the client also creates an application independently without using the portable artifacts.
For more convenience in building RESTful web services, JAX-WS supports XML/HTTP binding in the provider and dispatch interfaces. Through this, the provider and consumer can more easily create a RESTful web service from a JEUS web service.
As explained in "8.2. Provider Interface for Service Endpoint", in order to implement a provider interface, a service endpoint class that implements Provider<SOAPMessage> or Provider<Source> must be created. The bound Source or SOAPMessage object is used to process the message payload or the entire message.
The provider endpoint can be set to a different binding type by using a binding identifier, which becomes available when the endpoint class includes the @BindingType annotation. If there is no binding identifier, the default binding, SOAP1.1/HTTP, is declared.
The following example shows how to declare an XML/HTTP binding by using the binding identifier.
@ServiceMode(value=Service.Mode.MESSAGE) @BindingType(value=HTTPBinding.HTTP_BINDING) public class ProviderImpl implements Provider<Source> { public Source invoke(Source source) { ... } }
As shown in the previous example, since the service endpoint class is bound to an inbound message through XML/HTTP, it implements the Provider<Source> interface instead of the Provider<SOAPMessage> interface.
As described in 2, a client for a RESTful web service is implemented by creating a dynamic service object and a port through 'HTTPBinding.HTTP_BINDING' as in the following example.
Service service = Service.createService(QName serviceQName); service.addPort(QName portName, String HTTPBinding.HTTP_BINDING, String endpointAddress);
Create a dispatch object by using the service object from the previous example. If necessary, the service can be invoked by specifying the request as POST or GET in the service object and registering the required query string (MessageContext.QUERY_STRING) and path (MessageContext. PATH_INFO) information.
The following example shows how to create the client-side code.
... Dispatch<Source> d = service.createDispatch(portQName, Source.class, Service.Mode.MESSAGE); Map<String, Object> requestContext = d.getRequestContext(); requestContext.put(MessageContext.HTTP_REQUEST_METHOD,new String("GET")); requestContext.put(MessageContext.QUERY_STRING, queryString); requestContext.put(MessageContext.PATH_INFO, path); Source result = d.invoke(null); ...
Note that no parameters are passed to the invoke method.
This section describes examples of a RESTful web service and client.
The following example shows a Java class that corresponds to a service of a RESTful web service.
[Example 8.3] << AddNumbersImpl.java >>
@WebServiceProvider(serviceName = "AddNumbersService") @ServiceMode(value = Service.Mode.MESSAGE) @BindingType(value = HTTPBinding.HTTP_BINDING) public class AddNumbersImpl implements Provider<Source> { @Resource(type = Object.class) protected WebServiceContext wsContext; public Source invoke(Source source) { try { MessageContext mc = wsContext.getMessageContext(); String query = (String) mc.get(MessageContext.QUERY_STRING); StringTokenizer st = new StringTokenizer(query, "=&/"); st.nextToken(); int number1 = Integer.parseInt(st.nextToken()); st.nextToken(); int number2 = Integer.parseInt(st.nextToken()); int sum = number1 + number2; String body = "<ns:addNumbersResponse xmlns:ns=\"urn:AddNumbers\"><ns:return>" + sum + "</ns:return></ns:addNumbersResponse>"; Source resultsrc = new StreamSource(new ByteArrayInputStream(body.getBytes())); return resultsrc; } catch (Exception e) { e.printStackTrace(); } return null; } }
As shown in the previous example, the RESTful web service declaration sets the BindingType annotation as 'HTTPBinding.HTTP_BINDING', and the Resource annotation as the member variable of the type WebServiceContext, wsContext. JAX-WS RI automatically injects wsContext at runtime.
In summary, the class obtains the MessageContext from WebServiceContext and uses it to get the query string value. Next, it creates a Source object that is parsed and returned by using the sendSource method, and returns the object as a return parameter.
The following is an example of a Java class that corresponds to a client of the RESTful web service.
[Example 8.4] << AddNumbersClient.java >>
public class AddNumbersClient { public static void main(String[] args) throws Exception { String endpointAddress = "http://localhost:8088/AddNumbers/addnumbers"; URL url = new URL(endpointAddress + "?num1=10&num2=20"); System.out.println("#############################################"); System.out.println("### JAX-WS Webservices examples - RESTful ###"); System.out.println("#############################################"); System.out.println("Testing RESTful webservices..."); InputStream in = url.openStream(); StreamSource source = new StreamSource(in); try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); StreamResult sr = new StreamResult(bos); Transformer trans = TransformerFactory.newInstance().newTransformer(); Properties oprops = new Properties(); oprops.put(OutputKeys.OMIT_XML_DECLARATION, "yes"); trans.setOutputProperties(oprops); trans.transform(source, sr); System.out.println(bos.toString()); bos.close(); } catch (Exception e) { e.printStackTrace(); } } }
As shown in the previous example, in a RESTful web service client, the code for creating portable artifacts from WSDL is not visible. It only shows that the Source object created through a URL object is transmitted in a stream format.
This section describes how to execute a RESTful web service by using the implemented classes and other configuration files.
Create a RESTful web service and deploy it to JEUS by using the following command.
$ ant build deploy
Once the service is deployed successfully, build the client.
As shown in the following example, create a client for a RESTful web service and invoke the service. The console outputs the details of the message exchanges between the RESTful web service and client.
$ ant run ... run: [java] ############################################# [java] ### JAX-WS Webservices examples - RESTful ### [java] ############################################# [java] Testing Restful webservices... [java] <ns:addNumbersResponse xmlns:ns="urn:AddNumbers"><ns:return>30</ns:r eturn></ns:addNumbersResponse> ... BUILD SUCCESSFUL ... ---[HTTP request]--- Host: localhost:8088 Content-type: application/x-www-form-urlencoded Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 Connection: keep-alive User-agent: JAX-WS RI 2.2 - JEUS 8 -------------------- ---[HTTP response 200]--- <ns:addNumbersResponse xmlns:ns="urn:AddNumbers"><ns:return>30</ns:return></ns:a ddNumbersResponse> -------------------- ...