Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

Basic SOA Using REST


3.5 RESTful Services with and without JWS

The focus now switches from client-side consumption of RESTful Web Services to development and deployment of such services themselves. As in Section 3.3, this section examines how to deploy such services both with and without JAX-WS. As before, the purpose here is to compare and contrast the JWS approach with the bare-bones approach of simply working with Java’s HTTP servlet tools to build and deploy a simple RESTful service.

Again, the example used to illustrate the concepts in this section is a basic building block of SOA Web Services—the deployment of a simple download service. In this case, it is the “New Orders” service discussed from the client perspective in previous sections. This section examines how to deploy a Java class that provides a getNewOrders() method both with and without JAX-WS.

3.5.1 Deploying a REST Service without Using JWS

This section provides the first example in the book of how to deploy a Java method as a Web service. It is the simplest RESTful Web service imaginable— the service consumed by the client in Section 3.3.2. This service simply returns an XML document containing the “new orders."

Example 3-13 shows a mock object implementation of the getNewOrders() method. This implementation of OrderManager is called a mock object because it is a “mock-up” of a real OrderManager class. It is a stub implementation of the server side. The getNewOrders() method returns the contents of a hard-coded file (/orders.xml) instead of providing a live interface to an Order Management System. I use mock objects like this to illustrate the Java classes that are deployed as Web Services. This strategy provides realistic examples you can actually run, without requiring you to have any actual back-end systems to provide Order Management or Customer Service implementations.

Example 3-13 The OrderManager.getNewOrders(...) Method to Be Deployed As a Web Service

26 public class OrderManager {
27
28 public Source getNewOrders() throws FileNotFoundException
{
29 // get the resource that provides the new orders
30 InputStream src = getClass().getResourceAsStream("/orders.xml");
31 if ( src == null ) {
32 throw new FileNotFoundException("/orders.xml");
33 }
34 return new StreamSource(src);
35 }
36
37 }
book-code/chap03/rest-get/endpoint-servlet/src/java/samples/OrderManager.java

Several questions arise when considering how to enable this method as a Web service:

1. How is the class instantiated.
2.
How is the Source instance returned by getNewOrders() converted into an XML stream in an HTTP response message.
3.
How is the class deployed as a Web service.

These three questions mirror the three components of a Web Services Platform Architecture outlined in Chapter 1, Section 1.2. Question #1 is about invocation—the first component of a Web Services Platform Architecture.

To invoke a Web service, you must be able to get an instance of its implementation class. Question #2 is about serialization—the second component of a Web Services Platform Architecture. The Java object that is returned must be serialized to XML that can be written out “onto the wire."

Question #3 is about deployment—the third component of a Web Services Platform Architecture.

This section gives simple answers to these questions, as the example shows how to deploy the Java method as a Web service using a dedicated servlet. However, what is interesting to note is that, even in this simple case, the solutions need to deal with all three of the Web Services Platform Architecture components.

In this example, the Web service is accessed with an HTTP GET request. The servlet needs to handle the HTTP GET request, instantiate the Java class, invoke the getNewOrders() method, and write the results out as XML in the HTTP GET response. Figure 3-10 shows the basic architecture.

As illustrated here, this simple architecture deploys a RESTful Web service using the java.servlet.http.HttpServlet class. The GetNewOrders- servlet class acts as a front-end to the OrderManager that mediates between the HTTP request/response messages and invokes the Java method that implements the Web service. The steps illustrated in the figure are:

1. The client sends an HTTP GET request to the appropriate URL (a URL that has been associated, in the servlet container’s configuration, with the GetNewOrders class).
2.
The servlet container wraps the HTTP request stream in an instance of HTTPServletRequest and passes it to the GetNewOrdersServlet’s doGet() method.
3.
The doGet() method creates an instance of the OrderManager class and invokes the getNewOrders() method.
4. The getNewOrders() method returns the new orders XML document as an instance of javax.xml.transform.Source.
5.
An instance of javax.xml.transform.Transformer is used to write the new orders Source to the ServletOutputStream (wrapped in an instance of java.xml.transform.stream.StreamResult).
6.
The getNewOrders() method returns and the HTTP response containing the new orders XML document is returned to the client.

Example 3-14 shows the code for the GetNewOrdersServlet’s doGet() method. This code is straightforward, but there are a few items to notice and think about. First, as you can see, the servlet has to instantiate the OrderManager class. This assumes that OrderManager is available to the class loader. In this example, I accomplish that by bundling the OrderManager class in the WAR that is deployed. This is the simplest way to get an instance of a class that needs to be deployed as a Web service, but it is not always feasible. For example, if the class is implemented as an EJB, you will need to request its interface from the EJB container, instead of instantiating an instance. Furthermore, suppose the class requires other container services (e.g., JNDI and a database connection). In the real world, it is not so easy to just deploy a POJO by packaging its class definition into a WAR.

Even in this example, using a mock object, the returned data (orders.xml) needs to be packaged into the WAR along with the deployed class. How you get an instance of a class being deployed as a Web services is a topic I cover in some detail when we explore the design of the SOA-J in Chapter 11.

Another item to notice is the use of the HttpServletResponse.setContentType(" text/xml") method to set the content type of the HTTP response.

This is important, because many REST clients (including early versions of the GlassFish implementation of Dispatch) will fail if the content type is not "text/xml." You need to be doubly careful with this, because some of the HttpServletResponse methods (e.g., sendError(int sc, String msg) ), on some servlet containers, change the content type to "text/xml" since their error messages are implemented as HTML content.

Lastly, notice the use of the default instance of Transformer to simply write XML from a Source to a Result. Unlike in Section 3.4, here I am not doing any XSL transformation. I am just using the Transformer to write the XML returned by the OrderManager to the ServletOutputStream.

Example 3-14 The GetNewOrdersServlet doGet(...) Method

33 public void doGet(HttpServletRequest req,
34 HttpServletResponse res)
35 throws IOException, ServletException {
36 // invoke the Java method
37 OrderManager om = new OrderManager
();
38 Source src = om.getNewOrders();
39 // write the file to the HTTP response stream
40 ServletOutputStream out = res.getOutputStream();
41 res.setContentType("text/xml");
42 StreamResult strRes = new StreamResult(out);
43 try {
44 TransformerFactory.newInstance().newTransformer()
45 .transform(src, strRes);
46 } catch (Exception e) {
47 throw new IOException(e.getMessage());
48 }
49 out.close();
50 }
book-code/chap03/rest-get/endpoint-servlet/src/java/samples
/GetNewOrdersServlet.java

The instructions for deploying and invoking this servlet are included with Example 3-6.

Example 3-15 shows the deployment descriptor for the Web service implemented by the GetNewOrdersServlet class (together with OrderManager).

This is the standard web.xml file, which is placed into the WEB-INF subdirectory of the WAR package used to deploy this Web service.

Example 3-15 The web.xml Deployment Descriptor Bundled in the GetNewOrdersServlet WAR
9 <web-app>
10 <servlet>
11 <servlet-name>GetNewOrdersServlet</servlet-name>
12 <servlet-class> samples.GetNewOrdersServlet </servlet-class>
13 </servlet>
14 <servlet-mapping>
15 <servlet-name>GetNewOrdersServlet</servlet-name>
16 <url-pattern>/NewOrders</url
-pattern>
17 </servlet-mapping>
18 </web-app>
book-code/chap03/rest-get/endpoint-servlet/src/webapp/WEB-INF/web.xml

Notice in Example 3-15 that the servlet GetNewOrdersServlet is mapped to the URL pattern /NewOrders. The deployment tool you use to deploy the WAR determines the context root for the Web application. In this case, the GlassFish default is being used—and it takes the context root from the name of the WAR file (chap03-rest-get-endpoint-servlet- 1.0.war). And the base URL for the servlet container is http://localhost: 8080 (unless you have customized the profile section of the <bookcode>/ pom.xml file when you installed the code example—see Appendix B, Section B.5). So, the URL for this Web service becomes:

http://localhost:8080/chap03-rest-get-endpoint-servlet-1.0/NewOrders That pretty much wraps up the “how-to” discussion for creating and deploying a simple REST Web service using a servlet to invoke a Java method. As you can see, it is not hard to use servlets for deployment of basic RESTful Web services. The three questions posed at the beginning of the section have been answered as follows:

1. The Web service implementation class (i.e., OrderManager) is instantiated using the no-arg default constructor. This assumes that such a constructor exists and that the class definition is on the classpath. It also assumes that all resources required by the class are available.
2.
The object returned by getNewOrders
() is converted into an XML stream using a Transformer. This is a very simple scenario where the method being deployed returns a result that is already represented as an XML infoset. In most cases, the result will be a Java object that is not an XML infoset representation and requires serialization.
3. The class is deployed by bundling it with a dedicated servlet.

Problems are encountered, however, when you want to deploy multiple services. Using the architecture described in this section, you need to have a servlet for each Web service. That quickly becomes cumbersome and inefficient.

What is needed is an invocation subsystem so that a single servlet can be used to invoke more than one service. However, to do that requires a method of mapping URLs to services at a finer granularity than provided by the web.xml file. As you can see in Example 3-15, the web.xml mapping is from a single URL to a single servlet. So, for a single servlet to handle multiple URLs (and multiple Web services), additional deployment meta-data must be added to this simple architecture that falls outside of the servlet processing model.

One way to do this might be to map all base URLs of the form http://example.

com/services/* to the servlet. Then, local paths such as /getNewOrder, /addCustomerHistory, and so on are mapped to individual Web services.

In fact, this approach is used by a variety of Web Services engines, including Apache Axis [AXIS] and the SOA-J engine introduced in Chapter 11. JWS also offers a variation on this approach, which I examine in detail in Chapter 7. For now, I’m going to defer further discussion of deployment issues and move on to the nuts and bolts of how to deploy this “New Orders” example Web service using JAX-WS and JSR-181 annotations.

3.5.2 Deploying a RESTful Service with JWS

This section illustrates how a Web service is deployed using JWS. This is the same service that was deployed in Section 3.5.1. However, the operation of the service—as deployed using JWS—is very different.

The primary difference here is that instead of using a servlet as in Example 3-14, the JWS version uses an instance of Provider<Source> to implement the RESTful service. Example 3-16 shows how it is implemented.

The @WebServiceProvider annotation used in this example is defined by the JAX-WS 2.0 specification. It is used to declare a reference to a Web Service that implements a Provider<Source> interface. The @WebServiceProvider annotation is required for deploying a RESTful Web service and is discussed in more detail in Chapter 7, which covers JAX-WS server-side development and deployment.

The Provider<Source> interface is basically the server-side version of Dispatch<Source> discussed in Section 3.3.2. It enables you to create a Web service that works directly with the XML message as an instance of javax.xml.transform.Source—rather than a JAXB 2.0 representation of the XML message. Along with @WebServiceProvider, the javax.xml.ws.Provider interface is explained in greater detail in Chapter 7. In this section, my goal is just to give you a quick example of how a RESTful service can be created and deployed with JAX-WS.

The @BindingType annotation (javax.xml.ws.BindingType) used in Example 3-16 is also defined by the JAX-WS 2.0 specification and is used to specify the binding that should be employed when publishing an endpoint.

The property value indicates the actual binding. In this case, you can see that the value is specified as follow:

value=HTTPBinding.HTTP_BINDING This indicates that the XML/HTTP binding should be used, rather than the default SOAP 1.1/HTTP. This is how REST endpoints are specified in JAX-WS 2.0—by setting the @BindingType. If one were to leave the @BindingType annotation off this example, the Java EE 5 container would deploy it as a service that expects to receive a SOAP envelope, rather than straight XML over HTTP (i.e., REST).

Example 3-16 GetNewOrdersProvider Implements Provider<Source> to Create a RESTful Web Service

21 import javax.xml.transform.Source;
22 import javax.xml.ws.BindingType;
23 import javax.xml.ws.Provider;
24 import javax.xml.ws.WebServiceProvider;
25 import javax.xml.ws.http.HTTPBinding;
26 import javax.xml.ws.http.HTTPException;
27
28 @WebServiceProvider
29 @BindingType(value=HTTPBinding.HTTP_BINDING)
30 public class GetNewOrdersProvider implements Provider<Source> {
31
32 public Source invoke(Source xml) {
33 OrderManager om = new OrderManager
();
34 try {
35 return om.getNewOrders();
36 } catch (Throwable t) {
37 t.printStackTrace();
38 throw new HTTPException(500);
39 }
40 }
41
42 }
book-code/chap03/rest-get/endpoint-jaxws/src/java/samples
/GetNewOrdersProvider.java

The Provider<Source> interface specifies the invoke() method, which receives and returns an instance of Source. As shown in this example, inside the invoke() message, the OrderManager class gets instantiated and the OrderManager.getNewOrders() method is invoked to implement the Web service functionality. So, instead of wrapping the OrderManager inside an HttpServlet.doGet() method, as in Example 3-14, this example wraps the service implementation class inside a Provider.invoke() method.

At this point, it is worth asking the same questions posed in Section 3.5.1.

In particular:

1. How is the class instantiated.
2.
How is the Source instance returned by getNewOrders() converted into an XML stream in an HTTP response message.
3.
How is the class deployed as a Web service.

As you can see from the code, some of these questions get very different answers when a RESTful Web service is implemented as a Provider<Source> than when it is implemented using an HttpServlet. These differences serve to contrast the JAX-WS 2.0 approach to Web Services deployment with the straightforward servlet implementation of the preceding section.

The answer to the first question is the same in both cases—the class is instantiated each time the service is invoked. However, the answer to the second question is different in this case. Here, the Source instance can be returned directly. It does not need to be converted into a stream and written out to the HTTP response message. These details related to the binding of the service to the HTTP transport are handled by the JAX-WS run-time implementation. Lastly, the answer to the third question is also very different.

Java EE 5 supports many options for deploying Web services—these are all discussed in Chapter 8. A web.xml deployment descriptor can be used (even though this is not a servlet!), but is not required. In fact, it is possible to deploy a JWS Web service without any deployment descriptors. The Java EE 5 container can often deploy a service based entirely on its annotations.

Figure 3–11 shows the architecture supporting the RESTful Web service created and deployed here using JAX-WS 2.0.

As illustrated here, this JWS architecture deploys the RESTful Web service using the java.servlet.http.Provider<Source> class. The GetNew- OrdersProvider class acts as a front-end to the OrderManager that mediates between the XML request/response messages and invokes the Java method that implements the Web service. The steps illustrated in the figure are:

1. The client sends an HTTP POST request to the appropriate URL (a URL that is specified at deployment time—either in a deployment descriptor or by a deployment tool). Notice that a POST request is used here, rather than a GET request. That is because early implementations of JAX-WS allowed RESTful Web services only to accept POST requests. Recent versions support both POST and GET requests.
2. The JWS container extracts the XML message from the HTTP POST request and passes it to the GetNewOrders.invoke() method.

This functionality is provided by the JAX-WS runtime.
3.
The invoke() method creates an instance of the OrderManager class and invokes the getNewOrders() method.
4.
The getNewOrders() method returns the new orders XML document as an instance of javax.xml.transform.Source.
5.
The instance of Source—the Web service’s return XML message— is inserted into the HTTP response message and returned to the caller. This functionality (i.e., wrapping the XML response message inside the HTTP response message) is provided by the JAX-WS runtime.

In some ways, the JWS implementation of this RESTful Web service is simpler than its servlet-based counterpart discussed in Section 3.5.1. The simplification comes from not having to translate between HTTP request/ response messages and the XML request/response messages. JAX-WS handles that translation so that the developer can work directly with the XML messages. In other ways, however, the JWS implementation shown here seems more cumbersome. Two annotations are required—@WebService- Provider and @BindingType. If you are not used to annotations, these can make the example seem confusing.

To deploy and invoke this RESTful Web service example, do the following:

1. Start GlassFish (if it is not already running).
2.
Go to <book-code>/chap03/rest-get/endpoint-jaxws.
3.
To build and deploy the Web service enter:
mvn install
... and when that command finishes, then enter:
ant deploy
4.
Go to <book-code>/chap03/rest-get/client-jaxws.
5.
To run the client enter:
ant run-jaxws
6.
To undeploy the Web service, go back to <book-code>/chap03/ rest-post/endpoint-jaxws and enter:
ant undeploy


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.