/*
* Created on : 07-11-2013
* Author : Bastian Weinlich
*/
package de.hpi.i2b2.girix;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.SAXException;
import com.sun.jna.Library;
import com.sun.jna.Native;
import de.hpi.i2b2.girix.datavo.girixconfig.RscriptletType;
import de.hpi.i2b2.girix.datavo.girixmessages.InputParameterType;
import edu.harvard.i2b2.common.exception.I2B2Exception;
import edu.harvard.i2b2.common.util.jaxb.JAXBUtil;
// Helper class with some useful methods and constants
public class GIRIXUtil {
// Needed for jaxb initialization
private static final String[] DEFAULT_PACKAGE_NAME = new String[] {
"de.hpi.i2b2.girix.datavo.i2b2message",
"de.hpi.i2b2.girix.datavo.pdo",
"de.hpi.i2b2.girix.datavo.pdo.query",
"de.hpi.i2b2.girix.datavo.girixconfig",
"de.hpi.i2b2.girix.datavo.girixmessages"
};
private static Log log = LogFactory.getLog(GIRIXUtil.class);
private static boolean initialized = false;
private static JAXBUtil jaxbutil = null;
// This is the separator character used for csv string that is imported into R
public final static String SEP = ";";
// testingmode is for local testing without binding aar/jar packages and deploying it to jboss. Just call a service method in GIRIXService
// from a testing main class. Note that testingmode won't work in jboss and non-testingmode won't work locally!
private static boolean testingmode = false;
// Remains null if not in testingmode
private static String BUILDPROPERTIESPATH = null;
// Path to the scriptlet directory
private static String RSCRIPTLETPATH = null;
// Path to the XML schema (.xsd) file for config.xml files
private static String CONFIGSCHEMAPATH = null;
// Path to R program
private static String RHOME = null;
// Path to JRI lib
private static String JRILIBPATH = null;
// Path to the web directory
private static String WEBDIRPATH = null;
// Path to the web directory
private static String UPLOADURL = null;
// Calling this method will configure the program for running locally (without jboss)
public static void setTestingmodeON(String path) {
testingmode = true;
BUILDPROPERTIESPATH = path;
}
// Initializes some important constants
// a) from a build.properties file on the local file system (in testingmode)
// b) from the build.properties file that was packed by ant into the aar file (that runs in jboss)
public static void initializeGIRIXUtil() {
if (initialized) return;
if (testingmode) {
Properties testingprops = new Properties();
try {
testingprops.load(new FileInputStream(BUILDPROPERTIESPATH));
RSCRIPTLETPATH = testingprops.getProperty("girix.directory");
RHOME = testingprops.getProperty("r.home");
CONFIGSCHEMAPATH = testingprops.getProperty("config.schema.path");
JRILIBPATH = testingprops.getProperty("jri.libpath");
WEBDIRPATH = testingprops.getProperty("web.dir");
UPLOADURL = testingprops.getProperty("upload.url");
} catch (Exception e) {
// In testing mode a stack trace is sufficient
e.printStackTrace();
}
} else {
Properties prop = new Properties();
Enumeration<URL> resources = null;
try {
resources = GIRIXUtil.class.getClassLoader().getResources("etc/build.properties");
prop.load(resources.nextElement().openStream());
RSCRIPTLETPATH = prop.getProperty("girix.directory");
RHOME = prop.getProperty("r.home");
String jbosshome = prop.getProperty("jboss.home");
JRILIBPATH = jbosshome + "/standalone/lib/ext";
WEBDIRPATH = prop.getProperty("web.dir");
UPLOADURL = prop.getProperty("upload.url");
} catch (IOException e) {
log.error("Exception stack trace:\n" + getStackTraceAsString(e));
}
}
initialized = true;
}
public static String getWEBDIRPATH() {
return WEBDIRPATH;
}
public static String getUPLOADURL() {
return UPLOADURL;
}
public static JAXBUtil getJAXBUtil() {
if(jaxbutil == null) {
jaxbutil = new JAXBUtil(DEFAULT_PACKAGE_NAME);
}
return jaxbutil;
}
public static String getStackTraceAsString(Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
return sw.toString();
}
public static String getRSCRIPTLETPATH() throws I2B2Exception {
return RSCRIPTLETPATH;
}
public static URL getCONFIGSCHEMAURL() throws I2B2Exception {
if (testingmode) {
// Convert file path string to an URL object
URL ret = null;
try {
ret = new File(CONFIGSCHEMAPATH).toURI().toURL();
if (ret == null) {
throw new NullPointerException();
}
} catch (Exception e) {
throw new I2B2Exception("Error accessing local config schema file path", e);
}
return ret;
} else {
// Schema file GIRIXConfig.xsd is packed into the archive file GIRIX.aar and is accessed by the following code
Enumeration<URL> resources = null;
try {
resources = GIRIXUtil.class.getClassLoader().getResources("etc/GIRIXConfig.xsd");
} catch (IOException e) {
throw new I2B2Exception("Error accessing resource: Config schema file", e);
}
return resources.nextElement();
}
}
public static RscriptletType validateAndUnmarshallScriptletConfigFile(String scriptletPath, String scriptletName) throws I2B2Exception, SAXException {
RscriptletType girixType = null;
String scriptletconfigpath = scriptletPath + "/config.xml";
File scriptletconfig = new File(scriptletconfigpath);
// If a config file exists...
if (scriptletconfig.exists()) {
// ...that is no directory and readable...
if (scriptletconfig.isDirectory() || ! scriptletconfig.canRead()) {
log.error("Scriptlet config file error (Is a directory? Access rights?) at path: " + scriptletconfigpath);
String scriptletconferror = "Error delivered from server: Scriptlet config file access error with scriptlet " + scriptletName + " \n" +
"Please contact the server admin.";
throw new I2B2Exception(scriptletconferror);
}
// ...read in config file and validate against xml schema
Source xmlFile = new StreamSource(scriptletconfig);
validateXML(xmlFile, getCONFIGSCHEMAURL());
// Unmarshall XML file into a 'rscriptlet' (jaxb) object
try {
girixType = (RscriptletType) getJAXBUtil().unMashallerRequest(scriptletconfigpath).getValue();
if (girixType == null) throw new NullPointerException();
} catch (Exception e) {
throw new I2B2Exception("Error during unmarshalling a scriptlet config file", e);
}
} else {
// If no config file is provided, just create a minimal GIRIX object
de.hpi.i2b2.girix.datavo.girixconfig.ObjectFactory girixconfFac = new de.hpi.i2b2.girix.datavo.girixconfig.ObjectFactory();
girixType = girixconfFac.createRscriptletType();
girixType.setSettings(girixconfFac.createSettingsType());
}
return girixType;
}
// Validate an XML file against an XML schema
public static void validateXML(Source xmlFile, URL schemaFile) throws I2B2Exception, SAXException {
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
try {
Schema schema = schemaFactory.newSchema(schemaFile);
Validator validator = schema.newValidator();
validator.validate(xmlFile);
log.info("XML file " + xmlFile.getSystemId() + " is valid");
} catch (IOException e) {
throw new I2B2Exception("Error during XML file validation (IOException)", e);
}
}
// Helper method
public static Map<String, String> convertListIntoMapAndDecodeHTML(List<InputParameterType> l) {
if (l == null) return null;
Map<String, String> m = new HashMap<String, String>();
for (InputParameterType ipt : l) {
String name = ipt.getName().replace("&", "&");
name = name.replace("<", "<");
name = name.replace(">", ">");
m.put(name, ipt.getValue());
}
return m;
}
// This method does two things: a) Update library path at runtime to be able to access libjri.so library
// b) Setting the R_HOME environment variable
public static void setUpREnvironment() throws I2B2Exception {
String updatedLibPath = System.getProperty("java.library.path");
if (!updatedLibPath.contains(JRILIBPATH)) {
updatedLibPath = updatedLibPath + ":" + JRILIBPATH;
System.setProperty("java.library.path", updatedLibPath);
Field sysPathsField = null;
try {
sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
sysPathsField.setAccessible(true);
sysPathsField.set(null, null);
} catch (Exception e) {
throw new I2B2Exception("Error while setting library path", e);
}
}
// Comment this line and set R_HOME by yourself for platform independence
Environment.libc.setenv("R_HOME", RHOME, 0);
}
}
// Using hack from http://quirkygba.blogspot.de/2009/11/setting-environment-variables-in-java.html
// Note that this works only on unix system!
class Environment {
public interface LibC extends Library {
public int setenv(String name, String value, int overwrite);
public int unsetenv(String name);
}
static LibC libc = (LibC) Native.loadLibrary("c", LibC.class);
}