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.