package services; import generic.Utils; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.net.URL; import java.text.ParseException; import java.util.List; import javax.servlet.http.HttpServletResponse; import org.molgenis.cluster.RScript; import org.molgenis.framework.db.Database; import org.molgenis.framework.db.DatabaseException; import org.molgenis.framework.db.QueryRule; import org.molgenis.framework.db.QueryRule.Operator; import org.molgenis.framework.server.FrontControllerAuthenticator; import org.molgenis.framework.server.FrontControllerAuthenticator.LoginStatus; import org.molgenis.framework.server.FrontControllerAuthenticator.LogoutStatus; import org.molgenis.framework.server.MolgenisContext; import org.molgenis.framework.server.MolgenisRequest; import org.molgenis.framework.server.MolgenisResponse; import org.molgenis.framework.server.MolgenisService; import org.molgenis.framework.server.MolgenisServiceAuthenticationHelper; import decorators.MolgenisFileHandler; /** Use seperate servlet because of the custom R script that needs to be added */ public class XqtlRApiService implements MolgenisService { private MolgenisContext mc; public XqtlRApiService(MolgenisContext mc) { this.mc = mc; } @Override public void handleRequest(MolgenisRequest request, MolgenisResponse response) throws ParseException, DatabaseException, IOException { // as used in /molgenis/src/org/molgenis/generators/R/RApiGen.R.ftl, // must match! String pwdString = MolgenisServiceAuthenticationHelper.LOGIN_PASSWORD; String usrString = MolgenisServiceAuthenticationHelper.LOGIN_USER_NAME; // Utils.console("starting RApiServlet"); OutputStream outs = response.getResponse().getOutputStream(); PrintStream out = new PrintStream(new BufferedOutputStream(outs), false, "UTF8"); // 1.4 if (request.getString(usrString) != null && request.getString(pwdString) != null) { String usr = request.getString(usrString); String pwd = request.getString(pwdString); LoginStatus login = FrontControllerAuthenticator.login(request, usr, pwd); String responseLine; if (login == LoginStatus.ALREADY_LOGGED_IN) { responseLine = "You are already logged in. Log out first."; } else if (login == LoginStatus.SUCCESSFULLY_LOGGED_IN) { responseLine = "Welcome, " + usr + "!"; } else if (login == LoginStatus.AUTHENTICATION_FAILURE) { responseLine = "User or password unknown."; } else if (login == LoginStatus.EXCEPTION_THROWN) { responseLine = "An error occurred. Contact your administrator."; } else { throw new IOException("Unknown login status: " + login); } writeResponse(response, responseLine, out); return; } if (request.getString("logout") != null && request.getString("logout").equals("logout")) { LogoutStatus logout = FrontControllerAuthenticator.logout(request); String responseLine; if (logout == LogoutStatus.ALREADY_LOGGED_OUT) { responseLine = "You are already logged out. Log in first."; } else if (logout == LogoutStatus.SUCCESSFULLY_LOGGED_OUT) { responseLine = "You are successfully logged out."; } else if (logout == LogoutStatus.EXCEPTION_THROWN) { responseLine = "An error occurred. Contact your administrator."; } else { throw new IOException("Unknown logout status: " + logout); } writeResponse(response, responseLine, out); return; } // Utils.console("filename is now: " + filename); String filename = request.getRequestPath().substring(request.getServicePath().length()); String s = ""; if (filename.startsWith("/")) { filename = filename.substring(1); } // if R file exists, return that if (!filename.equals("") && !filename.endsWith(".R")) { // Utils.console("bad request: no R extension"); s += "you can only load .R files\n"; } else if (filename.equals("")) { // Utils.console("getting default file"); String rSource = request.getAppLocation() + request.getRequestPath(); rSource = rSource.endsWith("/") ? rSource : rSource + "/"; // getRequestURL omits port! s += ("#first time only: install RCurl and bitops\n"); s += ("#install.packages(\"RCurl\", lib=\"~/libs\")\n"); s += ("#install.packages(\"bitops\", lib=\"~/libs\")\n"); s += ("\n"); s += ("#load RCurl and bitops\n"); s += ("library(bitops, lib.loc=\"~/libs\")\n"); s += ("library(RCurl, lib.loc=\"~/libs\")\n"); s += ("\n"); s += ("#robust sourcing function for URLs\n"); s += ("msource <- function(murl = \"http://127.0.0.1:8080/xqtl/api/R/\", verbose = TRUE){\n"); s += (" if(verbose) cat(\"Creating connection\",murl,\"\\n\")\n"); s += (" data <- getURLContent(murl)\n"); s += (" t <- tempfile()\n"); s += (" writeLines(data, con=t)\n"); s += (" sys.source(t,globalenv())\n"); s += (" unlink(t)\n"); s += ("}\n"); s += ("\n"); s += ("#location of this R API and the application itself\n"); s += ("r_api_location <- paste(\"" + rSource + "\")\n"); s += ("app_location <- paste(\"" + request.getAppLocation() + "\")\n"); s += ("\n"); s += ("#load autogenerated R interfaces\n"); s += ("msource(\"" + rSource + "source.R\")\n"); s += ("\n"); s += ("#load XGAP specific extension to use R/qtl\n"); s += ("msource(\"" + rSource + "xgap/R/RqtlTools.R\")\n"); s += ("\n"); s += ("#load XGAP specific extension to ease use of the Data <- DataElement structure as matrices\n"); s += ("msource(\"" + rSource + "xgap/R/DataMatrix.R\")\n"); s += ("\n"); s += ("#load cluster calculation scripts\n"); File[] listing = new File((this.getClass().getResource("../plugins/cluster/R/ClusterJobs/R")).getFile()) .listFiles(); if (listing != null) { for (File f : listing) { s += ("msource(\"" + rSource + "plugins/cluster/R/ClusterJobs/R/" + f.getName() + "\")\n"); } } else { s = ("#No R files seem available; did you generate R?"); } s += ("\n"); // quick addition for demo purposes s += ("#loading user defined scripts\n"); try { List<RScript> scripts = request.getDatabase().find(RScript.class); for (RScript script : scripts) { s += ("msource(\"" + rSource + "userscripts/" + script.getName() + ".R\")\n"); } s += ("\n"); s += ("#connect to the server\n"); s += ("MOLGENIS.connect()\n"); s += ("\n"); s += ("#--> login/logout using:\n"); s += ("# MOLGENIS.login(\"username\",\"password\")\n"); s += ("# MOLGENIS.logout()\n"); s += ("\n"); } catch (Exception e) { s += "#No database connection available to handle R-api"; // throw new IOException(e); } // quick addition for demo purposes } else if (filename.startsWith("userscripts/")) { try { Database db = request.getDatabase(); String name = filename.substring(12, filename.length() - 2); // Utils.console("getting '"+name+".r'"); QueryRule q = new QueryRule("name", Operator.EQUALS, name); RScript script = db.find(RScript.class, q).get(0); MolgenisFileHandler mfh = new MolgenisFileHandler(db); File source = mfh.getFile(script, db); Utils.console("printing file: '" + source.getAbsolutePath() + "'"); String str = this.printUserScript(source.toURI().toURL(), "", name); s += (str); } catch (Exception e) { throw new IOException(e); } } else { // otherwise return the default R code to source all // Utils.console("getting specific R file"); filename = filename.replace(".", "/"); filename = filename.substring(0, filename.length() - 2) + ".R"; // map to hard drive, minus path app/servlet File root = new File(app.servlet.FrontController.class.getResource("source.R").getFile()).getParentFile() .getParentFile().getParentFile(); if (filename.equals("source.R")) { root = new File(root.getAbsolutePath() + "/app/servlet"); } File source = new File(root.getAbsolutePath() + "/" + filename); // up to root of app // Utils.console("trying to load R file: " + filename + // " from path " + source); if (source.exists()) { String str = this.printScript(source.toURI().toURL(), ""); s += (str); } else { s += ("File '" + filename + "' not found\n"); } // Utils.console("done getting specific R file"); } writeResponse(response, s, out); // Utils.console("closed & flushed"); } private void writeResponse(MolgenisResponse response, String responseLine, PrintStream out) throws IOException { response.getResponse().setStatus(HttpServletResponse.SC_OK); response.getResponse().setContentLength(responseLine.length()); response.getResponse().setCharacterEncoding("UTF8"); response.getResponse().setContentType("text/plain"); out.print(responseLine); out.flush(); out.close(); response.getResponse().flushBuffer(); } private String printScript(URL source, String out) throws IOException { // Utils.console("reading file to be outputted"); BufferedReader reader = new BufferedReader(new InputStreamReader(source.openStream())); String sourceLine; while ((sourceLine = reader.readLine()) != null) { out += sourceLine + "\n"; } reader.close(); // Utils.console("done reading"); return out; } private String printUserScript(URL source, String out, String scriptName) throws IOException { // Utils.console("reading file to be outputted"); BufferedReader reader = new BufferedReader(new InputStreamReader(source.openStream())); out += "run_" + scriptName + " <- function(dbpath, subjob, item, jobid, outname, myanalysisfile, jobparams, investigationname, libraryloc){\n"; String sourceLine; while ((sourceLine = reader.readLine()) != null) { if (!sourceLine.trim().equals("")) { if (!sourceLine.trim().endsWith("{") && !sourceLine.trim().endsWith("}")) { out += "cat(Generate_Statement(\"" + sourceLine.replace("\"", "'") + "\"),file=myanalysisfile,append=T)\n"; } else { out += "cat(\"" + sourceLine.replace("\"", "'") + "\n\",file=myanalysisfile,append=T)\n"; } } else { // Utils.console("Removing empty line"); } } out += "}"; reader.close(); // Utils.console("done reading"); return out; } }