Beautiful Code [303]
PrintWriter out = resp.getWriter();
out.println(hotkey.getResponseXML());
out.close();
success = true;
}
}
}
if (!success) {
resp.setContentType("text/xml");
PrintWriter out = resp.getWriter();
out.println("Error retrieving product availability.");
out.close();
}
}
}
Looking through this code, you can see that it first reads in the request data and stores it for later use. It then searches this data to determine which type of request this is: pricing and availability, or an order status inquiry. Once it determines the type of request, the appropriate helper object is created. Notice how I used an interface, HotkeyAdaptor, to allow multiple implementations without having to write a bunch of duplicate code for each type of request.
The rest of this method involves parsing the XML request data, executing the appropriate query on the AS/400 system, creating an XML response, and writing it back to the user via HTTP. In the next section, you'll see how I hid the implementation details using interfaces and the very popular factory design pattern.
Integrating Business Partners the RESTful Way > Routing the Service Using the Factory Pattern
27.3. Routing the Service Using the Factory Pattern
One of the requirements of this system was that it be able to accommodate a wide variety of future requests from several different types of systems with minimal programming effort. I believe I accomplished this by simplifying the implementation down to a single command interface that exposed the basic methods needed to respond to a wide variety of requests:
public interface HotkeyAdaptor {
public void setXML(String _xml);
public boolean parseXML();
public boolean executeQuery();
public String getResponseXML();
}
So, how does the servlet decide which concrete implementation of the interface to instantiate? It first looks inside the request data for a specific string to tell it what type of request it is. Then, it uses a static method of a factory object to pick the appropriate implementation.
As far as the servlet knows, whatever implementation we're using will provide appropriate responses to each of these methods. By using an interface in the main servlet, we only have to write the execution code once, without any regard to which type of request it's dealing with or who may have made the request. All of the details are encapsulated in each individual implementation of this interface. Here's that snippet of code again from the servlet:
HotkeyAdaptor hotkey = null;
if (sb.toString().indexOf("Pip3A2PriceAndAvailabilityRequest") > 0) {
// Price and Availability Request
hotkey = HotkeyAdaptorFactory.getAdaptor(
HotkeyAdaptorFactory.ROSETTANET,
HotkeyAdaptorFactory.PRODUCTAVAILABILITY);
}
else if (sb.toString().indexOf("Pip3A5PurchaseOrderStatusQuery ") > 0) {
// Order Status
hotkey = HotkeyAdaptorFactory.getAdaptor(
HotkeyAdaptorFactory.ROSETTANET,
HotkeyAdaptorFactory.ORDERSTATUS);
}
The factory object, HotkeyAdaptorFactory, has a static method that takes two parameters telling it which XML protocol to use and what type of request it is. These are defined as static constants in the factory object itself. As you can see by the following code, the factory object simply uses a switch statement to select the appropriate implementation:
Code View: Scroll / Show All
public class HotkeyAdaptorFactory {
public static final int ROSETTANET = 0;
public static final int BIZTALK = 1;
public static final int EBXML = 2;
public static final int PRODUCTAVAILABILITY = 0;
public static final int ORDERSTATUS = 1;
public static HotkeyAdaptor getAdaptor(int _vocab, int _target) {
switch (_vocab) {
case (ROSETTANET) :
switch (_target) {
case (PRODUCTAVAILABILITY) :
return new HotkeyAdaptorRosProdAvailImpl();
case (ORDERSTATUS) :
return new HotkeyAdaptorRosOrdStatImpl();
default :
return null;
}
case (BIZTALK) :
case (EBXML) :
default :
return null;
}
}
}
While this