de.innovationgate.wgpublisher.webtml.actions.TMLAction.java Source code

Java tutorial

Introduction

Here is the source code for de.innovationgate.wgpublisher.webtml.actions.TMLAction.java

Source

/*******************************************************************************
 * Copyright 2009, 2010 Innovation Gate GmbH. All Rights Reserved.
 * 
 * This file is part of the OpenWGA server platform.
 * 
 * OpenWGA 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.
 * 
 * In addition, a special exception is granted by the copyright holders
 * of OpenWGA called "OpenWGA plugin exception". You should have received
 * a copy of this exception along with OpenWGA in file COPYING.
 * If not, see <http://www.openwga.com/gpl-plugin-exception>.
 * 
 * OpenWGA 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 OpenWGA in file COPYING.
 * If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package de.innovationgate.wgpublisher.webtml.actions;

import java.io.IOException;
import java.io.Serializable;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.builder.HashCodeBuilder;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;

import de.innovationgate.utils.UIDGenerator;
import de.innovationgate.utils.WGUtils;
import de.innovationgate.webgate.api.WGAPIException;
import de.innovationgate.webgate.api.WGCSSJSModule;
import de.innovationgate.webgate.api.WGCancelledException;
import de.innovationgate.webgate.api.WGContent;
import de.innovationgate.webgate.api.WGDatabase;
import de.innovationgate.webgate.api.WGDocument;
import de.innovationgate.webgate.api.WGException;
import de.innovationgate.webgate.api.WGHierarchicalDatabase;
import de.innovationgate.webgate.api.WGIllegalArgumentException;
import de.innovationgate.webgate.api.WGIllegalStateException;
import de.innovationgate.webgate.api.WGScriptModule;
import de.innovationgate.wga.common.DesignDirectory;
import de.innovationgate.wga.server.api.Design;
import de.innovationgate.wga.server.api.ObjectScope;
import de.innovationgate.wga.server.api.TMLScript;
import de.innovationgate.wga.server.api.WGA;
import de.innovationgate.wgpublisher.LoginException;
import de.innovationgate.wgpublisher.WGACore;
import de.innovationgate.wgpublisher.design.DesignResourceReference;
import de.innovationgate.wgpublisher.expressions.tmlscript.RhinoExpressionEngine;
import de.innovationgate.wgpublisher.expressions.tmlscript.TMLScriptException;
import de.innovationgate.wgpublisher.hdb.HDBModel;
import de.innovationgate.wgpublisher.so.ScopeObjectRegistry;
import de.innovationgate.wgpublisher.so.ScopeObjectRegistry.ScopeObject;
import de.innovationgate.wgpublisher.webtml.Base;
import de.innovationgate.wgpublisher.webtml.form.FieldReg;
import de.innovationgate.wgpublisher.webtml.form.TMLForm;
import de.innovationgate.wgpublisher.webtml.portlet.PortletEvent;
import de.innovationgate.wgpublisher.webtml.portlet.TMLPortlet;
import de.innovationgate.wgpublisher.webtml.portlet.TMLPortletProcessContext;
import de.innovationgate.wgpublisher.webtml.portlet.TMLPortletStateStorage;
import de.innovationgate.wgpublisher.webtml.portlet.TMLPortletStateTransientStorage;
import de.innovationgate.wgpublisher.webtml.utils.ItemExpression;
import de.innovationgate.wgpublisher.webtml.utils.ObjectStrategy;
import de.innovationgate.wgpublisher.webtml.utils.ProcessContext;
import de.innovationgate.wgpublisher.webtml.utils.ProcessContextRegistration;
import de.innovationgate.wgpublisher.webtml.utils.TMLContext;
import de.innovationgate.wgpublisher.webtml.utils.TMLDesignContext;
import de.innovationgate.wgpublisher.webtml.utils.TMLException;

public class TMLAction implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    /**
     * The action is defined on a WebTML page, either inline or via <tml:action id="actionid">
     */
    public static final int ORIGIN_TML = 1;

    /**
     * The action is defined by a TMLScript module
     */
    public static final int ORIGIN_SCRIPT_MODULE = 2;

    /**
     * The action is a default action
     */
    public static final int ORIGIN_DEFAULT = 3;

    public static final String DEFAULTACTION_RESET = "reset";

    public static final String DEFAULTACTION_REFRESH = "refresh";

    public static final String DEFAULTACTION_STORE = "store";

    public static final String DEFAULTACTION_REMOVE = "delete";

    public static final String DEFAULTACTION_ATTACH = "attach";

    public static final String DEFAULTACTION_LOGIN = "login";

    public static final String DEFAULTACTION_LOGOUT = "logout";

    public static final String DEFAULTACTION_SETSESSIONVAR = "setsessionvar";

    public static final String DEFAULTACTION_SETPSESSIONVAR = "setpsessionvar";

    public static final String DEFAULTACTION_SETVAR = "setvar";

    public static final String DEFAULTACTION_SETPVAR = "setpvar";

    public static final String DEFAULTACTION_HDBSTORE = "hdbstore";

    public static final String DEFAULTACTION_INCVAR = "incvar";

    public static final String DEFAULTACTION_FIREEVENT = "fireevent";

    public static final String DEFAULTACTION_CALLPCMETHOD = "pc";

    public static final String DEFAULTACTION_CALLMCMETHOD = "mc";

    public static final int FLAG_DEBOUNCED = 1;

    public abstract static class DefaultAction {

        private String _name;

        private boolean _debounced;

        public DefaultAction(String name, int flags) {
            _name = name;
            _debounced = (flags & FLAG_DEBOUNCED) == FLAG_DEBOUNCED;
        }

        public DefaultAction() {
        }

        /**
         * @return Returns the debounced.
         */
        public boolean isDebounced() {
            return _debounced;
        }

        /**
         * @return Returns the name.
         */
        public String getName() {
            return _name;
        }

        public abstract Object call(TMLAction action, TMLContext context, TMLActionLink link,
                Map<String, Object> objects) throws TMLException, TMLScriptException, WGException, IOException;

    }

    public static Map<String, DefaultAction> _defaultActions = new HashMap<String, DefaultAction>();

    static {

        DefaultAction action;

        action = new DefaultAction(DEFAULTACTION_ATTACH, FLAG_DEBOUNCED) {
            public Object call(TMLAction action, TMLContext context, TMLActionLink link,
                    Map<String, Object> objects) throws WGAPIException, IOException, TMLException {
                return TMLAction.defaultActionAttach(context);
            }
        };
        _defaultActions.put(action.getName(), action);

        action = new DefaultAction(DEFAULTACTION_REFRESH, FLAG_DEBOUNCED) {
            public Object call(TMLAction action, TMLContext context, TMLActionLink link,
                    Map<String, Object> objects) throws WGException {
                return TMLAction.defaultActionRefresh(context);
            }
        };
        _defaultActions.put(action.getName(), action);

        action = new DefaultAction(DEFAULTACTION_RESET, FLAG_DEBOUNCED) {
            public Object call(TMLAction action, TMLContext context, TMLActionLink link,
                    Map<String, Object> objects) throws TMLException, WGAPIException {
                return TMLAction.defaultActionReset(context);
            }
        };
        _defaultActions.put(action.getName(), action);

        action = new DefaultAction(DEFAULTACTION_STORE, FLAG_DEBOUNCED) {
            public Object call(TMLAction action, TMLContext context, TMLActionLink link,
                    Map<String, Object> objects) throws WGException, IOException, WGIllegalStateException {
                return TMLAction.defaultActionStore(context, link.getUnnamedParams());
            }
        };
        _defaultActions.put(action.getName(), action);

        action = new DefaultAction(DEFAULTACTION_REMOVE, FLAG_DEBOUNCED) {
            public Object call(TMLAction action, TMLContext context, TMLActionLink link,
                    Map<String, Object> objects)
                    throws TMLException, WGAPIException, IOException, WGIllegalStateException {
                return TMLAction.defaultActionRemove(context);
            }
        };
        _defaultActions.put(action.getName(), action);

        action = new DefaultAction(DEFAULTACTION_SETSESSIONVAR, FLAG_DEBOUNCED) {
            public Object call(TMLAction action, TMLContext context, TMLActionLink link,
                    Map<String, Object> objects)
                    throws TMLException, WGAPIException, IOException, WGIllegalStateException {
                return TMLAction.defaultActionSetSessionVar(context, link.getUnnamedParams());
            }
        };
        _defaultActions.put(action.getName(), action);

        action = new DefaultAction(DEFAULTACTION_SETVAR, FLAG_DEBOUNCED) {
            public Object call(TMLAction action, TMLContext context, TMLActionLink link,
                    Map<String, Object> objects) throws WGException, IOException, WGIllegalStateException {
                return TMLAction.defaultActionSetVar(context, link.getUnnamedParams());
            }
        };
        _defaultActions.put(action.getName(), action);

        action = new DefaultAction(DEFAULTACTION_SETPVAR, FLAG_DEBOUNCED) {
            public Object call(TMLAction action, TMLContext context, TMLActionLink link,
                    Map<String, Object> objects)
                    throws TMLException, WGAPIException, IOException, WGIllegalStateException {
                return TMLAction.defaultActionSetPortletVar(context, link.getUnnamedParams());
            }
        };
        _defaultActions.put(action.getName(), action);

        action = new DefaultAction(DEFAULTACTION_SETPSESSIONVAR, FLAG_DEBOUNCED) {
            public Object call(TMLAction action, TMLContext context, TMLActionLink link,
                    Map<String, Object> objects)
                    throws TMLException, WGAPIException, IOException, WGIllegalStateException {
                return TMLAction.defaultActionSetPortetSessionVar(context, link.getUnnamedParams());
            }
        };
        _defaultActions.put(action.getName(), action);

        action = new DefaultAction(DEFAULTACTION_HDBSTORE, FLAG_DEBOUNCED) {
            public Object call(TMLAction action, TMLContext context, TMLActionLink link,
                    Map<String, Object> objects)
                    throws WGException, IOException, WGIllegalStateException, TMLScriptException {
                return TMLAction.defaultActionHDBStore(context, link.getUnnamedParams());
            }
        };
        _defaultActions.put(action.getName(), action);

        action = new DefaultAction(DEFAULTACTION_INCVAR, FLAG_DEBOUNCED) {
            public Object call(TMLAction action, TMLContext context, TMLActionLink link,
                    Map<String, Object> objects)
                    throws TMLException, WGAPIException, IOException, WGIllegalStateException, TMLScriptException {
                return TMLAction.defaultActionIncVar(context, link.getUnnamedParams());
            }
        };
        _defaultActions.put(action.getName(), action);

        action = new DefaultAction(DEFAULTACTION_LOGIN, FLAG_DEBOUNCED) {
            public Object call(TMLAction action, TMLContext context, TMLActionLink link,
                    Map<String, Object> objects) throws WGException, IOException {
                TMLAction.defaultActionLogin(context, link.getUnnamedParams());
                return null;
            }
        };
        _defaultActions.put(action.getName(), action);

        action = new DefaultAction(DEFAULTACTION_LOGOUT, FLAG_DEBOUNCED) {
            public Object call(TMLAction action, TMLContext context, TMLActionLink link,
                    Map<String, Object> objects) throws WGException, IOException {
                TMLAction.defaultActionLogout(context, link.getUnnamedParams());
                return null;
            }
        };
        _defaultActions.put(action.getName(), action);

        action = new DefaultAction(DEFAULTACTION_FIREEVENT, FLAG_DEBOUNCED) {
            public Object call(TMLAction action, TMLContext context, TMLActionLink link,
                    Map<String, Object> objects) throws TMLException, WGAPIException, IOException,
                    WGIllegalStateException, TMLScriptException, LoginException {
                TMLAction.defaultActionFireEvent(context, link.getUnnamedParams());
                return null;
            }
        };
        _defaultActions.put(action.getName(), action);

        action = new DefaultAction(DEFAULTACTION_CALLPCMETHOD, FLAG_DEBOUNCED) {
            public Object call(TMLAction action, TMLContext context, TMLActionLink link,
                    Map<String, Object> objects) throws TMLException, TMLScriptException, WGException, IOException {
                return TMLAction.defaultActionCallPCMethod(action, context, link.getNamedParams(),
                        link.getUnnamedParams(), objects);
            };
        };
        _defaultActions.put(action.getName(), action);

        action = new DefaultAction(DEFAULTACTION_CALLMCMETHOD, FLAG_DEBOUNCED) {
            public Object call(TMLAction action, TMLContext context, TMLActionLink link,
                    Map<String, Object> objects) throws TMLException, TMLScriptException, WGException, IOException {
                return TMLAction.defaultActionCallMCMethod(action, context, link.getDefinitionModule(),
                        link.getNamedParams(), link.getUnnamedParams(), objects);
            };
        };
        _defaultActions.put(action.getName(), action);

    }

    public static final String CUSTOMACTION = "custom";

    private boolean _async;

    private boolean _master;

    private String _code;

    private Date _moduleDate;

    private String _ID;

    private DesignResourceReference _designReference;

    private String _objectName = null;

    public DesignResourceReference getDesignReference() {
        return _designReference;
    }

    public void setDesignReference(DesignResourceReference designReference) {
        _designReference = designReference;
    }

    // private int _sequence;
    private String _type;

    private int _origin;

    private boolean _debounce;

    private int _timeout;

    private ObjectStrategy _objectStrategy = ObjectStrategy.SINGLE;

    private Set<String> _calledSequenceIds = Collections.synchronizedSet(new LinkedHashSet<String>());

    private String _qualifier = null;

    static final String ACTION_XML_PREFIX = "<doc xmlns:tml=\"urn:webtml\">";

    static final String ACTION_XML_SUFFIX = "</doc>";

    public static final String OBJ_ACTION_PARAMS = "actionparams";

    public TMLAction() {
        _debounce = true;
        _timeout = RhinoExpressionEngine.DEFAULT_SCRIPTTIMEOUT;
    }

    public static Object defaultActionCallMCMethod(TMLAction action, TMLContext context,
            DesignResourceReference definitionModule, Map<String, Object> namedParams, List<Object> unnamedParams,
            Map<String, Object> objects) throws WGException {
        return new ItemExpression(context.designContext(definitionModule.toString()),
                "$mc." + action.getQualifier()).retrieve(namedParams, unnamedParams, objects);
    }

    public static Object defaultActionCallPCMethod(TMLAction action, TMLContext context,
            Map<String, Object> namedParams, List<Object> unnamedParams, Map<String, Object> objects)
            throws WGException {
        return new ItemExpression(context, "$pc." + action.getQualifier()).retrieve(namedParams, unnamedParams,
                objects);
    }

    protected static void defaultActionFireEvent(TMLContext context, List<Object> params)
            throws TMLException, WGAPIException {

        if (context.getportlet() == null) {
            throw new TMLException(
                    "Default action $" + DEFAULTACTION_FIREEVENT + " can only be called inside a WebTML portlet",
                    false);
        }

        if (params.size() <= 1) {
            throw new TMLException("Default action $" + DEFAULTACTION_FIREEVENT
                    + " needs the name of the portlet event as first parameter", false);
        }

        PortletEvent event = context.createevent(String.valueOf(params.get(0)));
        context.getportlet().fireevent(event);

    }

    protected static Boolean defaultActionRemove(TMLContext context) throws WGAPIException {

        WGContent content = context.content();

        // Move a portlet context positioned on the document to delete to its parent, if available, es to none
        TMLPortlet portlet = context.getportlet();
        if (portlet != null && portlet.getcontext() != null
                && portlet.getcontext().getpath().equals(context.getpath())) {
            TMLContext parentContext = portlet.getcontext().context("parent", false);
            if (parentContext != null) {
                portlet.setcontext(parentContext);
            } else {
                portlet.setcontext(null);
            }
        }

        // Look if we delete via HDBModel
        if (!content.isDummy() && content.getStructEntry().getContentType().getName()
                .equals(WGHierarchicalDatabase.CONTENT_TYPE)) {
            HDBModel model = HDBModel.getModel(content.getDatabase());
            if (model != null && HDBModel.isContent(content)) {
                model.deleteContent(content);
                return true;
            }
        }

        // Delete normally
        content.remove();
        return true;

    }

    protected static void defaultActionLogout(TMLContext context, List<Object> params)
            throws WGException, IOException {
        String domain = null;
        ;
        String redirect = null;
        TMLForm form = context.gettmlform();
        if (form != null) {
            if (form.hasfield("domain")) {
                domain = (String) form.field("domain");
            }
            if (form.hasfield("redirect")) {
                redirect = (String) form.field("redirect");
            }
        }
        if (domain == null && params.size() >= 1 && params.get(0) != null) {
            domain = String.valueOf(params.get(0));
        } else
            domain = (String) context.meta("db", "domain");

        if (redirect == null && params.size() >= 2 && params.get(1) != null) {
            redirect = String.valueOf(params.get(1));
        }

        if (context.logout(domain)) {
            if (redirect != null) {
                context.redirectto(redirect);
            } else
                context.redirectto(WGA.get().urlBuilder().build());
        }
    }

    protected static void defaultActionLogin(TMLContext context, List<Object> params)
            throws WGException, IOException {

        String username = null;
        String password = null;
        String domain = (String) context.meta("db", "domain");
        String redirect = null;
        TMLForm form = context.gettmlform();
        if (form != null) {
            if (form.hasfield("user")) {
                username = (String) form.field("user");
            } else if (form.hasfield("username")) {
                username = (String) form.field("username");
            }

            if (form.hasfield("password")) {
                password = (String) form.field("password");
            } else if (form.hasfield("pwd")) {
                password = (String) form.field("pwd");
            }

            if (form.hasfield("domain")) {
                domain = (String) form.field("domain");
            }

            if (form.hasfield("redirect")) {
                redirect = (String) form.field("redirect");
            }
        }

        if (username == null && params.size() >= 1 && params.get(0) != null) {
            username = String.valueOf(params.get(0));
        }

        if (password == null && params.size() >= 2 && params.get(1) != null) {
            password = String.valueOf(params.get(1));
        }

        if (domain == null && params.size() >= 3 && params.get(2) != null) {
            domain = String.valueOf(params.get(2));
        }

        if (redirect == null && params.size() >= 4 && params.get(3) != null) {
            redirect = String.valueOf(params.get(3));
        }

        if (context.login(username, password, domain)) {
            if (redirect != null) {
                context.redirectto(redirect);
            }
        } else {
            WGA.get(context).tmlPage().setVar("loginerror", 1);
            throw new TMLException("Login failed", false);
        }
    }

    public static Object defaultActionSetSessionVar(TMLContext context, List<Object> params) throws TMLException {

        if (params.size() >= 2 && !WGUtils.isEmpty(params.get(0)) && !WGUtils.isEmpty(params.get(1))) {

            context.setsessionvar(String.valueOf(params.get(0)), params.get(1));
            try {
                return context.item("tmlparam2");
            } catch (WGAPIException e) {
                throw new TMLException("Error retrieving item tmlparam2", false);
            }

        }

        else if (context.gettmlform() != null && context.gettmlform().issubmitted()) {

            TMLForm form = context.gettmlform();
            for (String fieldName : form.getfieldnames()) {

                Object value;
                FieldReg fieldReg = form.getforminfo().getFieldRegistration(fieldName);
                if (fieldReg != null && fieldReg.isMultiple()) {
                    value = form.fieldlist(fieldName);
                } else {
                    value = form.field(fieldName);
                }

                context.setsessionvar(fieldName, value);
            }

            return null;

        }

        else {
            throw new TMLException("Default action $" + DEFAULTACTION_SETSESSIONVAR
                    + " needs either 2 action parameters or a submitted WebTML Form", false);
        }

    }

    public static Object defaultActionSetPortetSessionVar(TMLContext context, List<Object> params)
            throws TMLException, WGAPIException {

        if (params.size() >= 2 && !WGUtils.isEmpty(params.get(0)) && !WGUtils.isEmpty(params.get(1))) {

            if (context.getportlet() == null) {
                throw new TMLException("Default action $" + DEFAULTACTION_SETPSESSIONVAR
                        + " can only be called inside a WebTML portlet", false);
            }

            context.getportlet().setsessionvar(String.valueOf(params.get(0)), params.get(1));

            try {
                return context.item("tmlparam2");
            } catch (WGAPIException e) {
                throw new TMLException("Error retrieving item tmlparam2", false);
            }

        }

        else if (context.gettmlform() != null && context.gettmlform().issubmitted()) {

            TMLForm form = context.gettmlform();
            for (String fieldName : form.getfieldnames()) {

                Object value;
                FieldReg fieldReg = form.getforminfo().getFieldRegistration(fieldName);
                if (fieldReg != null && fieldReg.isMultiple()) {
                    value = form.fieldlist(fieldName);
                } else {
                    value = form.field(fieldName);
                }

                context.getportlet().setsessionvar(fieldName, value);
            }

            return null;

        }

        else {

            throw new TMLException("Default action $" + DEFAULTACTION_SETPSESSIONVAR
                    + " needs either 2 action parameters or a submitted WebTML Form", false);

        }

    }

    public static Object defaultActionSetVar(TMLContext context, List<Object> params) throws WGException {

        if (params.size() >= 2 && !WGUtils.isEmpty(params.get(0)) && !WGUtils.isEmpty(params.get(1))) {

            WGA.get(context).tmlPage().setVar(String.valueOf(params.get(0)), params.get(1));
            try {
                return context.item("tmlparam2");
            } catch (WGAPIException e) {
                throw new TMLException("Error retrieving item tmlparam2", false);
            }

        }

        else if (context.gettmlform() != null && context.gettmlform().issubmitted()) {

            TMLForm form = context.gettmlform();
            for (String fieldName : form.getfieldnames()) {

                Object value;
                FieldReg fieldReg = form.getforminfo().getFieldRegistration(fieldName);
                if (fieldReg != null && fieldReg.isMultiple()) {
                    value = form.fieldlist(fieldName);
                } else {
                    value = form.field(fieldName);
                }

                context.setvar(fieldName, value);
            }

            return null;

        }

        else {

            throw new TMLException("Default action $" + DEFAULTACTION_SETVAR
                    + " needs either 2 action parameters or a submitted WebTML Form", false);

        }
    }

    public static Object defaultActionIncVar(TMLContext context, List<Object> params)
            throws TMLException, WGAPIException {

        if (params.size() < 1) {
            throw new TMLException("Default action $" + DEFAULTACTION_INCVAR + " needs 1 arguments while only "
                    + params.size() + " were provided", false);
        }

        try {
            String varName = String.valueOf(params.get(0));
            Number num = (Number) context.item(varName);
            if (num == null) {
                num = 0;
            }
            num = num.intValue() + 1;
            context.updateOrSetVar(varName, num);
            return num;
        } catch (Exception e) {
            throw new TMLException("Error executing default action 'incvar'", e, false);
        }
    }

    public static Object defaultActionSetPortletVar(TMLContext context, List<Object> params)
            throws TMLException, WGAPIException {

        if (params.size() >= 2 && !WGUtils.isEmpty(params.get(0)) && !WGUtils.isEmpty(params.get(1))) {

            if (context.getportlet() == null) {
                throw new TMLException(
                        "Default action $" + DEFAULTACTION_SETPVAR + " can only be called inside a WebTML portlet",
                        false);
            }

            context.getportlet().setvar(String.valueOf(params.get(0)), params.get(1));

            try {
                return context.item("tmlparam2");
            } catch (WGAPIException e) {
                throw new TMLException("Error retrieving item tmlparam2", false);
            }

        }

        else if (context.gettmlform() != null && context.gettmlform().issubmitted()) {

            TMLForm form = context.gettmlform();
            for (String fieldName : form.getfieldnames()) {

                Object value;
                FieldReg fieldReg = form.getforminfo().getFieldRegistration(fieldName);
                if (fieldReg != null && fieldReg.isMultiple()) {
                    value = form.fieldlist(fieldName);
                } else {
                    value = form.field(fieldName);
                }

                context.getportlet().setvar(fieldName, value);
            }

            return null;

        }

        else {
            throw new TMLException("Default action $" + DEFAULTACTION_SETPVAR
                    + " needs either 2 action parameters or a submitted WebTML Form", false);
        }
    }

    public TMLAction(String code, boolean master, boolean async, boolean debounce, int origin) {
        _type = CUSTOMACTION;
        _code = code;
        _master = master;
        _async = async;
        _debounce = debounce;
        _origin = origin;
        _timeout = RhinoExpressionEngine.DEFAULT_SCRIPTTIMEOUT;
    }

    /**
     * Constructor for default actions.
     * 
     * @param defaultActionName
     *            The name of the default action, already stripped of the $ qualifier
     */
    public TMLAction(String defaultActionName) {
        _code = "";
        _master = false;
        _async = false;
        _debounce = true;
        _timeout = RhinoExpressionEngine.DEFAULT_SCRIPTTIMEOUT;
        _origin = ORIGIN_DEFAULT;

        // Split up default action name and qualifier
        _type = defaultActionName;
        int dotPos = _type.indexOf(".");
        if (dotPos != -1) {
            _qualifier = _type.substring(dotPos + 1);
            _type = _type.substring(0, dotPos);
        }

        // check for supported action and set
        if (!_defaultActions.containsKey(_type)) {
            throw new IllegalArgumentException("Unsupported defaultaction '" + defaultActionName + "'");
        }

        setID(defaultActionName);
    }

    public boolean isDefaultAction() {
        return (!_type.equals(CUSTOMACTION));
    }

    /**
     * Returns the code.
     * 
     * @return String
     */
    public String getCode() {
        return _code;
    }

    /**
     * Returns the key. hashcode from all properties
     * 
     * @return Integer
     */
    public Integer getKey() {
        return new Integer(new HashCodeBuilder().append(_async).append(_master).append(_code).append(_type)
                .append(_qualifier).append(_debounce).append(_timeout)
                .append(getModuleDatabase() != null ? getModuleDatabase() : "nodb").toHashCode());
    }

    public static Boolean defaultActionAttach(TMLContext context) throws WGAPIException, IOException, TMLException {

        TMLForm tmlForm = context.gettmlform();

        if (!context.getdocument().getDatabase().getSessionContext().isMasterSession()) {
            TMLAction action = new TMLAction(TMLAction.DEFAULTACTION_ATTACH);
            MasterDefaultAction masterAction = new MasterDefaultAction(context, action, action.createActionLink(
                    Collections.<String, Object>emptyMap(), Collections.emptyList(), context), null);
            masterAction.start();
            context.getdocument().getDatabase().reopenSession();
        } else {
            WGDocument doc = context.getdocument();

            tmlForm.pushFiles(doc);

            doc.save();
        }

        return new Boolean(true);
    }

    public static Boolean defaultActionRefresh(TMLContext context) throws WGException {

        WGA wga = WGA.get(context);
        List<String> dropProcessIds = wga.call().getParamValues("$reloadProcessContexts");
        dropOverloadedProcessContexts(context, dropProcessIds);

        return true;
    }

    private static void dropOverloadedProcessContexts(TMLContext context, List<String> dropProcessIds) {
        if (dropProcessIds.size() == 0) {
            return;
        }

        ProcessContextRegistration reg = context.getEnvironment().getProcessContextRegistration();
        TMLPortletStateStorage stateStorage = context.getPortletStateStorage();
        if (!(stateStorage instanceof TMLPortletStateTransientStorage)) {
            return;
        }

        TMLPortletStateTransientStorage transientStorage = (TMLPortletStateTransientStorage) stateStorage;

        // Drop those process contextes whose portlets were "overloaded", and where no current portlet state was sent for.
        // So these were temporary instances of those portlets, no longer active (#00004513) 
        for (String id : dropProcessIds) {

            // Just for sure: We only want to drop portlet PCs
            ProcessContext pc = reg.getProcessContext(id);
            if (!(pc instanceof TMLPortletProcessContext)) {
                continue;
            }

            if (!transientStorage.getProcessIdsSentByClient().contains(id)) {
                reg.removeProcessContext(id);
            }
        }

    }

    /**
     * @param context
     * @throws IOException
     * @throws WGAPIException
     * @throws WGIllegalStateException
     */
    public static Boolean defaultActionStore(TMLContext context, List<Object> params)
            throws WGException, IOException, WGIllegalStateException {

        try {

            if (context.isbrowserinterface()) {
                context.getcontent().getDatabase().getSessionContext().setClient(WGACore.getBIClientString());
            } else {
                context.getcontent().getDatabase().getSessionContext().setClient(WGACore.getWebformClientString());
            }

            TMLForm tmlForm = context.gettmlform();

            if (tmlForm == null) {
                throw new TMLException("Cannot store form because there is no form", false);
            }

            return (tmlForm.store());

        } catch (WGCancelledException e) {
            if (context.gettmlform() != null) {
                context.gettmlform().addmessage(e.getMessage());
            }
            return false;
        } catch (TMLScriptException e) {
            throw new TMLException("TMLScript exception creating HDB content via $store", e, false);
        }

    }

    public static Boolean defaultActionHDBStore(TMLContext context, List<Object> params)
            throws WGException, IOException, WGIllegalStateException, TMLScriptException {

        TMLForm tmlForm = context.gettmlform();
        if (tmlForm == null) {
            throw new TMLException("Cannot store form because there is no form", false);
        }
        String formSource = tmlForm.getforminfo().getSource();
        if (WGUtils.isEmpty(formSource)) {
            throw new TMLException("Cannot store form because the form source is empty", false);
        }

        if (context.isbrowserinterface()) {
            context.getcontent().getDatabase().getSessionContext().setClient(WGACore.getBIClientString());
        } else {
            context.getcontent().getDatabase().getSessionContext().setClient(WGACore.getWebformClientString());
        }

        boolean result = false;
        if (formSource.equals("content") || formSource.equals("none") || formSource.equals("newcontent")) {
            Object param = null;
            if (params.size() >= 1) {
                param = params.get(0);
            }
            result = tmlForm.storeinhdb(param);
        } else {
            throw new TMLException("Cannot store form in HDB because the form source is invalid for this action: "
                    + String.valueOf(formSource), false);
        }

        // There are files to attach
        if (result == true && tmlForm.getfilenames().size() > 0) {
            defaultActionAttach(context);
        }

        return result;

    }

    public static Boolean defaultActionReset(TMLContext context) throws TMLException, WGAPIException {

        TMLForm tmlForm = context.gettmlform();
        if (tmlForm == null) {
            throw new TMLException("Cannot reset form because there is no form", false);
        }

        tmlForm.reset();

        return new Boolean(true);

    }

    public boolean isAsync() {
        return _async;
    }

    /**
     * @return
     */
    public boolean isMaster() {
        return _master;
    }

    /**
     * @param b
     */
    public void setAsync(boolean b) {
        _async = b;
    }

    /**
     * @param string
     */
    public void setCode(String string) {
        _code = string;
    }

    /**
     * @param b
     */
    public void setMaster(boolean b) {
        _master = b;
    }

    public TMLActionLink createActionLink(Map<String, Object> namedParams, List<Object> unnamedParams,
            TMLContext context) throws WGAPIException {

        TMLActionLink actionLink = new TMLActionLink(context.gethttpsession());

        actionLink.setSequenceId(isDebounce() ? UIDGenerator.generateUID() : TMLActionLink.EMPTY_PARAM);
        if (_type.equals(CUSTOMACTION)) {
            actionLink.setActionKey(getKey());
        } else {
            actionLink.setDefaultAction(getID());
        }

        // Determine db key and context path
        actionLink.setDbKey((String) context.getdocument().getDatabase().getAttribute(WGACore.DBATTRIB_DBKEY));
        if (!context.getdocument().isTemporary()) {
            actionLink.setContextPath(context.getpath());
        }

        // Determine portlet key, if present
        TMLPortlet portlet = context.getportlet();
        if (portlet != null) {
            actionLink.setPortletKey(portlet.getportletkey());
        }

        // Determine WebTML scope
        String scope = (String) context.option(Base.OPTION_WEBTML_SCOPE);
        if (scope != null) {
            actionLink.setWebtmlScope(scope);
        }

        // Set action params
        if (unnamedParams != null) {
            actionLink.getUnnamedParams().addAll(unnamedParams);
        }
        if (namedParams != null) {
            actionLink.getNamedParams().putAll(namedParams);
        }

        // Set definition module
        actionLink.setDefinitionModule(context.getDesignContext().getBaseReference());

        return actionLink;
    }

    public String getType() {
        return _type;
    }

    public void setType(String type) {
        _type = type;
    }

    public boolean isDebounce() {
        return _debounce;
    }

    public void setDebounce(boolean debounce) {
        _debounce = debounce;
    }

    public int getTimeout() {
        return _timeout;
    }

    public void setTimeout(int timeout) {
        _timeout = timeout;
    }

    public boolean sequenceIdAlreadyUsed(String sequenceId) {
        return _calledSequenceIds.contains(sequenceId);
    }

    public void calledWithSequenceId(String sequenceId) {
        // add sequence Id to set
        _calledSequenceIds.add(sequenceId);
        if (_calledSequenceIds.size() > 1000) {
            // if size exceeded remove first
            synchronized (_calledSequenceIds) {
                Iterator<String> it = _calledSequenceIds.iterator();
                it.next();
                it.remove();
            }
        }
    }

    public static DefaultAction getDefaultAction(TMLAction action) {
        return (DefaultAction) _defaultActions.get(action.getType());
    }

    /**
     * @return Returns the moduleName.
     */
    public String getModuleName() {
        DesignResourceReference ref = getDesignReference();
        if (ref != null) {
            return ref.getResourceName();
        } else {
            return null;
        }
    }

    /**
     * @return Returns the moduleDatabase.
     */
    public String getModuleDatabase() {
        DesignResourceReference ref = getDesignReference();
        if (ref != null) {
            return ref.getDesignApp();
        } else {
            return null;
        }
    }

    private WGCSSJSModule retrieveModule(TMLContext context) throws WGException {
        WGDatabase db = context.db(getModuleDatabase());
        if (db == null) {
            throw new WGException("Database " + getModuleDatabase() + " not found");
        }

        if (!db.isSessionOpen()) {
            throw new WGException("User has no access to database " + getModuleDatabase());
        }

        WGCSSJSModule mod = db.getCSSJSModule(getModuleName(), WGScriptModule.CODETYPE_TMLSCRIPT);
        // Module not available or was deleted. We just return null.
        if (mod == null) {
            return null;
        }

        if (!mod.getCodeType().equals(WGCSSJSModule.CODETYPE_TMLSCRIPT)) {
            throw new WGException("Script module '" + getModuleName() + "' in database " + getModuleDatabase()
                    + " is no TMLScript module");
        }
        return mod;
    }

    /**
     * @return Returns the iD.
     */
    public String getID() {
        return _ID;
    }

    /**
     * @param id
     *            The iD to set.
     */
    public void setID(String id) {
        _ID = id;
    }

    /**
     * @return Returns the moduleDate.
     */
    public Date getModuleDate() {
        return _moduleDate;
    }

    /**
     * @param moduleDate
     *            The moduleDate to set.
     */
    public void setModuleDate(Date moduleDate) {
        _moduleDate = moduleDate;
    }

    /**
     * Tests if this action definition is up-to-date. This only applies to
     * actions that base on TMLScript modules, which might have been updated.
     * 
     * @param context
     *            A context that this method needs to test for up-to-date-ness
     * @return true, if the action definition is up-to-date, false (surprise) if
     *         not
     */
    public boolean upToDate(TMLContext context) {

        if (_origin != ORIGIN_SCRIPT_MODULE) {
            return true;
        }

        try {
            WGCSSJSModule mod = retrieveModule(context);
            if (mod != null) {
                return mod.getLastModified().equals(getModuleDate());
            } else {
                return false;
            }
        } catch (WGException e) {
            context.getlog().error("Error testing module action up-to-date", e);
            return false;
        }

    }

    public static TMLAction buildActionFromScriptModule(WGCSSJSModule mod)
            throws TMLActionException, WGAPIException {
        return buildActionFromScriptModule(mod, ObjectStrategy.SINGLE);
    }

    public static TMLAction buildActionFromScriptModule(WGCSSJSModule mod, ObjectStrategy defaultObjectStrategy)
            throws TMLActionException, WGAPIException {

        String code = mod.getCode().trim();
        TMLAction action = null;

        // See if code is surrounded by <tml:action> Tag. Parse it out to get
        // flags
        if (code.startsWith("<tml:action")) {
            List<String> codelines = WGUtils.deserializeCollection(code, "\n");
            if (codelines.size() < 2) {
                throw new TMLActionException(
                        "Too few lines for action script module. A script module that begins with <tml:action> must a) have this tag on a separate line and b) end with </tml:action> on a separate line");
            }

            // Take first and last line of code and try to parse them as XML
            String actionXML = ACTION_XML_PREFIX + ((String) codelines.get(0))
                    + ((String) codelines.get(codelines.size() - 1)) + ACTION_XML_SUFFIX;
            String actionCode = "";
            if (codelines.size() >= 3) {
                actionCode = WGUtils.serializeCollection(codelines.subList(1, codelines.size() - 1), "\n");
            }

            // Parse out action flags
            boolean master = false;
            boolean async = false;
            boolean debounce = true;
            int timeout = RhinoExpressionEngine.DEFAULT_SCRIPTTIMEOUT;
            try {

                Document actionDOM = DocumentHelper.parseText(actionXML);
                Element actionElement = actionDOM.getRootElement()
                        .element(new QName("action", new Namespace("tml", "urn:webtml")));
                master = WGUtils.stringToBoolean(actionElement.attributeValue("master", "false"));
                async = WGUtils.stringToBoolean(actionElement.attributeValue("async", "false"));
                debounce = WGUtils.stringToBoolean(actionElement.attributeValue("debounce", "true"));
                try {
                    timeout = Integer.parseInt(actionElement.attributeValue("timeout",
                            new Integer(RhinoExpressionEngine.DEFAULT_SCRIPTTIMEOUT).toString()));
                } catch (NumberFormatException e) {
                    throw new TMLActionException(
                            "Unparseable action timeout: " + actionElement.attributeValue("timeout"));
                }
                action = new TMLAction(actionCode, master, async, debounce, ORIGIN_SCRIPT_MODULE);
                action.setTimeout(timeout);
            } catch (DocumentException e) {
                throw new TMLActionException("Unable to build action from script module", e);
            }
        }

        // No tag surrounds the action code. Take the code unmodified
        else {
            action = new TMLAction(code, false, false, true, ORIGIN_SCRIPT_MODULE);
        }

        if (action != null) {
            action.setDesignReference(new DesignResourceReference(mod));
            action.setModuleDate(mod.getLastModified());
            action.setID(action.getModuleDatabase() + "/" + action.getModuleName());

            // Determine object strategy: May be modified by strategy determined on module.
            ObjectStrategy objectStrategy = defaultObjectStrategy;
            String determinedObjectStrategyStr = (String) mod
                    .getExtensionData(RhinoExpressionEngine.EXTDATA_OBJECTSTRATEGY);
            if (determinedObjectStrategyStr != null) {
                objectStrategy = ObjectStrategy.valueOf(determinedObjectStrategyStr);
            }
            action.setObjectStrategy(objectStrategy);

            // Determine name of eventually included object from original filename
            String sourceFileName = mod.getSourceFileName();
            if (sourceFileName != null) {
                String objectName = DesignDirectory.getTMLScriptObjectName(sourceFileName);
                action.setObjectName(objectName);
            }

        }
        return action;

    }

    public int getOrigin() {
        return _origin;
    }

    public String getDescription() {

        if (isDefaultAction()) {
            return "WebTML Default Action " + getID();
        }
        if (getOrigin() == TMLAction.ORIGIN_SCRIPT_MODULE) {
            return "TMLScript-Module " + getModuleDatabase() + "/" + getModuleName();
        } else if (getID() != null) {
            return "WebTML Action " + getID() + " from WebTML-Module " + getModuleDatabase() + "/"
                    + getModuleName();
        } else {
            return "Anonymous WebTML-Action from WebTML-Module " + getModuleDatabase() + "/" + getModuleName();
        }

    }

    public TMLDesignContext createContextDelegate(TMLContext context, TMLDesignContext baseContext)
            throws WGException {
        return createDesignContext(context, baseContext);
    }

    public TMLDesignContext createDesignContext(TMLContext context, TMLDesignContext baseContext)
            throws WGException {

        // For default dbs just return the original design context
        if (getOrigin() == TMLAction.ORIGIN_DEFAULT) {
            return baseContext;
        }

        WGDatabase moduleDB = context.db();
        if (getModuleDatabase() != null && !getModuleDatabase().equals(moduleDB.getDbReference())) {
            moduleDB = context.db(getModuleDatabase());
        }

        // Page/Inline actions use their originating WebTML module as base reference (#00001443)
        if (getOrigin() == TMLAction.ORIGIN_TML) {
            return baseContext.createContextDelegate(moduleDB, getModuleName());
        }

        // Module actions use their module
        else {
            return baseContext.createContextDelegate(moduleDB, getID());
        }

    }

    public ObjectStrategy getObjectStrategy() {
        return _objectStrategy;
    }

    public void setObjectStrategy(ObjectStrategy objectStrategy) {
        _objectStrategy = objectStrategy;
    }

    public String getQualifier() {
        return _qualifier;
    }

    public void setQualifier(String qualifier) {
        _qualifier = qualifier;
    }

    public String getObjectName() {
        return _objectName;
    }

    public void setObjectName(String objectName) {
        _objectName = objectName;
    }

}