Java Web Programming with Eclipse contents
Last modified February 28, 2011 11:08 am

back next

Web Services (continued)

Video

Invocation of the Publish Service from the Wiki Application

The code presented in this section relies on the JDOM library. You should copy the JDOM jar file from the publisher application into the WEB-INF/lib folder in the wiki application. Then, add the JDOM jar file to the build path of the wiki application.

In this section we modify the wiki application so that it calls the publish web service. The following listing contains the implementation of the helper method in the publish page servlet that calls to take care of the details of communicating with the publisher application. You should add this code to the publish page servlet of the wiki application. The publish method is declared as private because it will only be invoked from code inside the publish page servlet.

   private String publish(Page page) throws IOException
   {
      logger.debug("publish()");
      String pageName = page.getName();
      String pageUrl = "http://localhost:8080/wiki/view/" + pageName;

      SAXBuilder builder = new SAXBuilder();
      
      Element titleElement = new Element("title");
      titleElement.addContent(pageName);
      Element linkElement = new Element("link");
      linkElement.addContent(pageUrl);

      Element root = new Element("item");
      root.addContent(titleElement);
      root.addContent(linkElement);
      Document document = new Document(root);

      StringWriter sw = new StringWriter();
      XMLOutputter outputter = new XMLOutputter();
      outputter.output(document, sw);
      String docString = sw.toString();

      byte[] docBytes = docString.getBytes("UTF-8");
      String contentLengthHeader = "Content-length: " + docBytes.length + "\r\n";
      String contentTypeHeader = "Content-type: text/xml\r\n";
      String hostHeader = "Host: localhost\r\n";
      String connectionHeader = "Connection: close\r\n";
      String requestLine = "POST /publisher/publish HTTP/1.1\r\n";
      
      Socket socket = new Socket("localhost", 8080);
      OutputStream os = socket.getOutputStream();
      os.write(requestLine.getBytes("US-ASCII"));
      os.write(hostHeader.getBytes("US-ASCII"));
      os.write(contentTypeHeader.getBytes("US-ASCII"));
      os.write(contentLengthHeader.getBytes("US-ASCII"));
      os.write(connectionHeader.getBytes("US-ASCII"));
      os.write("\r\n".getBytes("US-ASCII"));
      os.write(docBytes);
      os.flush();

      InputStream is = socket.getInputStream();
      InputStreamReader isr = new InputStreamReader(is);
      BufferedReader br = new BufferedReader(isr);
      
      // Read through header lines.
      while (true)
      {
         String line = br.readLine();
         if (line.length() == 0) break;
      }

      Document responseDoc = null;
      try 
      {
         responseDoc = builder.build(br);
      } catch (JDOMException e) {
         throw new RuntimeException(e);
      }
      
      Element idElement = responseDoc.getRootElement();
      String id = idElement.getText();
      br.close();
      return id;
   }

When you organize imports on the above code, select the following classes from the lists presented to you.

The publish method given above takes a wiki page object as its sole argument. It uses this object to construct a request to publish a news feed that points to the wiki page. To keep the example simple, we include only the title and wiki page link as data in the news feed. We start the implementation of the publish method by contructing variables that represent these two pieces of information: pageName and pageUrl.

The next thing we do is create an instance of SAXBuilder. SAX stands for simple API for XML. The SAX builder object is used for constructing XML documents. In our case, we use this object to construct the XML message to send to the publish service of the publisher application, which will contain the title and link for our wiki page.

Recall that the document that the wiki application submits to the publisher application contains a single root element called item, which represents the news item being submitted. The following XML document is an example.

<item>
   <title>Yahoo home page</title>
   <link>http://yahoo.com/</link>
</item>

Under the item element there are 2 children elements that contain text data (rather than sub-elements). The first child element is called title, and it contains within a start tag and an end tag the title of the news item. The second child element is called link, and it contains within a start tag and an end tag the url of the news article.

For each of the child elements, title and link, we create an instance of the JDOM element class. The following shows how we firts create the title element and then assign its contents. To keep the example application simple, we simply use the name of the wiki page for the title of the news item.

Element titleElement = new Element("title");
titleElement.addContent(pageName);

After creating the 2 child elements, we create an element to represent the root element item, and add the 2 child elements to it. This is shown in the following.

Element root = new Element("item");
root.addContent(titleElement);
root.addContent(linkElement);

We then create an instance of the JDOM document class to contain the root element as follows.

Document document = new Document(root);

We then create a StringWriter and an XMLOutputter. We pass the XML document and the string writer into the output method of the XML outputter class. The XML outputter writes a serialized version of the XML document into the string writer. We then extract the string that was written into the string writer and prepare to send this string to the publish service.

StringWriter sw = new StringWriter();
XMLOutputter outputter = new XMLOutputter();
outputter.output(document, sw);
String docString = sw.toString();

At this point, we have constructed a string that contains the MXL document to send to the service. We are going to write the character comprising this string into the output stream provided through a communication socket object. To prepare for this, we convert the string representation into a byte array as follows.

byte[] docBytes = docString.getBytes("UTF-8");

When converting characters into their byte equivalents, one must decide on a character representation. In our case, we use the UTF-8 representation, which is the default representation for XML documents.

The next set of operations is to construct the HTTP header that will precede the XML document in the outgoing stream of bytes.

String contentLengthHeader = "Content-length: " + docBytes.length + "\r\n";
String contentTypeHeader = "Content-type: text/xml\r\n";
String hostHeader = "Host: localhost\r\n";
String connectionHeader = "Connection: close\r\n";
String requestLine = "POST /publisher/publish HTTP/1.1\r\n";

The header of an HTTP/1.1 request message is comprised of a request line followed by 1 or more header lines. In our case, we send the content length, which is the number of bytes in the body of the request message. In our case, the content length is the number of bytes in the byte array representation of the XML document. We also send the content type, which is text/xml. The content type header specifies what is called the MIME-type of the object being transported in the body of the request message, which is a legacy term from the IETF email specifications. We send a host header that indicates the host name of the computer we are sending the message to. The host name header is the only request header that is required to be sent in version 1.1 of HTTP. The purpose of the host header is to inform the server which host name it resolved to get the server's IP address. This is useful in the case that a server has multiple host names (or virtual hosts). We send a connection header indicating that the underlying TCP connection will be closed by the client after the server returns a response. The request line is the the first line to be sent in an HTTP request. It starts with an HTTP method, POST in our case, followed by the name of a resource on the server, followed by the version of the HTTP protocol being used.

We then create a TCP socket connection to the server hosting the publisher application (localhost in our example) and send the HTTP request message to it.

Socket socket = new Socket("localhost", 8080);
OutputStream os = socket.getOutputStream();
os.write(requestLine.getBytes("US-ASCII"));
os.write(hostHeader.getBytes("US-ASCII"));
os.write(contentTypeHeader.getBytes("US-ASCII"));
os.write(contentLengthHeader.getBytes("US-ASCII"));
os.write(connectionHeader.getBytes("US-ASCII"));
os.write("\r\n".getBytes("US-ASCII"));
os.write(docBytes);
os.flush();

Note that Tomcat in our case is listening to port 8080. For this reason, Tomcat receives the HTTP message that we send (from inside Tomcat). Tomcat will parse the incoming HTTP message and construct an HTTPServletRequest object as a result. By examining the requested resource /publisher/publish, Tomcat determines to route the message to the publish servlet of the publisher application.

After sending the HTTP request message to the publisher application, it is time read the response message returned by the service.

InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);

We start by getting a reference to the input stream from the socket, which allows us to read raw bytes being returned to use through the TCP connection represented by the socket. We wrap the input stream in an input stream reader. This class converts the incoming stream of bytes into a stream of characters. The two streams mentioned so far do not do any buffering. We then wrap the input stream reader in a buffered reader, which allows us to read a line at a time. A line is a sequence of characters terminated by a new line character. Also, if the end of stream occurs, then the line can end this way as well.

We then enter a loop that reads through the header lines of the HTTP response message. HTTP header lines are terminated by an empty line, which is the condition we use to terminate the loop.

while (true)
{
   String line = br.readLine();
   if (line.length() == 0) break;
}

Once we are at the start of the HTTP message data, we are ready to parse the XML documment that the service is returning. We do this by calling the build method on the SAX builder object that we used at an earlier point to create the outgoing XML document.

Document responseDoc = null;
try 
{
   responseDoc = builder.build(br);
} catch (JDOMException e) {
   throw new RuntimeException(e);
}

Recall that the document returned by the service looks like the following, minus any XML prolog (XML syntax preceding the root element).

<id>3<id>

Thus, once we have an object representing the XML document, we simply need to extract the text contents of its root element to obtain the id of the newly created news item.

Element idElement = responseDoc.getRootElement();
String id = idElement.getText();
br.close();

We then close the buffered reader, which closes the underlying input stream, and we return the extracted id to the calling code.

Note that in this example code we do not handle communication failures or errors internal to the web service. In a real application, additional work would be needed to handle these failures.

Use the manager application to restart the wiki application. Then verify that all three applications -- website, publisher and wiki -- function as expected.

Open three different browser windows and point them to the following three pages.

Click the publish operation on the hello wiki page. Refresh the list news items page to see the hello page appear in the list. Refresh the website page to see the new news item appear there as well.

back next

Copyright 2007-2009 David Turner and Jinseok Chae. All rights reserved.