Java tutorial
/** * Genji Scrum Tool and Issue Tracker * Copyright (C) 2015 Steinbeis GmbH & Co. KG Task Management Solutions * <a href="http://www.trackplus.com">Genji Scrum Tool</a> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* $Id:$ */ package com.aurel.track.admin.customize.scripting; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.aurel.track.beans.TPersonBean; import com.aurel.track.beans.TScriptsBean; import com.aurel.track.beans.TSiteBean; import com.aurel.track.beans.TWorkItemBean; import com.aurel.track.errors.ErrorData; import com.aurel.track.fieldType.runtime.base.WorkItemContext; import com.aurel.track.util.LdapUtil; import com.aurel.track.util.event.IEventSubscriber; import groovy.lang.Binding; import groovy.lang.GroovyObject; import groovy.lang.GroovyShell; public class GroovyScriptExecuter { private static final Logger LOGGER = LogManager.getLogger(GroovyScriptExecuter.class); /** * the name of the handler method in the script */ public static final String HANDLE_METHOD_NAME = "handleEvent"; /** * Hardcoded script name name for filtering out e-mail submissions */ public static final String EMAIL_GUARD_SCRIPT = "EmailGuardScript"; /** * Hardcoded script name for configuring the newly created item by e-mail */ public static final String EMAIL_ACTIVITY_SCRIPT = "EmailActivityScript"; /** * Hardcoded script name executed by sending item e-mail */ public static final String EMAIL_SEND_SCRIPT = "EmailSendScript"; /** * Hardcoded script name for ldap synchronize */ public static final String EVENT_HANDLER_LDAP_SYNCHRONIZER_CLASS = "LdapSynchronizer"; /** * Hardcoded script name for user authentication */ public static final String USER_AUTHENTICATION_SCRIPT = "UserAuthentication"; /** * Used in guards to prevent either creation of an item (for e-mail * submission) or reject the status transition */ public static final String HPACKAGE = "com.trackplus.event."; /** * Class name for the event handler for user events (login, logout, add * user, remove user, etc.) */ public static final String EVENT_HANDLER_USER_CLASS = HPACKAGE + "EventHandlerUser"; /** * Class name for the event handler handling system events (startup, * shutdown, etc.) */ public static final String EVENT_HANDLER_SYSTEM_CLASS = HPACKAGE + "EventHandlerSystem"; /** * Class name for the event handler handling project events (create project, * delete project, etc.) */ public static final String EVENT_HANDLER_PROJECT_CLASS = HPACKAGE + "EventHandlerProject"; /** * Script class handling item save events. */ public static final String ITEM_SAVE_HANDLER = "ItemSaveHandler"; /** * The constructor is being called from the system during login. It * initializes the EventHandler with some helpful information which is * stored in a {@link TPersonBean} and a {@link SessionBean}. * <p/> * The user and session bean contain information such as user name, user * e-mail, user preferred locale, last login, the {@link ApplicationBean} * containing the application context, and much more. * <p/> * Access to the persistence layer can be obtained via the static * {@link DAOFactory} class like this:<br/> * <code> * ...<br/> * DAOFactory fact = DAOFactory.getFactory();<br/> * WorkItemDAO widao = fact.getWorkItemDAO();<br/> * ...<br/> */ public GroovyScriptExecuter() { } /** * * @param eventNo * @param inputBinding * @return */ public Map handleEvent(int eventNo, Map<String, Object> inputBinding) { Map returnBinding = null;// = new HashMap(); inputBinding.put(BINDING_PARAMS.CONTINUE, new Boolean(true)); inputBinding.put(BINDING_PARAMS.EVENT, new Integer(eventNo)); if (eventNo >= IEventSubscriber.EVENT_ISSUE_BASE && eventNo < IEventSubscriber.EVENT_ISSUE_BASE + 1000) { if (GroovyScriptLoader.getInstance().doesGroovyClassExist(ITEM_SAVE_HANDLER)) { returnBinding = executeGroovyHandler(ITEM_SAVE_HANDLER, inputBinding); } } else if (eventNo >= IEventSubscriber.EVENT_USER_BASE && eventNo < IEventSubscriber.EVENT_USER_BASE + 1000) { if (GroovyScriptLoader.getInstance().doesGroovyClassExist(EVENT_HANDLER_USER_CLASS)) { returnBinding = executeGroovyHandler(EVENT_HANDLER_USER_CLASS, inputBinding); } } else if (eventNo >= IEventSubscriber.EVENT_PROJECT_BASE && eventNo < IEventSubscriber.EVENT_PROJECT_BASE + 1000) { if (GroovyScriptLoader.getInstance().doesGroovyClassExist(EVENT_HANDLER_PROJECT_CLASS)) { returnBinding = executeGroovyHandler(EVENT_HANDLER_PROJECT_CLASS, inputBinding); } } else if (eventNo >= IEventSubscriber.EVENT_SYSTEM_BASE && eventNo < IEventSubscriber.EVENT_SYSTEM_BASE + 1000) { if (GroovyScriptLoader.getInstance().doesGroovyClassExist(EVENT_HANDLER_SYSTEM_CLASS)) { returnBinding = executeGroovyHandler(EVENT_HANDLER_SYSTEM_CLASS, inputBinding); } } if (returnBinding == null || returnBinding.get(BINDING_PARAMS.CONTINUE) == null || !returnBinding.get(BINDING_PARAMS.CONTINUE).getClass().equals(Boolean.class)) { if (returnBinding != null) { LOGGER.warn("Problem with Groovy EventHandler returnBinding type: is not Boolean"); } returnBinding = new HashMap(); returnBinding.put(BINDING_PARAMS.CONTINUE, new Boolean(true)); // default // is // to // proceed // with // operation } return returnBinding; } /** * Helper method for parameter classes. The "parameter" script are probably * changed according to the customer needs while the logic scripts are * relative stable (probably not changed by the customer) If the parameter * script is changed (and automatically recompiled) the original "parameter" * class file seems to remain in the Groovy classpath. In the local * GroovyScriptLoader cache (availableClasses map) the actual version is * stored That's why in the logic scripts the parameter script's * fields/methods shouldn't be accessed directly (it contains the old * version of the class from the Groovy classpath) but from the * GroovyScriptLoader cache through reflection. * * @param handlerClass * @param methodName * @return */ public static Object getParameterInstanceGroovyHandler(String handlerClass, String methodName) { if (GroovyScriptLoader.getInstance().doesGroovyClassExist(handlerClass)) { try { Class groovyClass = GroovyScriptLoader.getInstance().getGroovyClass(handlerClass); Object object = null; Method getInstance = groovyClass.getMethod("getInstance", new Class[] {}); if (getInstance != null) { // if getInstance() exists get the already initialized // "parameter" object (instance of "parameter" class) object = getInstance.invoke(groovyClass, new Object[] {}); } else { // no getInstance(): always create and initialize a new // "parameter" object (instance of "parameter" class) object = groovyClass.newInstance(); } // method name (probably a getter method) Method method = groovyClass.getMethod(methodName, new Class[] {}); return method.invoke(object, (Object[]) new Class[] {}); } catch (Exception e) { LOGGER.warn("Problem calling Groovy EventHandler: " + e.getMessage()); LOGGER.debug(ExceptionUtils.getStackTrace(e)); } } else { LOGGER.debug("The Groovy class " + handlerClass + " was not found"); } return null; } /** * If something goes wrong here the returnBinding is unchanged, that is the * regular action commences. */ public static Map<String, Object> executeGroovyHandler(String handlerClass, Map<String, Object> inputBinding) { Map<String, Object> returnBinding = null; if (GroovyScriptLoader.getInstance().doesGroovyClassExist(handlerClass)) { try { // GroovyObject handler = // GroovyScriptLoader.getInstance().newInstance(handlerClass); GroovyObject handler = (GroovyObject) GroovyScriptLoader.getInstance().getGroovyClass(handlerClass) .newInstance(); returnBinding = (Map<String, Object>) handler.invokeMethod(HANDLE_METHOD_NAME, inputBinding); } catch (Exception e) { LOGGER.error("Problem calling Groovy EventHandler: " + e.getMessage()); LOGGER.debug(ExceptionUtils.getStackTrace(e)); } } else { LOGGER.debug("The Groovy class " + handlerClass + " was not found"); } return returnBinding; } /** * Execute a Groovy method on a Groovy class * * @param handlerClass * @param methodName * @param inputBinding * @return */ public static Object executeGroovyMethod(String handlerClass, String methodName, Object inputBinding) { if (GroovyScriptLoader.getInstance().doesGroovyClassExist(handlerClass)) { try { GroovyObject handler = (GroovyObject) GroovyScriptLoader.getInstance().getGroovyClass(handlerClass) .newInstance(); return handler.invokeMethod(methodName, inputBinding); } catch (Exception e) { LOGGER.warn("Calling method " + methodName + " on handler class " + handlerClass + " failed with " + e.getMessage()); LOGGER.debug(ExceptionUtils.getStackTrace(e)); } } else { LOGGER.debug("The Groovy class " + handlerClass + " was not found"); } return null; } /** * Executes the Groovy script activity 1. may modify the workItemBean before * save 2. may return list of ErrorData in the returnBinding map containing * "errorList" as key. If the list is not empty then the issue will not be * saved but the error message will be shown as a validation error * * @param handlerClass * @param workItemContext * @param personBean * @return */ public static List<ErrorData> executeActivityScript(String handlerClass, WorkItemContext workItemContext, TPersonBean personBean) { Map<String, Object> inputBinding = workItemContext.getInputBinding(); if (inputBinding == null) { // not from e-mail submission inputBinding = new HashMap<String, Object>(); } inputBinding.put(BINDING_PARAMS.USER, personBean); inputBinding.put(BINDING_PARAMS.LOCALE, workItemContext.getLocale()); inputBinding.put(BINDING_PARAMS.ISSUE, workItemContext.getWorkItemBean()); inputBinding.put(BINDING_PARAMS.ISSUE_ORIGINAL, workItemContext.getWorkItemBeanOriginal()); inputBinding.put(BINDING_PARAMS.WORKITEM_CONTEXT, workItemContext); Map<String, Object> returnBinding = executeGroovyHandler(handlerClass, inputBinding); if (returnBinding != null) { return (List<ErrorData>) returnBinding.get(BINDING_PARAMS.ERRORLIST); } else { LOGGER.debug("Activity script " + handlerClass + " does not exist"); return null; } } /** * Executes the Groovy script guard. If the guard is not satisfied then the * script should set the guardPassed boolean value to false in the return * binding * * @param handlerClass * @param workItemBean * @param personID * @param inputBinding * context coming from specific source (e-mail) * @return */ public static boolean executeGuardScript(String handlerClass, TWorkItemBean workItemBean, Integer personID, Map<String, Object> inputBinding) { if (inputBinding == null) { inputBinding = new HashMap<String, Object>(); } inputBinding.put(BINDING_PARAMS.USER_ID, personID); inputBinding.put(BINDING_PARAMS.ISSUE, workItemBean); inputBinding.put(BINDING_PARAMS.ISSUE_ORIGINAL, workItemBean); Map<String, Object> returnBinding = executeGroovyHandler(handlerClass, inputBinding); Boolean guardPassed = null; if (returnBinding != null) { guardPassed = (Boolean) returnBinding.get(BINDING_PARAMS.GUARD_PASSED); if (guardPassed != null) { LOGGER.debug("Guard " + handlerClass + " passed: " + guardPassed.booleanValue()); } } else { LOGGER.debug("Guard " + handlerClass + " does not exists"); } if (guardPassed == null) { return true; } return guardPassed.booleanValue(); } /** * Executes the Groovy script guard. If the guard is not satisfied then the * script should set the "reject" boolean value in the return binding * * @param handlerClass * the Groovy handler class * @param siteBean * the TSiteBean object for this Genji instance * @param filter * the LDAP filter expression for the search * @return a single HashMap <"map", HashMap (login name, TPersonBean)> */ public static Map<String, Object> executeLdapScript(String handlerClass, TSiteBean siteBean, String filter) { Map<String, Object> inputBinding = null; if (inputBinding == null) { inputBinding = new HashMap<String, Object>(); } inputBinding.put(BINDING_PARAMS.SITEBEAN, siteBean); inputBinding.put(BINDING_PARAMS.LDAP_FILTER, filter); inputBinding.put(BINDING_PARAMS.LDAPMAP, LdapUtil.getLdapMap()); Map<String, Object> returnBinding = null; try { returnBinding = executeGroovyHandler(handlerClass, inputBinding); } catch (Exception e) { return null; } return returnBinding; } /** * Executes the Groovy user authentication script. * * @param request * the HttpServlet request possibly containing authentication informations * @param siteBean * the TSiteBean object for this Genji instance * @return a single HashMap <"map", HashMap (login name, TPersonBean)> */ public static Map<String, Object> executeAuthenticationScript(HttpServletRequest request, TSiteBean siteBean) { Map<String, Object> inputBinding = null; if (inputBinding == null) { inputBinding = new HashMap<String, Object>(); } inputBinding.put(BINDING_PARAMS.SITEBEAN, siteBean); inputBinding.put(BINDING_PARAMS.REQUEST, request); Map<String, Object> returnBinding = null; try { returnBinding = executeGroovyHandler(USER_AUTHENTICATION_SCRIPT, inputBinding); } catch (Exception e) { return null; } return returnBinding; } /** * Execute a plain Groovy shell script * @param theScript the Groovy script to be evaluated * @param inputBinding the parameters passed to the script * @return a map with key/value pairs as result */ public static Map<String, Object> executeGroovyScript(String theScript, Binding inputBinding) { TScriptsBean scriptBean = ScriptAdminBL.loadByClassName(theScript); String scriptCode = null; Map<String, Object> result = new HashMap<String, Object>(); if (scriptBean != null) { scriptCode = scriptBean.getSourceCode(); GroovyShell shell = new GroovyShell(inputBinding); result.put("result", shell.evaluate(scriptCode)); } else { result.put("error", "Can't load script " + theScript); } return result; } }