/* =============================================================================== * * Part of the InfoGlue Content Management Platform (www.infoglue.org) * * =============================================================================== * * Copyright (C) * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2, as published by the * Free Software Foundation. See the file LICENSE.html for more information. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY, including 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, write to the Free Software Foundation, Inc. / 59 Temple * Place, Suite 330 / Boston, MA 02111-1307 / USA. * * =============================================================================== */ package org.infoglue.cms.applications.workflowtool.util; import java.util.Collections; import java.util.Map; import org.apache.log4j.Logger; import org.exolab.castor.jdo.Database; import org.infoglue.cms.util.workflow.DatabaseSession; import com.opensymphony.module.propertyset.PropertySet; import com.opensymphony.provider.BeanProvider; import com.opensymphony.provider.bean.DefaultBeanProvider; import com.opensymphony.workflow.WorkflowException; /** * Base class containing logic used by both <code>InfoglueFunction</code> and <code>InfoglueCondition</code>. * The main purpose of this class is to provide convenience methods for the parameters, arguments, and propertyset objects * and to handle the <code>DatabaseSession</code> object. */ public abstract class InfoglueWorkflowBase { /** * The class logger. */ private final static Logger logger = Logger.getLogger(InfoglueWorkflowBase.class.getName()); /** * The default encoding. */ protected static final String UTF8_ENCODING = "utf-8"; /** * The prefix for all keys representing workflow specific information in the propertyset. */ public static final String WORKFLOW_PROPERTYSET_PREFIX = "workflow_"; /** * The key used for storing the function status in the propertyset. */ public static final String FUNCTION_STATUS_PROPERTYSET_KEY = WORKFLOW_PROPERTYSET_PREFIX + "status"; /** * The prefix for all keys representing errors in the propertyset. */ public static final String ERROR_PROPERTYSET_PREFIX = "error_"; /** * The key used by the <code>DatabaseSession</code> in the <code>parameters</code>. */ private static final String DB_PARAMETER = "db"; /** * The parameters (transient variables) of the current execution context. */ private Map parameters; /** * The arguments passed to the function. */ private Map arguments; /** * The propertyset associated with the current workflow. */ private InfogluePropertySet propertySet; /** * The database associated with the current execution. */ private DatabaseSession workflowDatabase; /** * If <code>throwException</code> is used inside both <code>try</code> and <code>catch</catch> * then it will be logged twice. To get ride of this, the lastException is tracked. */ private Exception lastException; /** * Default constructor. */ public InfoglueWorkflowBase() { super(); } /** * Method used for initializing the object; will be called before any execution is performed. * <p><strong>Note</strong>! You must call <code>super.initialize()</code> first.</p> * * @throws WorkflowException if an error occurs during the initialization. */ protected void initialize() throws WorkflowException { workflowDatabase = (DatabaseSession) getParameter(DB_PARAMETER); } /** * Stores the execution context. * * @param transientVars the transient variables of the current execution context. * @param args the arguments of the function. * @param ps the propertyset associated with the current workflow. */ protected void storeContext(final Map transientVars, final Map args, final PropertySet ps) { this.parameters = transientVars; this.arguments = Collections.unmodifiableMap(args); this.propertySet = new InfogluePropertySet(ps); } /** * The preferred way to throw an exception from a subclass. * Logs the error, sets the mode of the database to rollback only and throws an exception. * * @param message the exception message. * @throws WorkflowException always throws an exception with the specified message. */ protected void throwException(final String message) throws WorkflowException { throwException(new WorkflowException(message)); } /** * The preferred way to throw an exception from a subclass. * Logs the error, sets the mode of the database to rollback only and throws an exception. * * @param e the exception to chain. * @throws WorkflowException always throws an exception. If the specified exception is a * workflow exception, that exception is thrown. Otherwise a chained workflow exception is thrown. */ protected void throwException(final Exception e) throws WorkflowException { if(lastException != e) { logger.info(e.getMessage(), e); } lastException = e; workflowDatabase.setRollbackOnly(); throw (e instanceof WorkflowException) ? (WorkflowException) e : new WorkflowException(e); } /** * Returns true if the specified argument exists; false otherwise. * * @param name the name of the argument. * @return true if the specified argument exists; false otherwise. */ protected final boolean argumentExists(final String name) { return arguments.containsKey(name); } /** * Returns the specified argument if it exists; otherwise an exception is thrown. * Only use this method if the argument is absolutely required. * * @param name the name of the argument. * @return the specified argument. * @throws WorkflowException if the specified argument doesn't exists. */ protected final String getArgument(final String name) throws WorkflowException { if(!arguments.containsKey(name)) { throwException("Required argument " + name + " is missing."); } return arguments.get(name).toString(); } /** * Returns the specified argument if it exists; otherwise the default value. * * @param name the name of the argument. * @param defaultValue the default value. * @return the specified argument if it exists; otherwise the default value. */ protected final String getArgument(final String name, final String defaultValue) throws WorkflowException { return arguments.containsKey(name) ? arguments.get(name).toString() : defaultValue; } /** * Returns true if the specified parameter exists; false otherwise. * * @param name the name of the parameter. * @return true if the specified parameter exists; false otherwise. */ protected final boolean parameterExists(final String key) { return parameters.containsKey(key); } /** * Returns the specified parameter if it exists; otherwise an exception is thrown. * Only use this method if the parameter is absolutely required. * * @param name the name of the parameter. * @return the specified parameter. * @throws WorkflowException if the specified parameter doesn't exists. */ protected final Object getParameter(final String key) throws WorkflowException { return getParameter(key, true); } /** * Returns the specified parameter if it exists; otherwise an exception is thrown. * Only use this method if the parameter is absolutely required. * * @param name the name of the parameter. * @return the specified parameter. * @throws WorkflowException if the specified parameter doesn't exists. */ protected final String getParameterStringValue(final String key, final boolean required) throws WorkflowException { Object o = getParameter(key, required); if(o instanceof String[]) return ((String[])o)[0]; else return (o == null ? null : o.toString()); } /** * Returns the specified parameter if it exists; otherwise the default value. * * @param name the name of the parameter. * @param defaultValue the default value. * @return the specified parameter if it exists; otherwise the default value. */ protected final Object getParameter(final String key, final Object defaultValue) throws WorkflowException { return parameters.containsKey(key) ? parameters.get(key) : defaultValue; } /** * Returns the specified parameter. If the parameter is required and not found, an exception is thrown. * * @param key the key. * @param required indicates if the parameter is required. * @return the specified parameter. * @throws WorkflowException if a required parameter is missing. */ protected final Object getParameter(final String key, final boolean required) throws WorkflowException { final Object parameter = parameters.get(key); if(required && parameter == null) { final WorkflowException e = new WorkflowException("Required parameter " + key + " is missing."); logger.error(e.toString()); throw e; } return parameter; } /** * Stores the specified parameter. * * @param key the lookup key. * @param value the value. */ protected final void setParameter(final String key, final Object value) { parameters.put(key, value); } /** * Returns the parameters (transient variables) of the current execution context. * * @return the parameters (transient variables) of the current execution context. */ protected final Map getParameters() { return parameters; } /** * Returns true if the specified property exists; false otherwise. * * @return true if the specified property exists; false otherwise. */ protected final boolean propertySetContains(final String key) { return propertySet.exists(key); } /** * Returns the specified data property as a string. * * @param key the key. */ protected final String getPropertySetDataString(final String key) { return propertySet.getDataString(key); } /** * Stores the specified data property. * * @param key the key. * @param value the value. */ protected final void setPropertySetDataString(final String key, final String value) { propertySet.setDataString(key, value); } /** * Returns the specified string property. * * @param key the key. */ protected final String getPropertySetString(final String key) { return propertySet.getString(key); } /** * Stores the specified string property. * * @param key the key. * @param value the value. */ protected final void setPropertySetString(final String key, final String value) { propertySet.setString(key, value); } /** * Removes the property with the specified key from the propertyset. * * @param key the property key. */ protected final void removeFromPropertySet(final String key) { removeFromPropertySet(key, false); } /** * Removes the property with the specified key from the propertyset. * If key is a prefix, all properties having keys starting with the specified key * will be removed. * * @param key the property key. * @param isPrefix indicates if the key should be used as key prefix. */ protected final void removeFromPropertySet(final String key, final boolean isPrefix) { propertySet.removeKeys(key, isPrefix); } /** * */ protected final String translate(final String s) { final Object o = translateVariables(s, parameters, propertySet); return (o == null) ? null : o.toString(); } /** * Returns the propertyset associated with the current workflow. * * @return the propertyset associated with the current workflow. */ protected final InfogluePropertySet getPropertySet() { return propertySet; } /** * Returns the database associated with the current execution. * * @return the database associated with the current execution. * @throws WorkflowException if an error occurs when opening/starting the database/transaction. */ protected final Database getDatabase() throws WorkflowException { return workflowDatabase.getDB(); } /** * Parses a string for instances of "${foo}" and returns a string with all instances replaced * with the string value of the foo object (<b>foo.toString()</b>). If the string being passed * in only refers to a single variable and contains no other characters (for example: ${foo}), * then the actual object is returned instead of converting it to a string. */ public static Object translateVariables(String s, Map transientVars, PropertySet ps) { Object result = null; String temp = s.trim(); if (temp.startsWith("${") && temp.endsWith("}") && (temp.indexOf('$', 1) == -1)) { // the string is just a variable reference, don't convert it to a string String var = temp.substring(2, temp.length() - 1); result = getVariableFromMaps(var, transientVars, ps); } else { // the string passed in contains multiple variables (or none!) and should be treated as a string while (true) { int x = s.indexOf("${"); int y = s.indexOf("}", x); if ((x != -1) && (y != -1)) { String var = s.substring(x + 2, y); String t = null; Object o = getVariableFromMaps(var, transientVars, ps); if (o != null) { t = o.toString(); } if (t != null) { s = s.substring(0, x) + t + s.substring(y + 1); } else { // the variable doesn't exist, so don't display anything s = s.substring(0, x) + s.substring(y + 1); } } else { break; } } result = s; } return result; } private static BeanProvider beanProvider = new DefaultBeanProvider(); //~ Methods //////////////////////////////////////////////////////////////// public static Object getVariableFromMaps(String var, Map transientVars, PropertySet ps) { int firstDot = var.indexOf('.'); String actualVar = var; if (firstDot != -1) { actualVar = var.substring(0, firstDot); } Object o = transientVars.get(actualVar); if (o == null) { o = ps.getAsActualType(actualVar); } if (firstDot != -1) { o = beanProvider.getProperty(o, var.substring(firstDot + 1)); } return o; } }