coral.service.ExpTemplateUtil.java Source code

Java tutorial

Introduction

Here is the source code for coral.service.ExpTemplateUtil.java

Source

/*
 *   Copyright 2009-2015 Markus Schaffner
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
package coral.service;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.tools.generic.MathTool;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;

import coral.model.ExpData;
import coral.utils.CoralUtils;

/**
 * Provide template (and script) evaluation features.
 * 
 * @author Markus Schaffner
 * 
 */
public class ExpTemplateUtil {

    // static ScriptEngineManager m = new ScriptEngineManager();

    static protected final Log logger = LogFactory.getLog(ExpTemplateUtil.class);
    private static Process ztreeProcess = null;

    String basepath = "";

    public ExpTemplateUtil(String basepath) {

        this.basepath = basepath;

        try {

            Properties p = new Properties();

            p.setProperty(RuntimeConstants.RESOURCE_LOADER, "file");
            p.setProperty("file.resource.loader.path", basepath);
            // p.setProperty("file.resource.loader.cache", "false");

            // TODO the following two options are probably less then optimal
            p.setProperty("velocimacro.library.autoreload", "true");
            p.setProperty("velocimacro.permissions.allow.inline.to.replace.global", "true");

            // TODO do we care about velocity logs? probably not in production
            p.setProperty("runtime.log.logsystem.class", "org.apache.velocity.runtime.log.NullLogSystem");

            Velocity.init(p);

        } catch (Exception e1) {
            logger.error("Error in velocity setup", e1);
            throw new RuntimeException(e1);
        }

    }

    // main entry method
    public String eval(String template, ExpData data, ErrorFlag error, ExpServiceImpl service) {

        String result = null;

        if (template.endsWith(".vm")) {
            result = evalVM(template, data, error, service);
        } else if (template.endsWith(".html") || template.endsWith(".htm")) {
            result = evalVM(template, data, error, service);
        } else if (template.endsWith(".js")) {
            result = evalScriptR(template, data, error, service);
        } else if (template.endsWith(".ztt")) {
            result = runZtree(template, data, error, service);
        } else {
            result = errorPage("cannot process this type of file: " + template);
        }

        return result;
    }

    // velocity template evaluation
    public static String evalVM(String t, Map data, ErrorFlag error, ExpServiceImpl service,
            Map<String, Object>... adds) {

        /*
         * Make a context object and populate with the data. This is where the
         * Velocity engine gets the data to resolve the references (ex. $list)
         * in the template
         */

        VelocityContext context = new VelocityContext();
        context.put("data", data);
        context.put("error", error);
        context.put("_coralhost", CoralUtils.getHostStr());

        context.put("_math", new MathTool());

        for (Map<String, Object> add : adds) {
            for (Map.Entry<String, Object> a : add.entrySet()) {
                context.put(a.getKey(), a.getValue());
            }
        }

        if (service != null) {
            context.put("debug", service.debug);
        }

        for (Object o : data.entrySet()) {
            Map.Entry e = (Map.Entry) o;
            if (e != null && e.getKey() != null && !e.getKey().toString().equals("")) {
                Object value = data.get(e.getKey());
                if (logger.isDebugEnabled()) {
                    logger.debug("entered value: " + e.getKey() + " == " + e.getValue().toString() + " \t\t | "
                            + e.getClass());
                }
                context.put(e.getKey().toString(), value);
            }
        }

        /*
         * get the Template object. This is the parsed version of your template
         * input file.
         */

        Template template = null;

        try {
            template = Velocity.getTemplate(t);
        } catch (ResourceNotFoundException rnfe) {
            logger.error("error : cannot find template " + t, rnfe);
            return errorPage(template + ": " + rnfe.getMessage());
        } catch (ParseErrorException pee) {
            logger.error("Syntax error in template " + t + ":", pee);
            return errorPage(template + ": " + pee.getMessage());
        } catch (Exception e) {
            logger.error("Velocity exception in template: " + t + ":", e);
            return errorPage(template + ": " + e.getMessage());
        }

        /*
         * Now have the template engine process your template using the data
         * placed into the context. Think of it as a 'merge' of the template and
         * the data to produce the output stream.
         */

        StringWriter writer = new StringWriter();
        try {
            template.merge(context, writer);
        } catch (ResourceNotFoundException e) {
            logger.error("vm file not found", e);
            return errorPage(template + ": " + e.getMessage());
        } catch (ParseErrorException e) {
            logger.error("vm parsing problem", e);
            return errorPage(template + ": " + e.getMessage());
        } catch (MethodInvocationException e) {
            logger.error("vm mie", e);
            return errorPage(template + ": " + e.getMessage());
        } catch (Exception e) {
            logger.error("vm exp", e);
            return errorPage(template + ": " + e.getMessage());
        }

        return writer.toString();
    }

    /*
     * 
     * // javascript evaluation public String evalScript(String scriptname,
     * ExpData data, ErrorFlag error, ExpServiceImpl service) {
     * 
     * // Map<String, String> newOrChangedMap = new LinkedHashMap<String, //
     * String>();
     * 
     * ScriptEngine jsEngine = m.getEngineByName("js"); ScriptContext context =
     * new SimpleScriptContext();
     * 
     * // context.setAttribute("data", data, ScriptContext.ENGINE_SCOPE); //
     * context.setAttribute("error", error, ScriptContext.ENGINE_SCOPE);
     * 
     * for (Map.Entry<String, Object> e : data.entrySet()) { if (e != null &&
     * e.getKey() != null && !e.getKey().toString().equals("")) { Object value =
     * data.get(e.getKey());
     * 
     * if (logger.isDebugEnabled()) { logger.debug("entered value: " +
     * e.getKey() + " == " + value + " \t\t | " + value.getClass()); }
     * context.setAttribute(e.getKey(), value, ScriptContext.ENGINE_SCOPE); } }
     * 
     * if (service != null) { context.setAttribute("agents",
     * service.getAllData().values() .toArray(), ScriptContext.ENGINE_SCOPE); }
     * 
     * try { BufferedReader br = new BufferedReader(new FileReader(new File(
     * basepath + scriptname)));
     * 
     * Object o = jsEngine.eval(br, context); if (logger.isDebugEnabled())
     * logger.debug("JS OBJECT: " + o);
     * 
     * Map<String, Object> outmap = context
     * .getBindings(ScriptContext.ENGINE_SCOPE);
     * 
     * for (Map.Entry<String, Object> e : outmap.entrySet()) { Object value =
     * e.getValue();
     * 
     * // TODO dirty way to unwrap NativeJavaObjects if (!(value instanceof
     * Number) && !(value instanceof String)) { try { Method m =
     * value.getClass().getMethod("unwrap"); value = m.invoke(value); } catch
     * (SecurityException e1) { // TODO Auto-generated catch block
     * e1.printStackTrace(); } catch (NoSuchMethodException e1) { // TODO
     * Auto-generated catch block // e1.printStackTrace(); } catch
     * (IllegalArgumentException e1) { // TODO Auto-generated catch block
     * e1.printStackTrace(); } catch (IllegalAccessException e1) { // TODO
     * Auto-generated catch block e1.printStackTrace(); } catch
     * (InvocationTargetException e1) { // TODO Auto-generated catch block
     * e1.printStackTrace(); } }
     * 
     * if (value instanceof Number || value instanceof String) { if
     * (!data.containsKey(e.getKey()) || data.get(e.getKey()) == null ||
     * !data.get(e.getKey()).toString() .equals(value.toString())) {
     * data.put(e.getKey(), value); // newOrChangedMap.put(e.getKey(),
     * value.toString()); if (logger.isDebugEnabled()) {
     * logger.debug("SCRIPTED VALUE: " + e.getKey() + " == " + value.toString()
     * + " \t\t | " + value.getClass()); } } else { if (logger.isDebugEnabled())
     * { logger.debug("retained: " + e.getKey() + " == " + value.toString()); }
     * } } else if (value instanceof List<?>) { Object[] array = ((List<?>)
     * value).toArray();
     * 
     * if (!data.containsKey(e.getKey()) || data.get(e.getKey()) == null ||
     * !data.get(e.getKey()).toString()
     * .equals(Arrays.asList(array).toString())) { data.put(e.getKey(), array);
     * // newOrChangedMap.put(e.getKey(), array); if (logger.isDebugEnabled()) {
     * logger.debug("SCRIPTED ARRAY: " + e.getKey() + " == " + value.toString()
     * + " \t\t | " + value.getClass()); } } else { if (logger.isDebugEnabled())
     * { logger.debug("ARRAY retained: " + e.getKey() + " == " +
     * value.toString()); } }
     * 
     * logger.debug("ARRAY: " + e.getKey() + " == " + value.toString() +
     * " \t\t | " + e.getValue().getClass()); } else { logger.debug("NONVALUE: "
     * + e.getKey() + " == " + value.toString() + " \t\t | " +
     * e.getValue().getClass()); } // context.removeAttribute(e.getKey(), //
     * ScriptContext.ENGINE_SCOPE); }
     * 
     * } catch (FileNotFoundException e1) {
     * logger.error("File Not Found Exception ", e1); return
     * errorPage(scriptname + " " + e1.getMessage()); } catch (ScriptException
     * e) { logger.error("Script failed with Exception " + e.getMessage());
     * return errorPage(scriptname + " " + e.getMessage()); }
     * 
     * return null; }
     * 
     * 
     * // evaluate simple javascript expression with data public static Object
     * evalExp(String exp, Map<String, Object> data) {
     * 
     * ScriptEngine jsEngine = m.getEngineByName("js"); ScriptContext context =
     * new SimpleScriptContext();
     * 
     * for (Map.Entry<String, Object> e : data.entrySet()) { if (e != null &&
     * e.getKey() != null && !e.getKey().toString().equals("")) { Object value =
     * e.getValue();
     * 
     * try { try { value = Integer.parseInt(value.toString()); } catch
     * (NumberFormatException ex) { double v =
     * Double.parseDouble(value.toString()); if (Math.round(v) == v) { value =
     * Math.round(v); } else { value = v; } } } catch (NumberFormatException ex)
     * { // value = value; } if (logger.isDebugEnabled()) {
     * logger.debug("entered value: " + e.getKey() + " == " +
     * e.getValue().toString() + " \t\t | " + e.getClass()); }
     * context.setAttribute(e.getKey(), value, ScriptContext.ENGINE_SCOPE); } }
     * 
     * Object o = null; try { o = jsEngine.eval(exp, context); if
     * (logger.isDebugEnabled()) logger.debug("JS OBJECT: " + o); } catch
     * (ScriptException e) { logger.error("script failed", e); }
     * 
     * return o; }
     */

    // javascript evaluation
    public String evalScriptR(String scriptname, ExpData data, ErrorFlag error, ExpServiceImpl service) {

        // Map<String, String> newOrChangedMap = new LinkedHashMap<String, sss
        // String>();

        // ScriptEngine jsEngine = m.getEngineByName("js");
        // ScriptContext context = new SimpleScriptContext();

        Context context = Context.enter();

        Scriptable scope = context.initStandardObjects();

        // context.setAttribute("data", data, ScriptContext.ENGINE_SCOPE);
        // context.setAttribute("error", error, ScriptContext.ENGINE_SCOPE);

        for (Map.Entry<String, Object> e : data.entrySet()) {
            if (e != null && e.getKey() != null && !e.getKey().toString().equals("")) {
                Object value = data.get(e.getKey());

                if (logger.isDebugEnabled()) {
                    logger.debug("entered value: " + e.getKey() + " == " + value + " \t\t | " + value.getClass());
                }
                scope.put(e.getKey(), scope, value);
            }
        }

        if (service != null) {
            scope.put("agents", scope, service.getAllData().values().toArray());
        }

        try {
            BufferedReader br = new BufferedReader(new FileReader(new File(basepath + scriptname)));

            Object o = context.evaluateReader(scope, br, "coral", 1, null);
            if (logger.isDebugEnabled())
                logger.debug("JS OBJECT: " + o);

            for (Object keyOb : scope.getIds()) {
                String key = keyOb.toString();
                Object value = scope.get(key.toString(), scope);

                if (logger.isDebugEnabled())
                    logger.debug("KEYS: " + key + " value: " + value);

                // TODO dirty way to unwrap NativeJavaObjects
                if (!(value instanceof Number) && !(value instanceof String)) {
                    try {
                        Method m = value.getClass().getMethod("unwrap");
                        value = m.invoke(value);
                    } catch (SecurityException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    } catch (NoSuchMethodException e1) {
                        // TODO Auto-generated catch block
                        // e1.printStackTrace();
                    } catch (IllegalArgumentException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    } catch (IllegalAccessException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    } catch (InvocationTargetException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                }

                if (value instanceof Number || value instanceof String) {
                    if (!data.containsKey(key) || data.get(key) == null
                            || !data.get(key).toString().equals(value.toString())) {
                        data.put(key, value);
                        // newOrChangedMap.put(key, value.toString());
                        if (logger.isDebugEnabled()) {
                            logger.debug("SCRIPTED VALUE: " + key + " == " + value.toString() + " \t\t | "
                                    + value.getClass());
                        }
                    } else {
                        if (logger.isDebugEnabled()) {
                            logger.debug("retained: " + key + " == " + value.toString());
                        }
                    }
                } else if (value instanceof List<?>) {
                    Object[] array = ((List<?>) value).toArray();

                    if (!data.containsKey(key) || data.get(key) == null
                            || !data.get(key).toString().equals(Arrays.asList(array).toString())) {
                        data.put(key, array);
                        // newOrChangedMap.put(key, array);
                        if (logger.isDebugEnabled()) {
                            logger.debug("SCRIPTED ARRAY: " + key + " == " + value.toString() + " \t\t | "
                                    + value.getClass());
                        }
                    } else {
                        if (logger.isDebugEnabled()) {
                            logger.debug("ARRAY retained: " + key + " == " + value.toString());
                        }
                    }

                    logger.debug("ARRAY: " + key + " == " + value.toString() + " \t\t | " + value.getClass());
                } else {
                    logger.debug("NONVALUE: " + key + " == " + value.toString() + " \t\t | " + value.getClass());
                }
                // context.removeAttribute(key,
                // ScriptContext.ENGINE_SCOPE);

            }

        } catch (FileNotFoundException e1) {
            logger.error("File Not Found Exception ", e1);
            return errorPage(scriptname + " " + e1.getMessage());
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        return null;
    }

    // evaluate simple javascript expression with data
    public static Object evalExp(String exp, Map<String, Object> data) {

        Context context = Context.enter();
        Scriptable scope = context.initStandardObjects();

        for (Map.Entry<String, Object> e : data.entrySet()) {
            if (e != null && e.getKey() != null && !e.getKey().toString().equals("")) {
                Object value = e.getValue();

                try {
                    try {
                        value = Integer.parseInt(value.toString());
                    } catch (NumberFormatException ex) {
                        double v = Double.parseDouble(value.toString());
                        if (Math.round(v) == v) {
                            value = Math.round(v);
                        } else {
                            value = v;
                        }
                    }
                } catch (NumberFormatException ex) {
                    // value = value;
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("entered value: " + e.getKey() + " == " + e.getValue().toString() + " \t\t | "
                            + e.getClass());
                }
                scope.put(e.getKey(), scope, value);
            }
        }

        Object o = null;
        try {
            o = context.evaluateString(scope, exp, "expression", 1, null);
            if (logger.isDebugEnabled())
                logger.debug("JS OBJECT: " + o);
        } catch (Exception e) {
            logger.error("script failed", e);
        }

        return o;
    }

    // TODO experimental ztree evaluation, is probably broken (untested)
    public String runZtree(String template, ExpData data, ErrorFlag error, final ExpServiceImpl service) {
        if (ztreeProcess == null) {
            Runtime rt = Runtime.getRuntime();
            try {
                ztreeProcess = rt.exec("ztree.exe /treatment " + template);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            new Thread() {
                public void run() {
                    File f = new File(basepath + "ztree_end.txt");
                    while (!f.exists()) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                    // TODO read f and post to database

                    ztreeProcess.destroy();
                    ztreeProcess = null;

                    // TODO delete file
                    f.delete();

                    // TODO progress client
                    for (Integer id : service.dataMap.keySet()) {
                        // todo add table comps as args
                        service.process(id, "zleaf");
                    }
                };
            }.start();
        }

        return null;
    }

    /**
     * helper function to generate errorpage with a message
     * 
     * @param message
     *            The message to display on the error page (might be stacktrace)
     * @return
     */
    static String errorPage(String message) {

        logger.error("produce error message for client: " + message);

        StringBuilder sb = new StringBuilder();

        sb.append("<html><body><h1>An error has occured</h1><p>");
        sb.append(message);
        sb.append("</p><p>See log file for details<p>");
        sb.append("<form action='" + CoralUtils.getHostStr()
                + "'><p><input type='submit' name='skiperror' value='SKIP'> <br><input type='submit' name='reload' value='RELOAD'> <br></form>");
        sb.append("</body></html>");

        return sb.toString();
    }
}