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:
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 may seem to be a rather simple abstraction, it goes a long way in making the code readable and understandable by an inexperienced programming staff. When it comes time to add a new distributor that happens to be using Microsoft's BizTalk product and wants to place orders electronically, the programmer has a simple template for adding this new requirement.