com.twinsoft.convertigo.beans.core.RequestableStep.java Source code

Java tutorial

Introduction

Here is the source code for com.twinsoft.convertigo.beans.core.RequestableStep.java

Source

/*
 * Copyright (c) 2001-2011 Convertigo SA.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Affero 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/>.
 *
 * $URL$
 * $Author$
 * $Revision$
 * $Date$
 */

package com.twinsoft.convertigo.beans.core;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactoryConfigurationError;

import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaCollection;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaParticle;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.NativeJavaArray;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.Undefined;
import org.mozilla.javascript.UniqueTag;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import com.twinsoft.convertigo.beans.common.XMLVector;
import com.twinsoft.convertigo.beans.connectors.ConnectionException;
import com.twinsoft.convertigo.beans.variables.RequestableMultiValuedVariable;
import com.twinsoft.convertigo.beans.variables.RequestableVariable;
import com.twinsoft.convertigo.beans.variables.StepMultiValuedVariable;
import com.twinsoft.convertigo.beans.variables.StepVariable;
import com.twinsoft.convertigo.engine.ConvertigoError;
import com.twinsoft.convertigo.engine.Engine;
import com.twinsoft.convertigo.engine.EngineException;
import com.twinsoft.convertigo.engine.enums.HttpPool;
import com.twinsoft.convertigo.engine.enums.Visibility;
import com.twinsoft.convertigo.engine.requesters.InternalRequester;
import com.twinsoft.convertigo.engine.util.GenericUtils;
import com.twinsoft.convertigo.engine.util.HttpUtils;
import com.twinsoft.convertigo.engine.util.Log4jHelper;
import com.twinsoft.convertigo.engine.util.StringUtils;
import com.twinsoft.convertigo.engine.util.TwsCachedXPathAPI;
import com.twinsoft.convertigo.engine.util.VersionUtils;
import com.twinsoft.convertigo.engine.util.XMLUtils;
import com.twinsoft.util.StringEx;

public abstract class RequestableStep extends Step
        implements IVariableContainer, IContainerOrdered, ISchemaParticleGenerator {

    private static final long serialVersionUID = 3948128175718822695L;

    public static final String SOURCE_SEPARATOR = ".";

    private XMLVector<XMLVector<Long>> orderedVariables = new XMLVector<XMLVector<Long>>();

    transient private List<StepVariable> vVariables = new LinkedList<StepVariable>();
    transient private List<StepVariable> vAllVariables = null;
    transient protected Document xmlHttpDocument = null;
    transient protected PostMethod method = null;
    transient protected HostConfiguration hostConfiguration = null;
    transient protected String targetUrl = "";

    private String contextName = "";

    private boolean bInternalInvoke = true;

    transient protected String projectName = "";

    transient protected Map<String, Object> request;

    transient public String wsdlType = "";

    public RequestableStep() {
        super();
        xml = true;

        hostConfiguration = new HostConfiguration();

        orderedVariables = new XMLVector<XMLVector<Long>>();
        orderedVariables.add(new XMLVector<Long>());
    }

    @Override
    public RequestableStep clone() throws CloneNotSupportedException {
        RequestableStep clonedObject = (RequestableStep) super.clone();
        clonedObject.vVariables = new LinkedList<StepVariable>();
        clonedObject.vAllVariables = null;
        clonedObject.xmlHttpDocument = null;
        clonedObject.hostConfiguration = new HostConfiguration();
        clonedObject.targetUrl = null;
        clonedObject.method = null;
        return clonedObject;
    }

    @Override
    public Object copy() throws CloneNotSupportedException {
        RequestableStep copiedObject = (RequestableStep) super.copy();
        copiedObject.vVariables = getVariablesCopy(copiedObject);
        return copiedObject;
    }

    public String getProjectName() {
        return projectName;
    }

    @Override
    public String toString() {
        String label = "";
        try {
            label += getLabel();
        } catch (EngineException e) {
        }

        if (label.isEmpty()) {
            return getName();
        } else {
            return getName() + " " + label;
        }
    }

    @Override
    public String toJsString() {
        return "";
    }

    protected Project getTargetProject(String projectName) throws EngineException {
        return getSequence().getLoadedProject(projectName);
    }

    public String getStepContextName() {
        return contextName;
    }

    public void setStepContextName(String contextName) {
        this.contextName = contextName;
    }

    public boolean isInternalInvoke() {
        return bInternalInvoke;
    }

    public void setInternalInvoke(boolean internalInvoke) {
        bInternalInvoke = internalInvoke;
    }

    private List<StepVariable> getVariablesCopy(RequestableStep copiedObject) throws CloneNotSupportedException {
        List<StepVariable> v = new ArrayList<StepVariable>(vVariables.size());
        for (Variable var : vVariables) {
            StepVariable stepVariableCopy = (StepVariable) var.clone();
            stepVariableCopy.parent = copiedObject;
            v.add(stepVariableCopy);
        }
        return v;
    }

    @Override
    protected void cleanCopy() {
        super.cleanCopy();
        if (vVariables != null) {
            vVariables.clear();
            vVariables = null;
        }
        if (vAllVariables != null) {
            vAllVariables.clear();
            vAllVariables = null;
        }
    }

    @Override
    public void preconfigure(Element element) throws Exception {
        super.preconfigure(element);

        String version = element.getAttribute("version");

        if (VersionUtils.compare(version, "4.6.0") < 0) {
            NodeList properties = element.getElementsByTagName("property");

            Element propName = (Element) XMLUtils.findNodeByAttributeValue(properties, "name", "name");
            String objectName = (String) XMLUtils
                    .readObjectFromXml((Element) XMLUtils.findChildNode(propName, Node.ELEMENT_NODE));

            Element propVarDef = (Element) XMLUtils.findNodeByAttributeValue(properties, "name",
                    "variablesDefinition");
            if (propVarDef != null) {
                propVarDef.setAttribute("name", "orderedVariables");
                hasChanged = true;
                Engine.logBeans.warn("[RequestableStep] The object \"" + objectName
                        + "\" has been updated to version 4.6.0 (property \"variablesDefinition\" changed to \"orderedVariables\")");
            }
        }
    }

    @Override
    public void configure(Element element) throws Exception {
        super.configure(element);

        String version = element.getAttribute("version");

        if (version == null) {
            String s = XMLUtils.prettyPrintDOM(element);
            EngineException ee = new EngineException(
                    "Unable to find version number for the database object \"" + getName() + "\".\nXML data: " + s);
            throw ee;
        }

        try {
            NodeList childNodes = element.getElementsByTagName("wsdltype");
            int len = childNodes.getLength();
            if (len > 0) {
                Node childNode = childNodes.item(0);
                Node cdata = XMLUtils.findChildNode(childNode, Node.CDATA_SECTION_NODE);
                if (cdata != null) {
                    wsdlType = cdata.getNodeValue();
                    Engine.logBeans.trace("(RequestableStep) configure() : wsdltype has been successfully set");
                } else
                    Engine.logBeans.trace("(RequestableStep) configure() : wsdltype is empty");
            }
        } catch (Exception e) {
            throw new EngineException(
                    "Unable to retrieve the wsdltype for the sequence step \"" + getName() + "\".", e);
        }

        if (VersionUtils.compare(version, "4.6.0") < 0) {
            // Backup wsdlTypes to file
            try {
                backupWsdlTypes(element);
                if (!wsdlType.equals("")) {
                    wsdlType = "";
                    hasChanged = true;
                    Engine.logBeans.warn("[RequestableStep] Successfully backup wsdlTypes for step \"" + getName()
                            + "\" (v 4.6.0)");
                } else {
                    Engine.logBeans.warn("[RequestableStep] Empty wsdlTypes for step \"" + getName()
                            + "\", none backup done (v 4.6.0)");
                }
            } catch (Exception e) {
                Engine.logBeans.error(
                        "[RequestableStep] Could not backup wsdlTypes for step \"" + getName() + "\" (v 4.6.0)", e);
            }
        }
    }

    protected String getWsdlBackupDir() throws Exception {
        return getProject().getDirPath() + "/backup-wsdl";
    }

    protected String getWsdlBackupDir(Element element) throws Exception {
        Element rootElement = element.getOwnerDocument().getDocumentElement();
        Element projectNode = (Element) XMLUtils.findChildNode(rootElement, Node.ELEMENT_NODE);
        NodeList properties = projectNode.getElementsByTagName("property");
        Element pName = (Element) XMLUtils.findNodeByAttributeValue(properties, "name", "name");
        String projectName = (String) XMLUtils
                .readObjectFromXml((Element) XMLUtils.findChildNode(pName, Node.ELEMENT_NODE));
        return Engine.PROJECTS_PATH + "/" + projectName + "/backup-wsdl";
    }

    protected void backupWsdlTypes(Element element) throws TransformerFactoryConfigurationError, Exception {
        if (wsdlType.equals(""))
            return;

        StringEx sx = new StringEx(wsdlType);
        sx.replaceAll("<cdata>", "<![CDATA[");
        sx.replaceAll("</cdata>", "]]>");
        sx.replaceAll("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>", "");
        String sDom = sx.toString();
        DocumentBuilder documentBuilder = XMLUtils.getDefaultDocumentBuilder();
        Document document = documentBuilder.parse(new InputSource(new StringReader(sDom)));

        String wsdlBackupDir = getWsdlBackupDir(element);
        File dir = new File(wsdlBackupDir);
        if (!dir.exists())
            dir.mkdirs();

        File file = new File(wsdlBackupDir + "/step-" + priority + ".xml");
        XMLUtils.saveXml(document, file);
    }

    public XMLVector<XMLVector<Long>> getOrderedVariables() {
        return orderedVariables;
    }

    public void setOrderedVariables(XMLVector<XMLVector<Long>> orderedVariables) {
        this.orderedVariables = orderedVariables;
    }

    @Override
    public void add(DatabaseObject databaseObject) throws EngineException {
        if (databaseObject instanceof StepVariable)
            addVariable((StepVariable) databaseObject);
        else
            throw new EngineException("You cannot add to a requestable step a database object of type "
                    + databaseObject.getClass().getName());
    }

    @Override
    public void remove(DatabaseObject databaseObject) throws EngineException {
        if (databaseObject instanceof StepVariable)
            removeVariable((StepVariable) databaseObject);
        else
            throw new EngineException("You cannot remove from a requestable step a database object of type "
                    + databaseObject.getClass().getName());
    }

    public List<StepVariable> getVariables(boolean reset) {
        if (reset)
            vAllVariables = null;
        return getVariables();
    }

    public List<StepVariable> getVariables() {
        checkSubLoaded();
        if ((vAllVariables == null) || hasChanged)
            vAllVariables = getAllVariables();
        return vAllVariables;
    }

    public List<StepVariable> getAllVariables() {
        checkSubLoaded();
        return sort(vVariables);
    }

    public Variable getVariable(int index) {
        checkSubLoaded();
        try {
            return vVariables.get(index);
        } catch (ArrayIndexOutOfBoundsException e) {
            return null;
        }
    }

    public Variable getVariable(String variableName) {
        checkSubLoaded();
        for (StepVariable variable : vVariables)
            if (variable.getName().equals(variableName))
                return variable;
        return null;
    }

    @Override
    public Object getVariableValue(String requestedVariableName) throws EngineException {
        Object value = null, valueToPrint = null;
        StepVariable stepVariable = (StepVariable) getVariable(requestedVariableName);
        if (stepVariable != null) {
            value = stepVariable.getValueOrNull();
            valueToPrint = Visibility.Logs.printValue(stepVariable.getVisibility(), value);
            if (Engine.logBeans.isDebugEnabled()) {
                if ((value != null) && (value instanceof String))
                    Engine.logBeans
                            .debug("Default value: " + requestedVariableName + " = \"" + valueToPrint + "\"");
                else
                    Engine.logBeans.debug("Default value: " + requestedVariableName + " = " + valueToPrint);
            }

            if (value == null && stepVariable.isRequired()) {
                throw new EngineException("Variable named \"" + requestedVariableName + "\" is required for step \""
                        + getName() + "\"");
            }
        }
        return value;
    }

    public boolean hasVariables() {
        checkSubLoaded();
        return vVariables.size() > 0;
    }

    public int numberOfVariables() {
        checkSubLoaded();
        return vVariables.size();
    }

    public void addVariable(StepVariable variable) throws EngineException {
        checkSubLoaded();
        String newDatabaseObjectName = getChildBeanName(vVariables, variable.getName(), variable.bNew);
        variable.setName(newDatabaseObjectName);
        vVariables.add(variable);
        variable.setParent(this);
        insertOrderedVariable(variable, null);
    }

    private void insertOrderedVariable(Variable variable, Long after) {
        List<Long> ordered = orderedVariables.get(0);
        int size = ordered.size();

        if (ordered.contains(variable.priority))
            return;

        if (after == null) {
            after = new Long(0);
            if (size > 0)
                after = ordered.get(ordered.size() - 1);
        }

        int order = ordered.indexOf(after);
        ordered.add(order + 1, variable.priority);
        hasChanged = true;
    }

    public void removeVariable(StepVariable variable) {
        checkSubLoaded();
        vVariables.remove(variable);
        variable.setParent(null);
        removeOrderedVariable(variable.priority);
    }

    private void removeOrderedVariable(Long value) {
        Collection<Long> ordered = orderedVariables.get(0);
        ordered.remove(value);
        hasChanged = true;
    }

    public void insertAtOrder(DatabaseObject databaseObject, long priority) throws EngineException {
        increaseOrder(databaseObject, new Long(priority));
    }

    private void increaseOrder(DatabaseObject databaseObject, Long before) throws EngineException {
        List<Long> ordered = null;
        Long value = new Long(databaseObject.priority);

        if (databaseObject instanceof Variable)
            ordered = orderedVariables.get(0);

        if (!ordered.contains(value))
            return;
        int pos = ordered.indexOf(value);
        if (pos == 0)
            return;

        if (before == null)
            before = ordered.get(pos - 1);
        int pos1 = ordered.indexOf(before);

        ordered.add(pos1, value);
        ordered.remove(pos + 1);
        hasChanged = true;
    }

    private void decreaseOrder(DatabaseObject databaseObject, Long after) throws EngineException {
        List<Long> ordered = null;
        long value = databaseObject.priority;

        if (databaseObject instanceof Variable)
            ordered = orderedVariables.get(0);

        if (!ordered.contains(value))
            return;
        int pos = ordered.indexOf(value);
        if (pos + 1 == ordered.size())
            return;

        if (after == null)
            after = ordered.get(pos + 1);
        int pos1 = ordered.indexOf(after);

        ordered.add(pos1 + 1, value);
        ordered.remove(pos);
        hasChanged = true;
    }

    public void increasePriority(DatabaseObject databaseObject) throws EngineException {
        if (databaseObject instanceof Variable)
            increaseOrder(databaseObject, null);
    }

    public void decreasePriority(DatabaseObject databaseObject) throws EngineException {
        if (databaseObject instanceof Variable)
            decreaseOrder(databaseObject, null);
    }

    /**
     * Get representation of order for quick sort of a given database object.
     */
    @Override
    public Object getOrder(Object object) throws EngineException {
        if (object instanceof Variable) {
            List<Long> ordered = orderedVariables.get(0);
            long time = ((Variable) object).priority;
            if (ordered.contains(time))
                return (long) ordered.indexOf(time);
            else
                throw new EngineException("Corrupted variable for step \"" + getName() + "\". Variable \""
                        + ((Variable) object).getName() + "\" with priority \"" + time
                        + "\" isn't referenced anymore.");
        } else
            return super.getOrder(object);
    }

    @Override
    public void stepMoved(StepEvent stepEvent) {
        StepVariable stepVariable;
        for (int i = 0; i < numberOfVariables(); i++) {
            stepVariable = (StepVariable) getVariable(i);
            if (stepVariable != null) {
                XMLVector<String> sourceDefinition = stepVariable.getSourceDefinition();
                if (sourceDefinition.size() > 0) {
                    StepSource source = new StepSource(this, sourceDefinition);
                    if (source != null) {
                        source.updateTargetStep((Step) stepEvent.getSource(), (String) stepEvent.data);
                    }
                }
            }
        }
    }

    @Override
    protected String getSpecificLabel() throws EngineException {
        try {
            StepVariable stepVariable;
            for (int i = 0; i < numberOfVariables(); i++) {
                stepVariable = (StepVariable) getVariable(i);
                if (stepVariable != null) {
                    XMLVector<String> sourceDefinition = stepVariable.getSourceDefinition();
                    if (sourceDefinition.size() > 0) {
                        StepSource source = new StepSource(this, sourceDefinition);
                        if (source != null) {
                            if (source.getLabel().equals("! broken source !"))
                                return " (! broken source in variable !)";
                        }
                    }
                }
            }
        } catch (EngineException e) {
        }
        return "";
    }

    public void importVariableDefinition(RequestableObject requestable) throws EngineException {
        if (!(requestable instanceof IVariableContainer))
            return;

        IVariableContainer container = (IVariableContainer) requestable;

        int size = container.numberOfVariables();
        for (int i = 0; i < size; i++) {
            RequestableVariable variable = (RequestableVariable) container.getVariable(i);
            if (variable != null) {
                String variableName = variable.getName();
                if (getVariable(variableName) == null) {
                    if (!StringUtils.isNormalized(variableName))
                        throw new EngineException("Variable name is not normalized : \"" + variableName + "\".");

                    StepVariable stepVariable = variable.isMultiValued() ? new StepMultiValuedVariable()
                            : new StepVariable();
                    stepVariable.setName(variableName);
                    stepVariable.setDescription(variable.getDescription());
                    stepVariable.setSourceDefinition(new XMLVector<String>());
                    stepVariable.setRequired(variable.isRequired());
                    stepVariable.setValueOrNull(variable.getValueOrNull());
                    stepVariable.setVisibility(variable.getVisibility());
                    addVariable(stepVariable);

                    stepVariable.bNew = true;
                    stepVariable.hasChanged = true;
                    hasChanged = true;
                }
            }
        }
    }

    public void exportVariableDefinition() throws EngineException {
        for (StepVariable stepVariable : getVariables()) {
            String variableName = stepVariable.getName();
            if (sequence.getVariable(variableName) == null) {
                if (!StringUtils.isNormalized(variableName))
                    throw new EngineException("Variable name is not normalized : \"" + variableName + "\".");

                RequestableVariable requestableVariable = stepVariable.isMultiValued()
                        ? new RequestableMultiValuedVariable()
                        : new RequestableVariable();
                requestableVariable.setName(variableName);
                requestableVariable.setDescription(stepVariable.getDescription());
                requestableVariable.setRequired(stepVariable.isRequired());
                requestableVariable.setValueOrNull(stepVariable.getValueOrNull());
                requestableVariable.setVisibility(stepVariable.getVisibility());
                sequence.addVariable(requestableVariable);

                requestableVariable.bNew = true;
                requestableVariable.hasChanged = true;
                sequence.hasChanged = true;
            }
        }
    }

    protected String getPostQuery(Scriptable scope) throws EngineException {
        StepVariable stepVariable;
        String postQuery = "";

        int len = numberOfVariables();
        String variableName;
        int variableVisibility;

        for (int i = 0; i < len; i++) {
            stepVariable = (StepVariable) getVariable(i);
            variableName = stepVariable.getName();
            variableVisibility = stepVariable.getVisibility();
            try {
                // Source value
                Object variableValue = stepVariable.getSourceValue();
                if (variableValue != null)
                    Engine.logBeans.trace("(RequestableStep) found value from source: "
                            + Visibility.Logs.printValue(variableVisibility, variableValue));

                // Otherwise Scope parameter
                if (variableValue == null) {
                    Scriptable searchScope = scope;
                    while ((variableValue == null) && (searchScope != null)) {
                        variableValue = searchScope.get(variableName, searchScope);
                        Engine.logBeans.trace("(RequestableStep) found value from scope: "
                                + Visibility.Logs.printValue(variableVisibility, variableValue));
                        if (variableValue instanceof Undefined)
                            variableValue = null;
                        if (variableValue instanceof UniqueTag
                                && ((UniqueTag) variableValue).equals(UniqueTag.NOT_FOUND))
                            variableValue = null;

                        if (variableValue == null)
                            searchScope = searchScope.getParentScope();// looks up in parent's scope
                    }
                }

                // Otherwise context parameter
                if (variableValue == null) {
                    variableValue = (sequence.context.get(variableName) == null ? null
                            : sequence.context.get(variableName));
                    if (variableValue != null)
                        Engine.logBeans.trace("(RequestableStep) found value from context: "
                                + Visibility.Logs.printValue(variableVisibility, variableValue));
                }

                // Otherwise sequence step default value
                if (variableValue == null) {
                    variableValue = getVariableValue(variableName);
                    if (variableValue != null)
                        Engine.logBeans.trace("(RequestableStep) found default value from step: "
                                + Visibility.Logs.printValue(variableVisibility, variableValue));
                }

                // otherwise value not found
                if (variableValue == null) {
                    Engine.logBeans.trace(
                            "(RequestableStep) Did not find any value for \"" + variableName + "\", ignore it");
                } else {
                    if (bInternalInvoke) {
                        if (stepVariable.isMultiValued() && getProject().isStrictMode()
                                && variableValue instanceof NodeList) {
                            String subXPath = ((StepMultiValuedVariable) stepVariable).getSubXPath();
                            if (subXPath.isEmpty()) {
                                variableValue = XMLUtils.toNodeArray((NodeList) variableValue);
                            } else {
                                TwsCachedXPathAPI xpathAPI = new TwsCachedXPathAPI(this.getProject());
                                NodeList nodeList = (NodeList) variableValue;
                                NodeList[] nodeLists = new NodeList[nodeList.getLength()];
                                for (int j = 0; j < nodeLists.length; j++) {
                                    try {
                                        nodeLists[j] = xpathAPI.selectNodeList(nodeList.item(j), subXPath);
                                    } catch (TransformerException e) {
                                        Engine.logBeans.debug("(RequestableStep) Failed to select subXpath", e);
                                    }
                                }
                                variableValue = nodeLists;
                            }
                        }
                        request.put(variableName, variableValue);
                    } else {
                        String parameterValue;
                        if (variableValue instanceof NodeList) {
                            NodeList list = (NodeList) variableValue;
                            if (list != null) {
                                if (list.getLength() == 0) { // Specifies here empty multivalued variable (HTTP invoque only)
                                    postQuery = addParamToPostQuery(variableName, "_empty_array_", postQuery);
                                } else {
                                    for (int j = 0; j < list.getLength(); j++) {
                                        parameterValue = getNodeValue(list.item(j));
                                        postQuery = addParamToPostQuery(variableName, parameterValue, postQuery);
                                    }
                                }
                            }
                        } else if (variableValue instanceof NativeJavaArray) {
                            Object object = ((NativeJavaArray) variableValue).unwrap();
                            List<String> list = GenericUtils.toString(Arrays.asList((Object[]) object));
                            if (list.size() == 0) { // Specifies here empty multivalued variable (HTTP invoque only)
                                postQuery = addParamToPostQuery(variableName, "_empty_array_", postQuery);
                            } else {
                                for (String value : list) {
                                    postQuery = addParamToPostQuery(variableName, value, postQuery);
                                }
                            }
                        } else if (variableValue instanceof Collection<?>) {
                            List<String> list = GenericUtils.toString((Collection<?>) variableValue);
                            if (list.size() == 0) { // Specifies here empty multivalued variable (HTTP invoque only)
                                postQuery = addParamToPostQuery(variableName, "_empty_array_", postQuery);
                            } else {
                                for (String value : list) {
                                    postQuery = addParamToPostQuery(variableName, value, postQuery);
                                }
                            }
                        } else if (variableValue instanceof String) {
                            parameterValue = variableValue.toString();
                            postQuery = addParamToPostQuery(variableName, parameterValue, postQuery);
                        } else {
                            parameterValue = variableValue.toString();
                            postQuery = addParamToPostQuery(variableName, parameterValue, postQuery);
                        }
                    }
                }
            } catch (ClassCastException e) {
                Engine.logBeans.warn("(RequestableStep) Ignoring parameter '" + variableName
                        + "' because its value is not a string");
            }
        }
        if (bInternalInvoke) {
            return null;
        } else {
            if (Engine.logBeans.isTraceEnabled()) {
                Engine.logBeans.trace("(RequestableStep) postQuery :"
                        + Visibility.Logs.replaceVariables(getVariables(), postQuery));
            }
            return postQuery;
        }
    }

    protected String addParamToPostQuery(String variableName, String parameterValue, String postQuery) {
        if (parameterValue != null) {
            postQuery += ((postQuery.length() != 0) ? "&" : "");
            postQuery += variableName + "=" + encodeValue(parameterValue);
        }
        return postQuery;
    }

    protected int doExecuteMethod() throws ConnectionException, URIException, MalformedURLException {
        int statuscode = -1;

        if (sequence.runningThread.bContinue) {
            // Tells the method to automatically handle authentication.
            method.setDoAuthentication(true);

            // Tells the method to automatically handle redirection.
            method.setFollowRedirects(false);

            try {
                displayCookies();

                HttpUtils.logCurrentHttpConnection(Engine.theApp.httpClient, hostConfiguration, HttpPool.global);
                Engine.logBeans.debug("(RequestableStep) HttpClient: executing method...");
                statuscode = Engine.theApp.httpClient.executeMethod(hostConfiguration, method, httpState);
                Engine.logBeans.debug("(RequestableStep) HttpClient: end of method successfull");

                displayCookies();

            } catch (IOException e) {
                try {
                    HttpUtils.logCurrentHttpConnection(Engine.theApp.httpClient, hostConfiguration,
                            HttpPool.global);
                    Engine.logBeans.warn("(RequestableStep) HttpClient: connection error to " + targetUrl + ": "
                            + e.getMessage() + "; retrying method");
                    statuscode = Engine.theApp.httpClient.executeMethod(hostConfiguration, method, httpState);
                    Engine.logBeans.debug("(RequestableStep) HttpClient: end of method successfull");
                } catch (IOException ee) {
                    throw new ConnectionException("Connection error to " + targetUrl, ee);
                }
            }
        }
        return statuscode;
    }

    protected void displayCookies() {
        // Display the cookies
        Cookie[] cookies = httpState.getCookies();
        if (Engine.logBeans.isTraceEnabled())
            Engine.logBeans.trace("(RequestableStep) HttpClient cookies:" + Arrays.asList(cookies).toString());
    }

    abstract protected byte[] executeMethod()
            throws IOException, URIException, MalformedURLException, EngineException;

    abstract protected void prepareForRequestable(Context javascriptContext, Scriptable scope)
            throws MalformedURLException, EngineException;

    @Override
    protected boolean stepExecute(Context javascriptContext, Scriptable scope) throws EngineException {
        if (isEnable()) {
            if (super.stepExecute(javascriptContext, scope)) {
                try {
                    request = new HashMap<String, Object>();

                    try {
                        prepareForRequestable(javascriptContext, scope);
                    } catch (Exception e) {
                        Engine.logBeans.error("An error occured while preparing transaction step \""
                                + RequestableStep.this.getName() + "\"", e);
                        xmlHttpDocument = ConvertigoError.get(e).buildErrorDocument(sequence.getRequester(),
                                sequence.context, false);
                        flushDocument();
                        return true;
                    }

                    if (bInternalInvoke) {
                        Engine.logBeans.debug("(RequestableStep) Internal invoke requested");
                        InternalRequester internalRequester = new InternalRequester(request,
                                sequence.context.httpServletRequest);

                        Object result = internalRequester.processRequest();

                        // MDC log parameters must return to their original values, because
                        // the internal requester has been executed on the same thread as us.
                        Log4jHelper.mdcSet(sequence.context.logParameters);

                        if (result != null) {
                            xmlHttpDocument = (Document) result;
                            if (Engine.isStudioMode()) {
                                ((Sequence) sequence.getOriginal())
                                        .fireDataChanged(new SequenceEvent(this, result));
                            } else {
                                sequence.fireDataChanged(new SequenceEvent(this, result));
                            }
                            flushDocument();
                        }
                    } else {
                        Engine.logBeans.debug("(RequestableStep) requesting : " + method.getURI());
                        byte[] result = executeMethod();
                        Engine.logBeans.debug(
                                "(RequestableStep) Total read bytes: " + ((result != null) ? result.length : 0));
                        if (result != null) {
                            makeDocument(result);
                            if (Engine.isStudioMode()) {
                                ((Sequence) sequence.getOriginal())
                                        .fireDataChanged(new SequenceEvent(this, result));
                            } else {
                                sequence.fireDataChanged(new SequenceEvent(this, result));
                            }
                            flushDocument();
                        }
                    }

                } catch (Exception e) {
                    setErrorStatus(true);
                    Engine.logBeans.error("An error occured while invoking transaction step \""
                            + RequestableStep.this.getName() + "\"", e);
                } finally {
                    if (!bInternalInvoke && (method != null))
                        method.releaseConnection();
                }
                return true;
            }
        }
        return false;
    }

    private void flushDocument() throws EngineException {
        if (sequence.runningThread.bContinue) {
            sequence.flushStepDocument(executeTimeID, xmlHttpDocument);
        }
    }

    private void makeDocument(byte[] result) throws Exception {
        if (sequence.runningThread.bContinue) {
            xmlHttpDocument = XMLUtils.parseDOM(new ByteArrayInputStream(result));

            // Checks if returned document is a Convertigo error document
            NodeList errors = xmlHttpDocument.getDocumentElement().getElementsByTagName("error");
            if (errors.getLength() > 0) {
                Element error = (Element) errors.item(0);
                if (error.getElementsByTagName("exception").getLength() > 0)
                    Engine.logBeans.warn("(SequenceStep) Retrieved document for step '" + getName() + "' ("
                            + executeTimeID + ") is in error");
            }
        }
    }

    @Override
    public List<DatabaseObject> getAllChildren() {
        List<DatabaseObject> rep = super.getAllChildren();
        List<StepVariable> stepVariables = getAllVariables();
        for (StepVariable stepVariable : stepVariables) {
            rep.add(stepVariable);
        }
        return rep;
    }

    @Override
    public XmlSchemaParticle getXmlSchemaObject(XmlSchemaCollection collection, XmlSchema schema) {
        XmlSchemaElement element = (XmlSchemaElement) super.getXmlSchemaObject(collection, schema);
        String namespace = Project.getProjectTargetNamespace(projectName);
        String localpart = getRequestableName() + "ResponseType";
        element.setSchemaTypeName(new QName(namespace, localpart));
        return element;
    }

    public boolean isGenerateElement() {
        return true;
    }

    abstract protected String getRequestableName();
}