RequestServer (3.b Unique Callback)
In this pattern, RequestServer has a third servlet to get weather information from WeatherClient, as in the pattern 3.a above. The difference is that it uses the callback Java Driver API to achieve the same thing. The Java code that initiates the request can do other things when waiting for the reply.
In this sample code (getWeatherUniqueCallback.java), the Java code:
- Instantiates an anonymous class that implements the CallbackInteface interface.
- Passes the anonymous
class as a parameter to the static
sendToCloverleafWithUniqueCallback()
method. This manages sending the message to the system engine and invokes the configured Java callback when the reply comes back from the WeatherClient thread.
This sample is one example of the callback mechanism, unique callback. With this, you can specify a unique instance of the callback class as the handler of this particular reply. This is opposed to a registered callback, which is described in another sample.
When the reply comes back, the doCallback()
method of the anonymous class is invoked to construct the
appropriate response back to the browser.
package com.infor.cloverleaf.javadriver.samples.requestserver;
protected void processRequest(final HttpServletRequest request, final HttpServletResponse response)
throws ServletException, IOException {
final String zipCode = request.getParameter("ZIP_CODE");
if (!zipCode.matches("[0-9]{5}(-[0-9]{4})?"))
throw new ServletException("The zip code field must at least look like a zip code. You provided '" + zipCode + "'.");
String driverControl = "{MSG_KEY " + zipCode + "}";
FromCloverleafMessage weatherResponse;
// keep track if the callback has done the work or not
final StringBuffer status = new StringBuffer();
try {
// create the message
ToCloverleafMessage toCloverleafMessage = new ToCloverleafMessage(request.getParameter("USER_DATA"), driverControl, MessageTypeEnum.DATA, "GET_THE_WEATHER", null, zipCode);
// create a callback which is connected to the response stream
/* Note: Being connected to a stream is a good example of when you should use a unique callback
* because if the JVM goes down the stream is lost anyway.
* So it wouldn't matter if the callback class survived the JVM restart because the stream
* won't anyway. A registered handler would work, but if you didn't unregister each one after use
* then the list of registered handlers would increase with every call.
*/
CallbackInterface callback = new CallbackInterface(){
public List<ToCloverleafMessage> doCallback(FromCloverleafMessage fromCloverleafMessage) throws BadDataException, RetryException {
// synchronize on the status object to avoid a tiny race condition
synchronized (status){
// if the message already timed out, this message will never succeed-
// notify Cloverleaf of the permanent failure
if (status.toString().equals("done"))
throw new BadDataException("the timeout already elapsed for this message, it cannot be processed anymore");
// otherwise continue
try {
// great, we got a callback, print the message out
writeResponse(response, fromCloverleafMessage, zipCode);
status.append("done");
} catch (Exception ex) {
// set status so user know something went wrong
status.append("callback caught exception: " + ex.getLocalizedMessage());
throw new BadDataException("failed to write out response to the HTTP servlet response object due to exception, giving up", ex);
} finally {
// wake up the request thread if it's still waiting
status.notifyAll();
}
}
// if no exception then everything went fine, no message to send back to CL
return null;
}
};
// have the message and the callback handler ready, send to Cloverleaf
ToCloverleafLink.sendToCloverleafWithUniqueCallback(callback, true, toCloverleafMessage);
} catch (Exception ex) {
throw new ServletException ("failed to either create the message or send it to Cloverleaf", ex);
}
synchronized(status){
try {
// make the request thread wait (using the status object for communication)
// for a reply for 20 seconds
status.wait(20000);
} catch (InterruptedException ex) {
// we don't care if it was interrupted or timed out here, either way we
// process it the same
}
// if we're not done already, it means the callback is too slow
if (!status.toString().equals("done")){
// set status so if callback does come eventually,
// it'll know it's not supposed to bother the thread with whatever
// it's current doing
status.append("done");
// throw an exception so the user gets a 500 error
throw new ServletException("timed out waiting for a callback!");
}// else the response is already written, nothing to do
}
}
}