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.


Tuesday, January 30, 2007

Using the Preferences API

Like many Java developers, I tend to find something that works for me and use it for years without researching better approaches. For example, I've used the Properties class for years to load and save configuration information in my Java programs. The Properties class works fine, but does have some drawbacks.

Last week I stumbled across another API, the Preferences class, that's been available since 1.4. It can be used the same way as the Properties class, to load and save configuration stuff. The big difference between it and Properties is that the Preference class allows you to store data in a consistent format, it allows you to take advantage of centralized repositories such as the Windows registry, and provides more flexibility with regards to storing the preferences in user folders.

Without going into a lot of detail, I've created a simple swing application that demonstrates the basics of the Preferences class. All the program TestPrefs does is save an loads the most recent window size and location.

Looking at the code below, I've highlighted the Preferences api calls for you. As you can see, the first thing to do is to decide where you want the preferences file to be located. In this case, I've chosen user root, which means that I've decided to store the preferences file under the user tree rather than the system tree. Next, I've told the program to store the file in the root folder of TestPrefs class. You can also specify a folder with something like node = root.node("/com/prefs/...").

Now that the class knows where to locate the preferences file, how do we write information to it? Looking at the windowClosing() method, you'll see node.putInt(..). The putInt() method has two parameters: name and value. For example, if you wanted to save a frame's width, you might create a name value/pair like this: node.putInt("width", jFrame.getWidth()).

Next, we want to read information from the preferences file and use it to position our window on the next execution of the program. looking at the getJFrame() method, you can see the jFrame.setSize() method. This time I use node.getInt(). Like putInt(), getInt() has a name/value pair. This time it works a little differently. In this case, the value parameter acts as a default value.The name parameter tries to find the associated value within the preferences file. If it can't, then the value parameter is used.

There are other methods associated with the Preferences api, and I encourage you to explore these. Hopefully, I've whetted your appetite enough to get started.

___________________

package test;

import java.awt.BorderLayout;
import javax.swing.SwingUtilities;
import java.util.prefs.Preferences;

import javax.swing.JPanel;
import javax.swing.JFrame;

public class TestPrefs {

private JFrame jFrame = null;

private JPanel jContentPane = null;

public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
public static final int DEFAULT_LEFT = 0;
public static final int DEFAULT_TOP = 0;

Preferences root = Preferences.userRoot();
 final Preferences node = Preferences.userNodeForPackage(this.getClass());

/**
* This method initializes jFrame
*
* @return javax.swing.JFrame
*/
private JFrame getJFrame() {
if (jFrame == null) {
jFrame = new JFrame();
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(node.getInt("width", DEFAULT_WIDTH),node.getInt("height", DEFAULT_HEIGHT));
jFrame.setLocation(node.getInt("left",DEFAULT_LEFT), node.getInt("top",DEFAULT_TOP));
jFrame.setContentPane(getJContentPane());
jFrame.setTitle("Test Preferences API");
jFrame.addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
node.putInt("left", jFrame.getX());
     node.putInt("top", jFrame.getY());
     node.putInt("width",jFrame.getWidth());
     node.putInt("height", jFrame.getHeight());
}
});
}
return jFrame;
}

/**
* This method initializes jContentPane
*
* @return javax.swing.JPanel
*/
private JPanel getJContentPane() {
if (jContentPane == null) {
jContentPane = new JPanel();
jContentPane.setLayout(new BorderLayout());
}
return jContentPane;
}



/**
* Launches this application
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
TestPrefs application = new TestPrefs();
application.getJFrame().setVisible(true);
}
});
}

}

Wednesday, January 10, 2007

Friday, January 05, 2007

New Blog "The Business of Technology"

Over time, this blog has been going in a couple of different directions. From the emails/comments I've received, most who visit this blog are looking for solutions to specific Java or other technology issues. While some of my posts have been related to these issues, others have focused more on managing IT. So, starting today I'm launching a new blog The Business of Technology. This blog will be solely dedicated to IT management issues. I'll continue to post Java solutions and other "techie" items on the Tech Thoughts blog.

I hope you enjoy the new blog. Thanks, Rick.

Technorati search