package org.autoplot.servlet; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.autoplot.ApplicationModel; import org.autoplot.JythonUtil; import org.autoplot.ScriptContext; import static org.autoplot.ScriptContext.waitUntilIdle; import org.autoplot.datasource.DataSetURI; import org.autoplot.datasource.URISplit; import org.autoplot.dom.Application; import org.autoplot.jythonsupport.JythonRefactory; import org.autoplot.jythonsupport.JythonUtil.Param; import org.autoplot.jythonsupport.ui.Util; import org.autoplot.scriptconsole.DumpRteExceptionHandler; import org.autoplot.scriptconsole.LoggingOutputStream; import org.das2.graph.DasCanvas; import org.das2.util.DasPNGConstants; import org.das2.util.DasPNGEncoder; import org.das2.util.FileUtil; import org.das2.util.monitor.NullProgressMonitor; import org.python.util.PythonInterpreter; /** * Run a script on the server side, and produce a client-side GUI for the * getParam calls. * @author jbf */ public class ScriptGUIServlet extends HttpServlet { /** * write out the current canvas to stdout. This is introduced to support servers. * TODO: this has issues with the size. See writeToPng(filename). * @param dom * @param out the OutputStream accepting the data, which is not closed. * @throws java.io.IOException */ public static void writeToPng( Application dom, OutputStream out) throws IOException { waitUntilIdle(); DasCanvas c = dom.getController().getApplicationModel().getCanvas(); int width= dom.getCanvases(0).getWidth(); int height= dom.getCanvases(0).getHeight(); BufferedImage image = c.getImage(width,height); DasPNGEncoder encoder = new DasPNGEncoder(); encoder.addText(DasPNGConstants.KEYWORD_CREATION_TIME, new Date().toString()); encoder.addText(DasPNGConstants.KEYWORD_SOFTWARE, "Autoplot" ); encoder.addText(DasPNGConstants.KEYWORD_PLOT_INFO, c.getImageMetadata() ); encoder.write( image, out); } /** * Processes requests for both HTTP GET and POST * methods. * * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String script= request.getParameter("script"); Map params= request.getParameterMap(); Map ssparams= new LinkedHashMap<>(); ArrayList slparams= new ArrayList<>(); StringBuilder sbparams= new StringBuilder(); for ( Object o: params.entrySet() ) { Entry e= (Entry)o; String value= Array.get(e.getValue(),0).toString(); ssparams.put( e.getKey().toString(), value ); slparams.add( e.getKey().toString() + "=" + value ); sbparams.append("&").append(e.getKey().toString()).append("=").append(value); } String sparams= sbparams.toString(); String[] aaparams= slparams.toArray(new String[slparams.size()]); if ( script==null ) { script= "https://github.com/autoplot/dev/blob/master/demos/2019/20190726/demoParams.jy"; } if ( !script.startsWith("https://github.com/autoplot/dev/") ) { throw new IllegalArgumentException("script must come from https://github.com/autoplot/dev/"); } String scriptURI= script; URISplit split= URISplit.parse(script); String pwd= split.path; String name= split.file.substring(split.path.length()); File scriptFile= DataSetURI.getFile( script, new NullProgressMonitor() ); script= FileUtil.readFileToString(scriptFile); if ( request.getParameter("img")!=null ) { // now run the script org.autoplot.Util.addFonts(); ApplicationModel model = new ApplicationModel(); model.setExceptionHandler( new DumpRteExceptionHandler() ); model.addDasPeersToAppAndWait(); Application dom= model.getDocumentModel(); System.err.println( "dom: "+ dom ); System.err.println( "dom options: "+ dom.getOptions() ); dom.getOptions().setAutolayout(false); PythonInterpreter interp = JythonUtil.createInterpreter( true, true ); interp.set("java",null); interp.set("org",null); interp.set("getFile",null); interp.set("dom",dom); interp.set("downloadResourceAsTempFile",null); LoggingOutputStream los1= new LoggingOutputStream( Logger.getLogger("autoplot.servlet.scriptservlet"), Level.INFO ); interp.setOut( los1 ); interp.set( "response", response ); // To support load balancing, insert the actual host that resolved the request response.setHeader( "X-Served-By", java.net.InetAddress.getLocalHost().getCanonicalHostName() ); //TODO: this limits to one user! LoggingOutputStream los2= new LoggingOutputStream( Logger.getLogger("autoplot.servlet.scriptservlet"), Level.INFO ); //ScriptContext._setOutputStream( los2 ); script= JythonRefactory.fixImports(script); ScriptContext.setApplicationModel(model); // why must I do this??? JythonUtil.runScript( dom, new ByteArrayInputStream(script.getBytes("UTF-8")), name, aaparams, pwd ); try (OutputStream out = response.getOutputStream()) { writeToPng(dom,out); try { los1.close(); } catch ( IOException ex ) {} try { los2.close(); } catch ( IOException ex ) {} } } else { response.setContentType("text/html;charset=UTF-8"); Map parms= Util.getParams( null, script, ssparams, new NullProgressMonitor() ); try (PrintWriter out = response.getWriter()) { out.println(""); out.println(""); out.println(""); out.println(""+name+""); out.println(""); out.println(""); out.println("

Servlet ScriptGUIServlet at " + request.getContextPath() + "

"); out.println("Running script "+scriptURI+""); out.println(""); out.println(""); out.println( ""); out.println( ""); out.println( "
"); out.println("
"); for ( Entry pe: parms.entrySet() ) { Param p= pe.getValue(); Object currentValue= p.value == null ? p.deft : p.value; out.println(""+p.name +", " + p.doc +"
"); if ( p.enums!=null ) { if ( p.enums.size()==2 && p.enums.contains("T") && p.enums.contains("F") ) { if ( "T".equals(currentValue) ) { out.println(""+p.name + ", " + p.doc); } else if ( "on".equals(currentValue) ) { out.println(""+p.name + ", " + p.doc); sparams= sparams.replace(p.name+"=on", p.name+"=T"); } else { out.println(""+p.name + ", " + p.doc); } } else { out.println(""); } } else if ( (p.type=='F') || (p.type=='A') ) { Object s= (p.value!=null) ? p.value : p.deft; out.println(""); } else if ( p.type=='T' ) { //TODO: nice timerange GUI Object s= (p.value!=null) ? p.value : p.deft; out.println(""); } else { //TODO: GUIs for URIs and other parameters. Object s= (p.value!=null) ? p.value : p.deft; out.println(""); } out.println("

"); } if ( parms.isEmpty() ) { out.println("script has no parameters."); } out.println(""); out.println(""); out.println(""); out.println( "
"); out.println( "image" ); out.println( "
"); out.println(""); out.close(); } } } // /** * Handles the HTTP GET method. * * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } /** * Handles the HTTP POST method. * * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } /** * Returns a short description of the servlet. * * @return a String containing servlet description */ @Override public String getServletInfo() { return "Short description"; }// }