Please bear with me, if this type of question has been asked before. I am having a dilemma with object design and seeking experts' help here to come up with the correct/better design for a webservice client. I am attaching a crude UML diagram below:
I have been writing/fixing Java code, but just getting into object design/architecture. I am writing a webservice client in Java to integrate a commercial Address Validation Geocoding Webservice into our application. Ours is a Tax software written in PowerBuilder running inside EAServer. I am writing the web service client in Java, so it can be integrated with the PB client through CORBA.
I have done a similar service earlier to get some GIS information and this was slightly simpler design handling only XML for both Request and Response. I handled getting/sending request and response in the controller itself.
I started using the same design and hit a road block: current web service handles both XML and REST (through HTTP GET URL); actually, there are 2 webservice urls, the one for Address Validation/Geocoding supports XML or REST, and the other for searching addresses uses REST only. I want to design a generic request abstract class/interface and the controller that can be used for both the services. While trying to come up with the classes, I ran into a classic design issue. Now with Request itself can be a XML string or a URI object (for REST Request), my approach of getting the request and sending it in Controller seems flawed.
Should I make the Request object itself build and send XML/REST itself to the external Service or should I somehow manage to get different types of Requests into the Controller and send it from there (using getRequest, sendRequest)?
Also, I am learning to use Design Patterns in Java. What type of design pattern would be applicable here? I saw command pattern suggested for a similar client. Also, can MVC pattern be applied here?
I just want to get it right, before I started coding.
UPDATE:
My Address Record to send as part of request is,
public class AddressRecord {
protected String AddressLine1;
protected String AddressLine2;
protected String City;
protected String State;
protected String PostalCode;
protected String Country;
...
}
And my request object is,
public class Request {
protected AddressRecord records[];
protected ArrayList actions;
protected ArrayList columns;
protected Properties options;
protected String transmissionReference;
protected String customerId;
....
}
It can have one or addresses (I am planning to send only one for now). It also allows to specify the exact columns needed, actions to be done, Options etc.
And my XMLRequest extends,
public class RequestXml extends Request {
..
public String buildRequest()
{
...
}
And my problem came when I try to do REST Request similarly,
public class RequestREST extends Request {
..
public URI buildRequest()
{
...
}
Depending on the columns chosen, the address will be validated and the service will optionally pass back the Lat/Long for the address, if found. A typical Address Request and Response may contain one or more of the following record (in XML format):
Request record
<Request> <Actions>Check</Actions> <Columns /> <CustomerID>1234567890</CustomerID> <Options /> <Records> <RequestRecord> <AddressLine1>200 Main St</AddressLine1> <AddressLine2 /> <City>Los Angeles</City> <PostalCode>90012</PostalCode> <State>CA</State> <Country>USA</Country> <CompanyName></CompanyName> <EmailAddress /> <FirstName /> <FreeForm /> <FullName></FullName> <LastLine /> <LastName /> <PhoneNumber></PhoneNumber> <RecordID>1</RecordID> </RequestRecord> <TransmissionReference /> </Records> </Request>
Response record: Will contain validated and standard formatted address record.
<ResponseRecord> <AddressLine1>200 Main St</AddressLine1> <AddressLine2></AddressLine2> <City>Los Angeles</City> <State>CA</State> <CompanyName></CompanyName> <EmailAddress></EmailAddress> <NameFull></NameFull> <PhoneNumber></PhoneNumber> <PostalCode>90012</PostalCode> <RecordExtras></RecordExtras> <RecordID>1</RecordID> <Reserved></Reserved> <Results>AS01</Results> <AddressExtras></AddressExtras> <AddressKey>92688211282</AddressKey> </ResponseRecord>
I am probably stretching it too much. Like @Vladislav suggested, I could probably make buildRequest in URI request also return a String, and that might solve this issue; that can get this job done, but I am trying to learn to do it the proper way of identifying objects and design.
UPDATE2:
@Vladislav, This is what I had for an earlier Webservice client. They called their service GeoQuery, so I named it BoeQueryManager. Does this qualify to be the transport class you are referring to? I think this qualifies to be the Controller. The GeoQuery_MyLAService, generated by WSDL2Java may be the Transport object you referred to? I have redone the diagram, for the new program, to reflect the changes I see per my new understanding of your comments. Can you please validate that?
FYI, our address validation is done in 3-parts - Address Validation/Search, Geocode and once we get the Lat/Long, do the internal GIS function to get city layers. I already did the 3rd part, now working on replacing the 1st and 2nd services.
public class BoeGeoQueryManager {
private void init(String aUrlStr) {
try {
URL endPoint = new URL(aUrlStr);
GeoQuery_MyLAService service = new GeoQuery_MyLAServiceLocator();
geoq = service.getGeoQuery_MyLAcfc(endPoint);
} catch (MalformedURLException me) {
logger.error("BOEGIS", "", this.toString() + " - " + "init", "Error initializing. " + me.getMessage());
me.printStackTrace();
} catch (ServiceException se) {
logger.error("BOEGIS", "", this.toString() + " - " + "init", "Error initializing Service. " + se.getMessage());
se.printStackTrace();
}
}
// Search by Address
public String queryBoeGis(String aAddress) throws Exception {
BoeXMLRequest lRequest = new BoeXMLRequest(aAddress, 0, 0, GISRequest.SEARCH_MODE_ADDRESS);
return queryBoeGis(lRequest);
}
// Search by Lat/Long
public String queryBoeGis(double aLatitude, double aLongitude) throws Exception {
BoeXMLRequest lRequest =
new BoeXMLRequest(GISRequest.REQUEST_BLANK_ADDRESS, aLatitude, aLongitude,
GISRequest.SEARCH_MODE_LATLONG);
return queryBoeGis(lRequest);
}
// 3. The controller is doing the Webservice call here. I just pass in
// the Request object into this. Controller is fully aware it's a XML
// Request/Response.
public String queryBoeGis(BoeXMLRequest aRequest) throws Exception {
String respXML = "";
String reqXML = "";
requestCount++;
try {
reqXML = aRequest.getXml();
if (aRequest.isModeLatLong()) {
respXML = geoq.qByCoords(reqXML);
} else if (aRequest.isModeAddress()) {
// search by address (or both)
respXML = geoq.addressValidationService(reqXML);
}
respXML = XMLDocument.cleanup(respXML);
BoeXMLResponse boeResponse = new BoeXMLResponse(respXML);
// To be able to get a flattened XML to be sent to the Legacy application
if (adjustXML)
respXML = XMLExtractor.getAdjXml(aRequest, boeResponse);
} catch (Exception e) {
e.printStackTrace();
respXML = "";
}
// Otherwise, return the original XML
return respXML;
}
}