/* * 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); } }