My blog has moved! Redirecting...

You should be automatically redirected. If not, visit http://mindsiview.wordpress.com/ and update your bookmarks.

Wednesday, January 31, 2007

Turning Servlets into Web Services

Introduction

It is relatively easy to build Java servlets that can parse and return XML documents with the help of the BorlandXML toolset found in JBuilder Developer and Enterprise editions. BorlandXML provides s a mechanism for extracting data from XML documents and creating XML documents using JavaBeans. In this article, we will use the BorlandXML toolset to build a servlet based web service that intercepts XML requests from the Internet and returns a XML response.

Some Housekeeping

As you are already aware, web services are used to send and receive XML files across the Internet. Let's look at an example. Assume that we are a product distributor. Aside from providing a web site for our customers, we want to build a facility to allow retail Point of Sale systems to query our product database over the web in order to check inventory quantities. To accomplish this task we're going to build a web service. For the web service we'll need two XML files: request.xml and response.xml.

request.xml

<?xml version="1.0"?>
<StockCheckRequest$>
<ProductNumber>0785263268</ProductNumber>
</StockCheckRequest>

The request.xml file will be used by the POS system to send us the product numbers. Without going into a long discourse on XML, suffice it to say, much like HTML, XML files are made up of elements (or tags) and data. Tags are denoted by the “<” “>” braces. Like HTML, XML tags are nested within one another. Also like HTML, XML tags begin contain a label between the braces that describes the information that follows. They end with a tag that “/” before the label. Unlike the typical HTML file, XML tags typically have custom labels that are either defined by an application (read web service) or in some cases an agreed upon standard.

Let's examine the structure of request.xml. It contains a set of XML tags called StockCheckRequest. Within the StockCheckRequest tags there is a set of tags for ProductNumber. It is between these tags that we define the data (in this case a product number) that we want to send.

Note: we can define multiple ProductNumber tags within the StockCheckRequest tag as long as we program for it.

Now let's take a look at the response.xml file.

response.xml

<?xml version="1.0"?>
<StockCheckResponse>
<Header>
 <FromCompany> </FromCompany>
 <FromPerson> </FromPerson>
 <FromEmail> </FromEmail>
 <SentDate> </SentDate>
</Header>
<Product>
<ProductNumber> </ProductNumber>
<Title> </Title>
<Description> </Description>
<Stock>
<OnHand> </OnHand>
<OnOrder> </OnOrder>
</Stock>
<PackQuantity> </PackQuantity>
<Price> </Price>
</Product>
</StockCheckResponse>

response.xml file is the file that we will be sending back to the retail POS system. As you can see it contains a set of tags called StockCheckResponse. Within this set of tags there is a set of tags called Header that contains information about our company. Next we have a set of Product tags that tell the POS system about the product they requested.

Now that we've established our XML, let's take a look at our project.

bdn.webservices

I assume you are already familiar with building projects, servlets, etc. in JBuilder. If not, you should familiarize yourself with the JBuilder environment before proceeding. That being said, we're creating a new project in JBuilder called bdn.webservices. We're going to add our XML files, request.xml and response.xml, to the project. Now let's have some fun.

In order to use our XML files within Java, we need a way to translate them. BorlandXML contains some handy tools for manipulating XML files. The first tool we'll look at is the DTD (Document Type Definition) generator. The purpose of a DTD is to define the XML's document structure with a list of legal elements. In order for applications to parse XML files, there must be a DTD associated with the file.

Step 1

To activate the generator we select right click the request.xml file in JBuilder's Project pane (see above) and select the Generate DTD menu option. We will see the XML to DTD Wizard window:

Click OK to generate the request.dtd file.

Step 2

Now that we've generated a DTD file, we can use another handy BorlandXML tool: the Java generator. When you right click on request.dtd in the Project pane, you'll see the following:

Select Generate Java. You should see the Databinding Wizard – Step 1 of 2 window.

Step 3

In this window, you have to option of selecting the Databinding framework: either BorlandXML or Castor. For this project, we're going to use BorlandXML. Select BorlandXML and click Next >. You will now see the Databinding Wizard – Step 2 of 2 window.

Step 4

This window allows us to select which DTD element we want to generate a Java class for. Refer back to our request.xml file. The root element for the XML file is StockCheckRequest. ProductNumber is a child element. Therefore we want to select StockCheckRequest and click Finish.

Looking at the Project pane, you can see that we now have new JavaBean objects: StockCheckRequest.java and ProductNumber.java. If you look at the Structure pane (found on the bottom left side of the JBuilder window), you'll notice lots of errors. These errors can be resolved by building the project.

Next, we'll need to repeat steps 1-4 for the response.xml file. Remember to select the StockCheckResponse element when generating the Java source code. Build the project to get rid of errors.

Now that we've created some JavaBeans we're going to build a servlet called StockCheck.java.

StockCheck.java

I'm going to assume that you are already familiar with building servlets in JBuilder. If not, I suggest that you read through the JBuilder help files. Let's take a look at the code.


package bdn_webservice;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import java.text.*;

public class StockCheck
extends HttpServlet {
// global variables
private static final String CONTENT_TYPE = "text/html";
String ProductStr;
StringBuffer instr;

public void init() throws ServletException {

}

//Process the HTTP Get request
public void doPost(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {

StockCheckRequest scRequest = new StockCheckRequest();
scRequest.unmarshal(request.getInputStream());
ProductStr = scRequest.getProductNumberText();

StockCheckResponse scResponse = new StockCheckResponse();
Header header = new Header();
header.setFromCompanyText("ACME Stuff Distributors, Inc.");
header.setFromEmailText("tech_dude@yahoo.com");
header.setFromPersonText("Rick Proctor");
SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd");
java.util.Date date = java.util.Calendar.getInstance().getTime();
header.setSentDateText(sf.format(date));

scResponse.setHeader(header);

Product product = new Product();
product.setDescriptionText("product description");
product.setPackQuantityText("12");
product.setPriceText("24.99");
product.setProductNumberText(ProductStr);
product.setTitleText("ACME Thing");

scResponse.setProduct(product);

response.setContentType(CONTENT_TYPE);
PrintWriter out = response.getWriter();
scResponse.marshal(out);

}

public void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
}

//Clean up resources
public void destroy() {
}
}

Take a look at the doPost() method. This method will be executed when a calling program sends a request.xml file to the servlet (we will see an example of this a little later). In the doPost() method we start by creating an instance of the StockCheckRequest object (see the request.xml section) called scRequest. Next we call the scRequest.unmarshal() method, passing an inputStream() object request...the request object is passed to the doPost() method as an input variable.

Note: BorlandXML uses a “marshalling framework” to convert XML documents. To turn XML into JavaBean objects use the unmarshal() method. To convert JavaBean objects to XML use the marshal() method. For more information see the JBuilder help documentation.

Now that we've converted the request.xml document, we can extract the ProductNumber field. In this case there are two methods that we can choose. The first method is getProductNumber(). This method returns a ProductNumber object. The second method getProductNumberText() returns a String object containing the ProductNumber data. In this case, we'll use the second method.

Building response.xml

Now, that we've received and parsed through the request.xml file, it's time to build and send a response. In the real world, we'd probably validate the ProductNumber field and bounce it against a database in order to retrieve information about the product. This is only an article, not the real world, so we'll just plug the data in.

The first thing we do is instantiate a StockCheckResponse object scResponse. This object will correspond to our response.xml file. The response.xml file we defined earlier had 3 levels of elements. At the top, we defined StockCheckResponse. Next we defined a Header element with detail elements underneath. Finally, we defined a Product element, with detail elements under it.

We instantiate a Header object header. Next, we call the set…Text() methods to load data into our header object. We call the scResponse.setHeader() method passing the header object. We instantiate a Product object product and call its set…Text() methods to load product specific data. Afterwards we call the scResponse.setProduct()method passing the product object.

Now that we've built a StockCheckResponse object, we need to convert it to an XML document and send the XML as a response. One method of accomplishing this task is to instantiate a PrintWriter object called out. This will create an output stream for us to write the XML file to. Next we call the marshal() method of our scResponse object using out. As mentioned earlier, the marshal() method converts our Javabean object to XML.

Congratulations. If you got this far, you've now converted a servlet into a web service. Now that we've built a web service, how do we test it? Let's take a quick look at a java class, TestStock.java, we can use to test our web service.

TestStock.java


package bdn_webservice;
import java.net.*;
import java.io.*;

public class TestStock {
public static void main(String[] args) {
HttpURLConnection connection;
URL url;
FileInputStream in;
InputStreamReader isr;
StringBuffer data = new StringBuffer();
try {

in = new FileInputStream("request.xml");
isr = new InputStreamReader(in);
int c;
while((c=isr.read())!=-1) {
data.append((char) c);
}
isr.close();
in.close();

url = new URL("http://localhost:8080/WebServiceMod/stockcheck");

connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setUseCaches(false);
OutputStream out = connection.getOutputStream();
String queryString = data.toString();
System.out.println(queryString);
out.write(queryString.getBytes());
out.close();
InputStream in1 = connection.getInputStream();
isr = new InputStreamReader(in1);
StringBuffer buffer = new StringBuffer();
try {
int letter = 0;

while ( (letter = isr.read()) != -1) {
  buffer.append( (char) letter);
  System.out.print((char) letter);
}
}
catch (Exception e) {
System.out.println("Cannot read from URL" +
e.toString()); }
finally {
try {
  isr.close();
}
catch (IOException io) {
  System.out.println("Error closing URLReader!");
}
}
connection.disconnect();
}
catch (Exception e) {
System.out.println(e);
}
}
}

As you can see, TestStock.java does the following:

  • Reads the request.xml file
  • Establishes a Http connection with the StockCheck servlet
  • Writes to request.xml file to the Http connection
  • Reads the response.xml file from Http connection
  • Writes the response.xml file to the console.

Now let's run our example. Outside of JBuilder we would fire up an application server such as Tomcat, make sure the StockCheck servlet was loaded, and then fire up the TestStock class. Since we're running this example in JBuilder, we'll do it a little differently. To start up Tomcat all we need to do is right click on The StockCheck tab, select Web Run from the menu and select use StockCheck. We will see something like this:

Now that our web service has started, we can execute the TestStock class. Right click the TestStock tab, and select Run using defaults. You should see the following results:

Looking at the highlighted areas in JBuilder's message pane, it is easy to see that we sent a request to the servlet. The servlet returned a response containing the information we wanted.

Conclusion

With JBuilder's BorlandXML tools, it is easy to turn a servlet into a web service. BorlandXML allows you to take a XML file, convert it to a DTD (Data Type Definition) file, and then create JavaBeans. Using the getters and setters, you can read data from an inbound XML file and send outbound data via XML tags. I hope this article sparks your imagination on ways you can convert your own servlets into a web service.


No comments:

Technorati search