com.twinsoft.convertigo.beans.steps.TransactionStep.java Source code

Java tutorial

Introduction

Here is the source code for com.twinsoft.convertigo.beans.steps.TransactionStep.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.steps;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;

import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.twinsoft.convertigo.beans.common.XMLVector;
import com.twinsoft.convertigo.beans.core.Connector;
import com.twinsoft.convertigo.beans.core.IContextMaintainer;
import com.twinsoft.convertigo.beans.core.ITagsProperty;
import com.twinsoft.convertigo.beans.core.Project;
import com.twinsoft.convertigo.beans.core.RequestableStep;
import com.twinsoft.convertigo.beans.core.Step;
import com.twinsoft.convertigo.beans.core.StepEvent;
import com.twinsoft.convertigo.beans.core.StepSource;
import com.twinsoft.convertigo.beans.core.Transaction;
import com.twinsoft.convertigo.beans.core.TransactionWithVariables;
import com.twinsoft.convertigo.engine.Engine;
import com.twinsoft.convertigo.engine.EngineException;
import com.twinsoft.convertigo.engine.EnginePropertiesManager;
import com.twinsoft.convertigo.engine.EnginePropertiesManager.PropertyName;
import com.twinsoft.convertigo.engine.EngineStatistics;
import com.twinsoft.convertigo.engine.enums.HeaderName;
import com.twinsoft.convertigo.engine.enums.MimeType;
import com.twinsoft.convertigo.engine.enums.Parameter;
import com.twinsoft.convertigo.engine.enums.Visibility;
import com.twinsoft.convertigo.engine.util.GenericUtils;
import com.twinsoft.convertigo.engine.util.ProjectUtils;
import com.twinsoft.convertigo.engine.util.StringUtils;
import com.twinsoft.convertigo.engine.util.VersionUtils;
import com.twinsoft.convertigo.engine.util.XMLUtils;

public class TransactionStep extends RequestableStep implements ITagsProperty {

    private static final long serialVersionUID = -8658842486320491604L;

    protected transient String connectorName = "";
    protected transient String transactionName = "";

    private String sourceTransaction = "";

    private XMLVector<String> connectionStringDefinition = new XMLVector<String>();

    public TransactionStep() {
        super();
    }

    public TransactionStep(boolean synchronous) {
        super();
    }

    @Override
    public TransactionStep clone() throws CloneNotSupportedException {
        TransactionStep clonedObject = (TransactionStep) super.clone();
        clonedObject.setSourceTransaction(sourceTransaction);
        return clonedObject;
    }

    @Override
    public TransactionStep copy() throws CloneNotSupportedException {
        TransactionStep copiedObject = (TransactionStep) super.copy();
        copiedObject.setSourceTransaction(sourceTransaction);
        return copiedObject;
    }

    @Override
    public String getStepNodeName() {
        return "transaction";
    }

    public String getConnectorName() {
        return connectorName;
    }

    public String getTransactionName() {
        return transactionName;
    }

    public XMLVector<String> getConnectionStringDefinition() {
        return connectionStringDefinition;
    }

    public void setConnectionStringDefinition(XMLVector<String> connectionStringDefinition) {
        this.connectionStringDefinition = connectionStringDefinition;
    }

    public String getConnectionStringValue() throws EngineException {
        String connectionStringValue = "";
        if (connectionStringDefinition.size() != 0) {
            StepSource source = new StepSource(this, connectionStringDefinition);
            Object value = source.getContextValues();
            if (value != null) {
                if (value instanceof NodeList) {
                    NodeList list = (NodeList) value;
                    if ((list != null) && (list.getLength() > 0)) {
                        connectionStringValue = getNodeValue(list.item(0));
                    }
                } else {
                    connectionStringValue = value.toString();
                }
            }
        }
        return connectionStringValue;
    }

    @Override
    protected void stepDone() {
        // Remove transaction's context if needed
        removeTransactionContext();

        super.stepDone();
    }

    private void removeTransactionContext() {
        if (Engine.isEngineMode()) {
            if (parent instanceof ParallelStep) {
                if (sequence.useSameJSessionForSteps()) {
                    // TODO??
                } else {
                    if (httpState != null) {
                        Cookie[] httpCookies = httpState.getCookies();
                        int len = httpCookies.length;
                        Cookie cookie = null;
                        for (int i = 0; i < len; i++) {
                            cookie = httpCookies[i];
                            if (cookie.getName().equalsIgnoreCase("JSESSIONID")) {
                                Engine.logBeans
                                        .debug("Executing deletion of transaction's context of TranscationStep \""
                                                + getName() + "\"");
                                Engine.theApp.contextManager.removeAll(cookie.getValue());
                                Engine.logBeans.debug("Deletion of transaction's context of TranscationStep \""
                                        + getName() + "\" done");
                                break;
                            }
                        }
                    }
                }
            }
        }
    }

    protected void prepareForRequestable(Context javascriptContext, Scriptable scope)
            throws MalformedURLException, EngineException {
        Transaction targetTransaction = getTargetTransaction();
        Connector targetConnector = targetTransaction.getConnector();

        String ctxName = getContextName(javascriptContext, scope);
        boolean useSequenceJSession = sequence.useSameJSessionForSteps();

        String connectionStringValue = (String) getConnectionStringValue();

        if (isInternalInvoke()) {
            request.put(Parameter.Project.getName(), new String[] { projectName });
            // request.put(Parameter.Pool.getName(), new String[] { "" });
            request.put(Parameter.MotherSequenceContext.getName(), new String[] { sequence.context.contextID });
            request.put(Parameter.Transaction.getName(), new String[] { targetTransaction.getName() });
            request.put(Parameter.Connector.getName(), new String[] { targetConnector.getName() });
            request.put(Parameter.Context.getName(), new String[] { sequence.addStepContextName(ctxName) });
            request.put(Parameter.SessionId.getName(), new String[] { getTransactionSessionId() });
            if (!connectionStringValue.equals(""))
                request.put(Parameter.ConnectorConnectionString.getName(), new String[] { connectionStringValue });
            getPostQuery(scope);
        } else {
            targetUrl = EnginePropertiesManager.getProperty(PropertyName.APPLICATION_SERVER_CONVERTIGO_URL);
            targetUrl += "/projects/" + projectName + "/.xml?";

            URL url = new URL(targetUrl);
            String host = url.getHost();
            int port = url.getPort();

            Engine.logBeans.trace("(TransactionStep) Host: " + host + ":" + port);
            hostConfiguration.setHost(host, port);

            method = new PostMethod(targetUrl);
            HeaderName.ContentType.setRequestHeader(method, MimeType.WwwForm.value());

            // Set transaction sessionId from context maintainer
            String sessionId = getTransactionSessionId();
            if (useSequenceJSession) {
                Engine.logBeans.trace("(TransactionStep) JSESSIONID required : " + sessionId);
                if (sessionId != null) {
                    method.setRequestHeader("Cookie", "JSESSIONID=" + sessionId + ";");
                    Engine.logBeans.trace("(TransactionStep) JSESSIONID used : " + sessionId);
                } else {
                    Engine.logBeans.trace("(TransactionStep) JSESSIONID is null");
                }
            } else {
                if (sessionId != null) {
                    method.setRequestHeader("Cookie", "JSESSIONID=" + sessionId + ";");
                    Engine.logBeans.trace("(TransactionStep) Transaction JSESSIONID used : " + sessionId);
                } else {
                    Engine.logBeans.trace("(TransactionStep) Transaction JSESSIONID is null");
                }
            }

            String postQuery = getPostQuery(scope);
            if (postQuery.indexOf(Parameter.Connector.getName()) == -1)
                postQuery = addParamToPostQuery(Parameter.Connector.getName(), targetConnector.getName(),
                        postQuery);
            if (postQuery.indexOf(Parameter.Transaction.getName()) == -1)
                postQuery = addParamToPostQuery(Parameter.Transaction.getName(), targetTransaction.getName(),
                        postQuery);
            if (postQuery.indexOf(Parameter.MotherSequenceContext.getName()) == -1)
                postQuery = addParamToPostQuery(Parameter.MotherSequenceContext.getName(),
                        sequence.context.contextID, postQuery);
            if (postQuery.indexOf(Parameter.Context.getName()) == -1)
                postQuery = addParamToPostQuery(Parameter.Context.getName(), sequence.addStepContextName(ctxName),
                        postQuery);
            if (!connectionStringValue.equals(""))
                postQuery = addParamToPostQuery(Parameter.ConnectorConnectionString.getName(),
                        connectionStringValue, postQuery);

            if (Engine.logBeans.isTraceEnabled())
                Engine.logBeans.trace("(TransactionStep) postQuery :"
                        + Visibility.Logs.replaceVariables(getVariables(), postQuery));

            try {
                method.setRequestEntity(new StringRequestEntity(postQuery, null, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                throw new EngineException("Encoding error", e);
            }
        }
    }

    private String getContextName(Context javascriptContext, Scriptable scope) {
        boolean useSameJSessionForSteps = sequence.useSameJSessionForSteps();
        String ctxName = getStepContextName();

        // Auto context name
        if (ctxName.equals("")) {
            ctxName = useSameJSessionForSteps ? "error_" + priority : "default";
            IContextMaintainer transactionContextMaintainer = getTransactionContextMaintainer();
            if (transactionContextMaintainer != null) {
                // Get context name for transaction (inherited or not)
                ctxName = transactionContextMaintainer.getInheritedContextName();
                ctxName = (ctxName == null) ? transactionContextMaintainer.getContextName() : ctxName;
                if (useSameJSessionForSteps) {
                    if (transactionContextMaintainer instanceof ParallelStep) {
                        // Transaction requires new context
                        ctxName += "-" + priority;
                    }
                }
            }
        }
        // Defined context name
        else {
            try {
                evaluate(javascriptContext, scope, ctxName, "ctxName", true);
            } catch (Exception e) {
                evaluated = null;
                Engine.logBeans.trace("(TransactionStep) " + e.getMessage());
            }
            ctxName = (evaluated != null) ? evaluated.toString()
                    : (useSameJSessionForSteps ? "error_" + priority : "default");
        }
        return ctxName;
    }

    private String getTransactionSessionId() {
        if (sequence.useSameJSessionForSteps()) {
            return sequence.getSessionId();
        } else {
            IContextMaintainer transactionContextMaintainer = getTransactionContextMaintainer();
            if (transactionContextMaintainer != null) {
                return transactionContextMaintainer.getTransactionSessionId();
            }
            return null;
        }
    }

    private void setTransactionSessionId(HttpState state) {
        if (sequence.useSameJSessionForSteps())
            return;

        IContextMaintainer transactionContextMaintainer = getTransactionContextMaintainer();
        if (transactionContextMaintainer != null) {
            transactionContextMaintainer.setTransactionSessionId(state);
        }
    }

    public String[] getConnectorNames() {
        try {
            if (!projectName.equals("")) {
                Project p = getTargetProject(projectName);
                List<Connector> v = new ArrayList<Connector>(p.getConnectorsList());
                v = GenericUtils.cast(sort((Vector<?>) v, true));
                String[] connectorNames = new String[v.size() + 1];
                connectorNames[0] = "";
                int i = 0;
                for (Connector connector : v) {
                    connectorNames[i + 1] = connector.getName();
                    i++;
                }
                return connectorNames;
            }
        } catch (EngineException e) {
        }
        return new String[] {};
    }

    public String[] getTransactionNames() {
        try {
            if (!projectName.equals("") && !connectorName.equals("")) {
                Project p = getTargetProject(projectName);
                Connector connector = p.getConnectorByName(connectorName);
                List<Transaction> v = new ArrayList<Transaction>(connector.getTransactionsList());
                v = GenericUtils.cast(sort((Vector<?>) v, true));
                String[] transactionNames = new String[v.size() + 1];
                transactionNames[0] = "";
                int i = 0;
                for (Transaction transaction : v) {
                    transactionNames[i + 1] = transaction.getName();
                    i++;
                }
                return transactionNames;
            }
        } catch (EngineException e) {
        }
        return new String[] {};
    }

    public void importVariableDefinition() throws EngineException {
        Transaction targetTransaction = getTargetTransaction();
        if (targetTransaction != null && targetTransaction instanceof TransactionWithVariables) {
            importVariableDefinition(targetTransaction);
        }
    }

    public Transaction getTargetTransaction() throws EngineException {
        Project p = getTargetProject(projectName);
        Connector connector = (connectorName.equals("") ? p.getDefaultConnector()
                : p.getConnectorByName(connectorName));
        Transaction targetTransaction = (transactionName.equals("") ? connector.getDefaultTransaction()
                : connector.getTransactionByName(transactionName));
        return targetTransaction;
    }

    protected byte[] executeMethod() throws IOException, URIException, MalformedURLException, EngineException {
        Header[] requestHeaders, responseHeaders = null;
        byte[] result = null;
        String contents = null;
        int statuscode = -1;

        if (sequence.runningThread.bContinue) {
            Engine.logBeans.debug(
                    "(TransactionStep) Executing method - " + method.getName() + "(" + method.getPath() + ")");

            String ts = sequence.context.statistics.start(EngineStatistics.EXECUTE_SEQUENCE_CALLS);

            try {
                requestHeaders = method.getRequestHeaders();
                if (Engine.logBeans.isTraceEnabled())
                    Engine.logBeans.trace(
                            "(TransactionStep) Request headers :\n" + Arrays.asList(requestHeaders).toString());

                statuscode = doExecuteMethod();

                Engine.logBeans.debug("(TransactionStep) Status: " + method.getStatusLine().toString());

                // stores transaction sessionId in context maintainer
                setTransactionSessionId(httpState);

                responseHeaders = method.getResponseHeaders();
                if (Engine.logBeans.isTraceEnabled())
                    Engine.logBeans.trace(
                            "(TransactionStep) Response headers:\n" + Arrays.asList(responseHeaders).toString());

                if (statuscode != -1) {
                    InputStream in = method.getResponseBodyAsStream();
                    if (in != null) {
                        ByteArrayOutputStream bos = new ByteArrayOutputStream();
                        byte[] buf = new byte[1024];
                        int len;
                        while ((len = in.read(buf)) > 0) {
                            bos.write(buf, 0, len);
                        }
                        result = bos.toByteArray();
                        in.close();
                        bos.close();
                    }

                    if (Engine.logBeans.isTraceEnabled()) {
                        contents = new String((result != null) ? result : new byte[] {});
                        Engine.logBeans.trace("(TransactionStep) Response content:\n" + contents);
                    }

                }

                if (statuscode >= 300) {
                    throw new EngineException("(TransactionStep) HTTP response returned status :"
                            + ((method != null) ? method.getStatusLine().toString() : String.valueOf(statuscode)));
                }
            } finally {
                if (method != null)
                    method.releaseConnection();

                sequence.context.statistics.stop(ts, sequence.getCurrentChildStep() != 0);
            }
        }
        return result;
    }

    @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 {
            if (VersionUtils.compare(version, "6.0.3") < 0) {
                String projectName = (String) XMLUtils.findPropertyValue(element, "projectName");
                // Handle wrong project name
                if (projectName.equals("")) {
                    projectName = "unknown_project";
                }

                String connectorName = (String) XMLUtils.findPropertyValue(element, "connectorName");
                // If the default connector has been set, find the explicit default connector name
                if (connectorName.equals("")) {
                    NodeList connectorsNodeList = element.getOwnerDocument().getElementsByTagName("connector");
                    Node connectorNode = XMLUtils.findNodeByAttributeValue(connectorsNodeList, "default", "true");
                    if (connectorNode != null) {
                        connectorName = (String) XMLUtils.findPropertyValue((Element) connectorNode, "name");
                    } else {
                        throw new EngineException(
                                "Unable to find the default connector for the project '" + projectName + "'");
                    }
                }

                String transactionName = (String) XMLUtils.findPropertyValue(element, "transactionName");
                // If the default transaction has been set, find the explicit default transaction name
                if (transactionName.equals("")) {
                    NodeList connectorsNodeList = element.getOwnerDocument().getElementsByTagName("connector");
                    int nlLen = connectorsNodeList.getLength();
                    for (int i = 0; i < nlLen; i++) {
                        Element connectorElement = (Element) connectorsNodeList.item(i);
                        String connectorNameElement = (String) XMLUtils.findPropertyValue(connectorElement, "name");
                        if (connectorName.equals(connectorNameElement)) {
                            NodeList transactionsNodeList = connectorElement.getElementsByTagName("transaction");
                            Node transactionNode = XMLUtils.findNodeByAttributeValue(transactionsNodeList,
                                    "default", "true");
                            if (transactionNode != null) {
                                transactionName = (String) XMLUtils.findPropertyValue((Element) transactionNode,
                                        "name");
                                break;
                            }
                            throw new EngineException("Unable to find the default transaction for the connector '"
                                    + connectorName + "' from project '" + projectName + "'");
                        }
                    }
                }

                String sourceTransaction = projectName + SequenceStep.SOURCE_SEPARATOR + connectorName
                        + SequenceStep.SOURCE_SEPARATOR + transactionName;

                setSourceTransaction(sourceTransaction);

                hasChanged = true;
                Engine.logBeans.warn("[SequenceStpe] The object \"" + getName()
                        + "\" has been updated to version 6.0.3; source transaction: " + sourceTransaction);
            }
        } catch (Exception e) {
            throw new EngineException(
                    "Unable to migrate the source definition for CallTransaction step \"" + getName() + "\".", e);
        }
    }

    @Override
    public Element toXml(Document document) throws EngineException {
        Element element = super.toXml(document);

        // Storing the transaction WSDL type
        try {
            Element wsdlTypeElement = document.createElement("wsdltype");
            if (wsdlType != null) {
                CDATASection cDATASection = document.createCDATASection(wsdlType);
                wsdlTypeElement.appendChild(cDATASection);
                element.appendChild(wsdlTypeElement);
            }
        } catch (NullPointerException e) {
            // Silently ignore
        }

        return element;
    }

    @Override
    public String[] getTagsForProperty(String propertyName) {
        if (propertyName.equals("sourceTransaction")) {
            List<String> transactionsList = new ArrayList<String>();

            try {
                List<String> projectNames = Engine.theApp.databaseObjectsManager.getAllProjectNamesList();
                for (String projectName : projectNames) {
                    Project project = Engine.theApp.databaseObjectsManager.getProjectByName(projectName);

                    List<Connector> connectors = project.getConnectorsList();
                    for (Connector connector : connectors) {
                        String connectorName = connector.getName();

                        List<Transaction> transactions = connector.getTransactionsList();

                        for (Transaction transaction : transactions) {
                            transactionsList.add(projectName + SOURCE_SEPARATOR + connectorName + SOURCE_SEPARATOR
                                    + transaction.getName());
                        }
                    }
                }
            } catch (EngineException e) {
                // Just ignore, should never happen
            }

            return transactionsList.toArray(new String[] {});
        }
        return super.getTagsForProperty(propertyName);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.twinsoft.convertigo.beans.core.RequestableStep#getSpecificLabel()
     */
    @Override
    protected String getSpecificLabel() throws EngineException {
        String label = super.getSpecificLabel();
        if (label.equals("")) {// normal case
            // Check for project
            if (projectName.equals("")) {
                label = "! broken project !";
            } else {
                // Check for project
                try {
                    if (ProjectUtils.existProjectSchemaReference(getProject(), projectName)) {
                        Project p = getTargetProject(projectName);
                        if (p == null) {
                            label = "! broken project!";
                        } else {
                            // Check for connector
                            Connector connector = (connectorName.equals("") ? p.getDefaultConnector()
                                    : p.getConnectorByName(connectorName));
                            if (connector == null) {
                                label = "! broken connector !";
                            } else {
                                // Check for transaction
                                Transaction transaction = (transactionName.equals("")
                                        ? connector.getDefaultTransaction()
                                        : connector.getTransactionByName(transactionName));
                                if (transaction == null) {
                                    label = "! broken transaction !";
                                } else {
                                    // Check for connection string
                                    StepSource source = new StepSource(this, connectionStringDefinition);
                                    if (source != null && !source.isEmpty()) {
                                        String csl = source.getLabel();
                                        if (csl.equals("! broken source !")) {
                                            label = " (! broken source in Connection string !)";
                                        }
                                    }
                                }
                            }
                        }
                    } else {
                        label = "! broken reference !";
                    }
                } catch (Exception e) {
                    label = "! broken project !";
                }
            }
        }
        return label;
    }

    @Override
    public String toString() {
        String label = "";
        try {
            label += getLabel();
        } catch (EngineException e) {
        }
        return StringUtils.normalize("Call_" + getSourceTransaction()) + (label.equals("") ? "" : " ") + label;
    }

    public String getSourceTransaction() {
        return sourceTransaction;
    }

    public void setSourceTransaction(String sourceTransaction) {
        this.sourceTransaction = sourceTransaction;
        StringTokenizer st = new StringTokenizer(sourceTransaction, TransactionStep.SOURCE_SEPARATOR);
        try {
            projectName = st.nextToken();
            connectorName = st.nextToken();
            transactionName = st.nextToken();
        } catch (Exception e) {
        }
    }

    protected String getRequestableName() {
        return connectorName + "__" + transactionName;
    }

    @Override
    public void stepMoved(StepEvent stepEvent) {
        super.stepMoved(stepEvent);

        StepSource source = new StepSource(this, connectionStringDefinition);
        if (source != null && !source.isEmpty()) {
            source.updateTargetStep((Step) stepEvent.getSource(), (String) stepEvent.data);
        }
    }

}