/*
* Copyright (c) 2006-2007 Massachusetts General Hospital
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the i2b2 Software License v1.0
* which accompanies this distribution.
*
* Contributors:
* Mike Mendis - initial API and implementation
* Bastian Weinlich - Adaption to GIRIXCell
*/
package de.hpi.i2b2.girix;
import de.hpi.i2b2.girix.datavo.i2b2message.RequestMessageType;
import de.hpi.i2b2.girix.datavo.i2b2message.ResponseMessageType;
import edu.harvard.i2b2.common.exception.I2B2Exception;
import org.apache.axiom.om.OMElement;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
// This class is a modification of the PFTService class of the main i2b2 project
// It is the entry point for the two possible GIRIXCell requests
public class GIRIXService {
private static Log log = LogFactory.getLog(GIRIXService.class);
// This method parses incoming getRScriptlets requests, checks the header and sets the appropriate handler
public OMElement getRScriptlets(OMElement getRScriptletsDataElement) throws I2B2Exception {
log.info("Incoming getRScriptlets request");
GIRIXUtil.initializeGIRIXUtil();
// Check for errors
if (getRScriptletsDataElement == null) {
log.error("Incoming request is null");
ResponseMessageType responseMsgType = MessageUtil.doBuildErrorResponseMessageType(null, "Error message delivered from remote server: Incoming request is null");
String girixDataResponse = MessageUtil.convertResponseMessageTypeToXML(responseMsgType);
return MessageUtil.convertXMLToOMElement(girixDataResponse);
}
// Convert request to RequestMessageType instance (jaxb)
RequestMessageType msg = null;
try {
msg = MessageUtil.convertXMLTORequestMessageType(getRScriptletsDataElement.toString());
} catch (I2B2Exception e) {
log.error("Incoming XML request is invalid", e);
ResponseMessageType responseMsgType = MessageUtil.doBuildErrorResponseMessageType(null, "Error message delivered from remote server: Incoming XML request is invalid");
String girixDataResponse = MessageUtil.convertResponseMessageTypeToXML(responseMsgType);
return MessageUtil.convertXMLToOMElement(girixDataResponse);
}
// Set appropriate handler
RequestHandler handler = new GetRScriptletsRequestHandler();
// Call execute to handle request in a new thread
OMElement ret = execute(handler, msg);
log.info("Outgoing getRScriptlets response");
return ret;
}
// This method parses incoming getRResults requests, checks the header and sets the appropriate handler
public OMElement getRResults(OMElement getRResultsDataElement) throws I2B2Exception {
log.info("Incoming getRResults request");
GIRIXUtil.initializeGIRIXUtil();
// Check for errors
if (getRResultsDataElement == null) {
log.error("Incoming request is null");
ResponseMessageType responseMsgType = MessageUtil.doBuildErrorResponseMessageType(null, "Error message delivered from remote server: Incoming request is null");
String girixDataResponse = MessageUtil.convertResponseMessageTypeToXML(responseMsgType);
return MessageUtil.convertXMLToOMElement(girixDataResponse);
}
// Convert request to RequestMessageType instance (jaxb)
RequestMessageType msg = null;
try {
msg = MessageUtil.convertXMLTORequestMessageType(getRResultsDataElement.toString());
} catch (I2B2Exception e) {
log.error("Incoming XML request is invalid", e);
ResponseMessageType responseMsgType = MessageUtil.doBuildErrorResponseMessageType(null, "Error message delivered from remote server: Incoming XML request is invalid");
String girixDataResponse = MessageUtil.convertResponseMessageTypeToXML(responseMsgType);
return MessageUtil.convertXMLToOMElement(girixDataResponse);
}
// Set appropriate handler
RequestHandler handler = new GetRResultsRequestHandler();
// Call execute to handle request in a new thread
OMElement ret = execute(handler, msg);
log.info("Outgoing getRResults response");
return ret;
}
// This method delegates the request processing to a new thread while stopping the time to a given timeout
// If the thread hasn't finished until timeout or another error (Exception) occurred during processing, an error response message will be created
private OMElement execute(RequestHandler handler, RequestMessageType message) throws I2B2Exception {
// Extract wait time. If no waitTime is given it defaults to 0 which is equivalent to an infinite waitTime
long waitTime = 0;
if (message.getRequestHeader() != null) {
waitTime = message.getRequestHeader().getResultWaittimeMs();
}
// Do query processing inside thread, so that service could send back message with timeout error
String unknownErrorMessage = "Error message delivered from the remote server: Unknown exception. See log file for stack trace.";
ExecutorRunnable er = new ExecutorRunnable();
er.setInput(message);
er.setRequestHandler(handler);
Thread t = new Thread(er);
String girixDataResponse = null;
// Start thread...
synchronized (t) {
t.start();
// ...meanwhile in main thread: count time and check for timeout
try {
long startTime = System.currentTimeMillis();
long deltaTime = -1;
while ((er.isJobCompleteFlag() == false) && (deltaTime < waitTime)) {
if (waitTime > 0) {
t.wait(waitTime - deltaTime);
deltaTime = System.currentTimeMillis() - startTime;
} else {
// wait until job is completed
t.wait();
}
}
// Now try to extract the result...
girixDataResponse = er.getOutputString();
// ...which is null if there was an error
if (girixDataResponse == null) {
// Error case 1: There was an exception during thread execution
if (er.getJobException() != null) {
// Error case 1.1: Causing exception was set -> Default unknown error message & logging stack trace
if (er.getJobException().getCause() != null) {
log.error("Exception stack trace:\n" + GIRIXUtil.getStackTraceAsString(er.getJobException()));
ResponseMessageType responseMsgType = MessageUtil.doBuildErrorResponseMessageType(null, unknownErrorMessage);
girixDataResponse = MessageUtil.convertResponseMessageTypeToXML(responseMsgType);
} else {
// Error case 1.2: Causing exception wasn't set -> Custom error message. Logging is done by throwing method
ResponseMessageType responseMsgType = MessageUtil.doBuildErrorResponseMessageType(null, er.getJobException().getMessage());
girixDataResponse = MessageUtil.convertResponseMessageTypeToXML(responseMsgType);
}
// Error case 2: Timeout
} else if (er.isJobCompleteFlag() == false) {
String timeOuterror = "Remote server timed out \nResult waittime = " + waitTime + " ms elapsed\nPlease try again";
log.error(timeOuterror);
log.debug("girix waited " + deltaTime + "ms for " + er.getRequestHandler().getClass().getName());
ResponseMessageType responseMsgType = MessageUtil.doBuildErrorResponseMessageType(null, timeOuterror);
girixDataResponse = MessageUtil.convertResponseMessageTypeToXML(responseMsgType);
// Error case 3: Result was set to null by the thread
} else {
log.error("girix data response is null");
log.debug("girix waited " + deltaTime + "ms for " + er.getRequestHandler().getClass().getName());
ResponseMessageType responseMsgType = MessageUtil.doBuildErrorResponseMessageType(null, "Error message delivered from the remote server: Result was set to null.");
girixDataResponse = MessageUtil.convertResponseMessageTypeToXML(responseMsgType);
}
}
} catch (InterruptedException e) {
log.error(e.getMessage());
throw new I2B2Exception("Thread error while running GIRIX job");
} finally {
t.interrupt();
er = null;
t = null;
}
}
// Send back answer. girixDataResponse contains either an error message or the proper response if there was no critical error
return MessageUtil.convertXMLToOMElement(girixDataResponse);
}
}