org.apache.ode.test.BPELTestAbstract.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ode.test.BPELTestAbstract.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.ode.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.geronimo.connector.outbound.GenericConnectionManager;
import org.apache.geronimo.connector.outbound.connectionmanagerconfig.LocalTransactions;
import org.apache.geronimo.connector.outbound.connectionmanagerconfig.PoolingSupport;
import org.apache.geronimo.connector.outbound.connectionmanagerconfig.SinglePool;
import org.apache.geronimo.connector.outbound.connectionmanagerconfig.TransactionSupport;
import org.apache.geronimo.connector.outbound.connectiontracking.ConnectionTracker;
import org.apache.geronimo.connector.outbound.connectiontracking.ConnectionTrackingCoordinator;
import org.apache.geronimo.transaction.manager.RecoverableTransactionManager;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.resource.spi.ConnectionManager;
import javax.sql.DataSource;
import javax.transaction.TransactionManager;
import javax.xml.namespace.QName;

import org.apache.derby.jdbc.EmbeddedXADataSource;
import org.apache.ode.bpel.common.evt.DebugBpelEventListener;
import org.apache.ode.bpel.dao.BpelDAOConnectionFactory;
import org.apache.ode.bpel.engine.BpelServerImpl;
import org.apache.ode.bpel.iapi.Message;
import org.apache.ode.bpel.iapi.MessageExchange;
import org.apache.ode.bpel.iapi.MyRoleMessageExchange;
import org.apache.ode.bpel.iapi.ProcessStore;
import org.apache.ode.bpel.iapi.ProcessStoreEvent;
import org.apache.ode.bpel.iapi.ProcessStoreListener;
import org.apache.ode.bpel.iapi.MessageExchange.Status;
import org.apache.ode.bpel.iapi.MyRoleMessageExchange.CorrelationStatus;
import org.apache.ode.bpel.memdao.BpelDAOConnectionFactoryImpl;
import org.apache.ode.il.EmbeddedGeronimoFactory;
import org.apache.ode.il.config.OdeConfigProperties;
import org.apache.ode.store.ProcessConfImpl;
import org.apache.ode.store.ProcessStoreImpl;
import org.apache.ode.utils.DOMUtils;
import org.apache.ode.utils.GUID;
import org.apache.ode.scheduler.simple.SimpleScheduler;
import org.apache.ode.scheduler.simple.JdbcDelegate;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.w3c.dom.Element;
import java.util.concurrent.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public abstract class BPELTestAbstract {
    private static final Log log = LogFactory.getLog(BPELTestAbstract.class);
    public static final long WAIT_BEFORE_INVOKE_TIMEOUT = 2000;

    private static final String SHOW_EVENTS_ON_CONSOLE = "no";

    protected BpelServerImpl _server;

    public static TransactionManager _txManager = null;
    public static DataSource _dataSource = null;

    protected ProcessStore store;

    protected MessageExchangeContextImpl mexContext;

    protected EntityManager em;

    protected EntityManagerFactory emf;

    protected ExecutorService executorService = Executors.newFixedThreadPool(1);

    protected SimpleScheduler scheduler;

    protected BpelDAOConnectionFactory _cf;

    /** Failures that have been detected. */
    protected List<Failure> _failures;

    /** The things we'd like to deploy. */
    protected List<Deployment> _deployments;

    /** The things we'd like to invoke. */
    protected List<Invocation> _invocations;

    /** What's actually been deployed. */
    private List<Deployment> _deployed;

    @Before
    public void setUp() throws Exception {
        EmbeddedGeronimoFactory factory = new EmbeddedGeronimoFactory();
        if (_txManager == null) {
            _txManager = createTransactionManager();
            org.springframework.mock.jndi.SimpleNamingContextBuilder.emptyActivatedContextBuilder()
                    .bind("java:comp/UserTransaction", _txManager);
            _dataSource = createDataSource(false);
            {
                _txManager.begin();
                try {
                    Connection c = _dataSource.getConnection();
                    c.prepareStatement(org.apache.commons.io.IOUtils
                            .toString(getClass().getResourceAsStream("/scheduler-schema.sql"))).execute();
                    c.close();
                } catch (Exception e) {

                }
                _txManager.commit();
            }
        }
        //        try {
        //            _dataSource.getConnection();
        //        } catch (Exception e) {
        //            
        //        }
        //        createDataSource(false);
        _failures = new CopyOnWriteArrayList<Failure>();
        _server = new BpelServerImpl();
        Properties props = getConfigProperties();
        _server.setConfigProperties(props);
        mexContext = new MessageExchangeContextImpl();
        _deployments = new ArrayList<Deployment>();
        _invocations = new ArrayList<Invocation>();
        _deployed = new ArrayList<Deployment>();

        {
            JdbcDelegate del = new JdbcDelegate(_dataSource);
            scheduler = new SimpleScheduler("node", del, props);
            scheduler.setTransactionManager(_txManager);
            _cf = new BpelDAOConnectionFactoryImpl(scheduler);
            _server.setDaoConnectionFactory(_cf);
        }
        _server.setInMemDaoConnectionFactory(new BpelDAOConnectionFactoryImpl(scheduler));
        _server.setScheduler(scheduler);
        _server.setBindingContext(new BindingContextImpl());
        _server.setMessageExchangeContext(mexContext);
        scheduler.setJobProcessor(_server);
        scheduler.setExecutorService(executorService);
        store = new ProcessStoreImpl(null, _dataSource, "hib", new OdeConfigProperties(new Properties(), ""), true);
        store.registerListener(new ProcessStoreListener() {
            public void onProcessStoreEvent(ProcessStoreEvent event) {
                // bounce the process
                _server.unregister(event.pid);
                if (event.type != ProcessStoreEvent.Type.UNDEPLOYED) {
                    ProcessConfImpl conf = (ProcessConfImpl) store.getProcessConfiguration(event.pid);
                    // Test processes always run with in-mem DAOs
                    conf.setTransient(true);
                    _server.register(conf);
                }
            }
        });
        _server.registerBpelEventListener(new DebugBpelEventListener());
        _server.init();
        _server.start();
        scheduler.start();
    }

    @After
    public void tearDown() throws Exception {
        for (Deployment d : _deployed) {
            try {
                store.undeploy(d.deployDir);
            } catch (Exception ex) {
                ex.printStackTrace();
                System.err.println("Error undeploying " + d);
            }
        }

        if (em != null)
            em.close();
        if (emf != null)
            emf.close();

        _server.stop();
        _failures = null;
        _deployed = null;
        _deployments = null;
        _invocations = null;

    }

    protected TransactionManager createTransactionManager() throws Exception {
        EmbeddedGeronimoFactory factory = new EmbeddedGeronimoFactory();
        TransactionManager _txManager = factory.getTransactionManager();
        _txManager.setTransactionTimeout(30);
        return _txManager;
    }

    protected DataSource createDataSource(boolean shutdown) throws Exception {
        TransactionSupport transactionSupport = LocalTransactions.INSTANCE;
        ConnectionTracker connectionTracker = new ConnectionTrackingCoordinator();

        PoolingSupport poolingSupport = new SinglePool(10, 0, 1000, 1, true, false, false);

        ConnectionManager connectionManager = new GenericConnectionManager(transactionSupport, poolingSupport, null,
                connectionTracker, (RecoverableTransactionManager) _txManager, getClass().getName(),
                getClass().getClassLoader());

        org.tranql.connector.derby.EmbeddedLocalMCF mcf = new org.tranql.connector.derby.EmbeddedLocalMCF();
        mcf.setCreateDatabase(true);
        mcf.setDatabaseName("target/testdb");
        mcf.setUserName("sa");
        mcf.setPassword("");
        if (shutdown) {
            mcf.setShutdownDatabase("shutdown");
        }
        return (DataSource) mcf.createConnectionFactory(connectionManager);

        //        d = org.tranql.connector.jdbc.JDBCDriverMCF();
        //        EmbeddedXADataSource ds = new EmbeddedXADataSource();
        //        ds.setCreateDatabase("create");
        //        ds.setDatabaseName("target/testdb");
        //        ds.setUser("sa");
        //        ds.setPassword("");
        //        _dataSource = ds;
        //        return _dataSource;
    }

    protected void negative(String deployDir) throws Throwable {
        try {
            go(new File(deployDir));
        } catch (junit.framework.AssertionFailedError ex) {
            return;
        }
        Assert.fail("Expecting test to fail");
    }

    protected void go(String deployDir) throws Exception {
        go(makeDeployDir(deployDir));
    }

    protected Deployment addDeployment(String deployDir) {
        return addDeployment(makeDeployDir(deployDir));
    }

    protected Deployment addDeployment(File deployDir) {
        Deployment deployment = new Deployment(deployDir);
        _deployments.add(deployment);
        return deployment;
    }

    protected void go(File deployDir) throws Exception {
        setup(deployDir);
        go();
    }

    protected void setup(File deployDir) throws Exception {
        addDeployment(deployDir);
        int propsFileCnt = 0;
        File testPropsFile = new File(deployDir, "test.properties");
        if (!testPropsFile.exists()) {
            propsFileCnt++;
            testPropsFile = new File(deployDir, "test" + propsFileCnt + ".properties");
            if (!testPropsFile.exists()) {
                System.err.println("can't find " + testPropsFile);
            }
        }

        if (!testPropsFile.exists()) {
            Assert.fail("Test property file not found in " + deployDir);
        }

        while (testPropsFile.exists()) {
            Properties testProps = new Properties();
            InputStream is = new FileInputStream(testPropsFile);
            try {
                testProps.load(is);
            } finally {
                is.close();
            }

            final QName serviceId = new QName(testProps.getProperty("namespace"), testProps.getProperty("service"));
            final String operation = testProps.getProperty("operation");

            Boolean sequential = Boolean.parseBoolean(testProps.getProperty("sequential", "false"));

            Invocation last = null;
            for (int i = 1; testProps.getProperty("request" + i) != null; i++) {
                final String in = testProps.getProperty("request" + i);
                final String responsePattern = testProps.getProperty("response" + i);

                last = addInvoke(testPropsFile + "#" + i, serviceId, operation, in, responsePattern,
                        sequential ? last : null);
            }
            propsFileCnt++;
            testPropsFile = new File(deployDir, "test" + propsFileCnt + ".properties");
        }
    }

    protected Invocation addInvoke(String id, QName target, String operation, String request,
            String responsePattern) throws Exception {
        return addInvoke(id, target, operation, request, responsePattern, null);
    }

    protected Invocation addInvoke(String id, QName target, String operation, String request,
            String responsePattern, Invocation synchronizeWith) throws Exception {

        Invocation inv = new Invocation(id, synchronizeWith);
        inv.target = target;
        inv.operation = operation;
        inv.request = DOMUtils.stringToDOM(request);
        inv.expectedStatus = null;
        if (responsePattern != null) {
            inv.expectedFinalStatus = MessageExchange.Status.RESPONSE;
            inv.expectedResponsePattern = Pattern.compile(responsePattern, Pattern.DOTALL);
        }

        _invocations.add(inv);
        return inv;
    }

    protected void go() throws Exception {
        try {
            doDeployments();
            doInvokes();
        } finally {
            checkFailure();
        }
    }

    protected void checkFailure() {
        StringBuffer sb = new StringBuffer("Failure report:\n");
        for (Failure failure : _failures) {
            sb.append(failure);
            sb.append('\n');
        }
        if (_failures.size() != 0) {
            System.err.println(sb.toString());
            Assert.fail(sb.toString());
        }
    }

    protected Deployment deploy(String location) {
        Deployment deployment = new Deployment(makeDeployDir(location));
        doDeployment(deployment);
        return deployment;
    }

    protected void doDeployments() {
        for (Deployment d : _deployments)
            doDeployment(d);
    }

    /**
     * Do all the registered deployments.
     *
     * @param d
     */
    protected void doDeployment(Deployment d) {
        Collection<QName> procs;

        try {
            procs = store.deploy(d.deployDir);

            _deployed.add(d);
        } catch (Exception ex) {
            if (d.expectedException == null) {
                log.error("", ex);
                failure(d, "DEPLOY: Unexpected exception: " + ex, ex);
            } else if (!d.expectedException.isAssignableFrom(ex.getClass())) {
                log.error("", ex);
                failure(d, "DEPLOY: Wrong exception; expected " + d.expectedException + " but got " + ex.getClass(),
                        ex);
            }
            return;
        }

        try {
            for (QName procName : procs) {
                ProcessConfImpl conf = (ProcessConfImpl) store.getProcessConfiguration(procName);
                // Test processes always run with in-mem DAOs
                conf.setTransient(true);
                _server.register(conf);
            }
        } catch (Exception ex) {
            if (d.expectedException == null)
                failure(d, "REGISTER: Unexpected exception: " + ex, ex);
            else if (!d.expectedException.isAssignableFrom(ex.getClass()))
                failure(d,
                        "REGISTER: Wrong exception; expected " + d.expectedException + " but got " + ex.getClass(),
                        ex);
        }
    }

    protected void doUndeployments() {
        for (Deployment d : _deployments) {
            try {
                undeploy(d);
            } catch (Exception ex) {
                ex.printStackTrace();
                failure(d, "Undeployment failed.", ex);
            }
        }

        _deployments.clear();
    }

    protected void undeploy(Deployment d) {
        if (_deployed.contains(d)) {
            _deployed.remove(d);
            store.undeploy(d.deployDir);
        }
    }

    protected void doInvokes() throws Exception {
        ArrayList<Thread> testThreads = new ArrayList<Thread>();
        for (Invocation i : _invocations) {
            InvokerThread t = new InvokerThread(i);
            testThreads.add(t);
        }

        for (Thread testThread : testThreads) {
            testThread.start();
            if (testThreads.size() > 0)
                Thread.sleep(getWaitBeforeInvokeTimeout());
        }

        for (Thread testThread : testThreads)
            testThread.join();

    }

    protected long getWaitBeforeInvokeTimeout() {
        return WAIT_BEFORE_INVOKE_TIMEOUT;
    }

    private void failure(Object where) {
        failure(where, "Failure", null);
    }

    private void failure(Object where, String message, Exception ex) {
        Failure f = new Failure(where, message, ex);
        _failures.add(f);
        Assert.fail(f.toString());
    }

    private void failure(Object where, String message, Object expected, Object actual) {
        Failure f = new Failure(where, message, expected, actual, null);
        _failures.add(f);
        Assert.fail(f.toString());
    }

    protected boolean isFailed() {
        return !_failures.isEmpty();
    }

    protected File makeDeployDir(String deployDir) {
        String deployxml = deployDir + "/deploy.xml";
        URL deployxmlurl = getClass().getResource(deployxml);
        if (deployxmlurl == null) {
            Assert.fail("Resource not found: " + deployxml);
        }
        try {
            return new File(deployxmlurl.toURI().getPath()).getParentFile();
        } catch (URISyntaxException e) {
            e.printStackTrace();
            Assert.fail(e.getMessage());
            return null;
        }
    }

    /**
     * Override this to provide configuration properties for Ode extensions
     * like BpelEventListeners.
     *
     * @return
     */
    protected Properties getConfigProperties() {
        // could also return null, returning an empty properties
        // object is more fail-safe.
        Properties p = new Properties();
        p.setProperty("debugeventlistener.dumpToStdOut", SHOW_EVENTS_ON_CONSOLE);
        return p;
    }

    protected static class Failure {
        Object where;

        String msg;

        Object expected;

        Object actual;

        Exception ex;

        public Failure(Object where, String msg, Exception ex) {
            this(where, msg, null, null, ex);
        }

        public Failure(Object where, String msg, Object expected, Object actual, Exception ex) {
            this.actual = actual;
            this.expected = expected;
            this.where = where;
            this.msg = msg;
            this.ex = ex;
        }

        public String toString() {
            StringBuffer sbuf = new StringBuffer(where + ": " + msg);
            if (ex != null) {
                sbuf.append("; got exception msg: " + ex.getMessage());
                ex.printStackTrace();
            }
            if (actual != null)
                sbuf.append("; got " + actual + ", expected " + expected);
            return sbuf.toString();
        }
    }

    /**
     * Represents a test deployement.
     *
     * @author mszefler
     *
     */
    public static class Deployment {
        /** The directory containing the deploy.xml and artefacts. */
        public File deployDir;

        /** If non-null the type of exception we expect to get when we deploy. */
        public Class expectedException = null;

        public Deployment(File deployDir) {
            this.deployDir = deployDir;
        }

        public String toString() {
            return "Deployment#" + deployDir;
        }
    }

    /**
     * Represents an test invocation of the BPEL engine.
     *
     * @author mszefler
     */
    public static class Invocation {
        /** Identifier (for reporting). */
        public String id;

        /** for sync invocations */
        public Invocation synchronizeWith;

        /** checking completion */
        public boolean done = false;

        /** Name of the operation to invoke. */
        public String operation;

        /** Name of service to invoke. */
        public QName target;

        /** Expected RegExp pattern for the response, or null */
        public Pattern expectedResponsePattern;

        /** The request message that should be sent to the server */
        public Element request;

        /** Number of ms to wait (relative to other invokes) before invoking. */
        public long invokeDelayMs = 0L;

        /** If non-null, expect an exception of this class (or subclass) on invoke. */
        public Class expectedInvokeException = null;

        /** If non-null, expecte this status right after invoke. */
        public MessageExchange.Status expectedStatus = null;

        /** If non-null, expect this status after response received. */
        public MessageExchange.Status expectedFinalStatus = MessageExchange.Status.COMPLETED_OK;

        /** If non-null, expect this correlation status right after invoke. */
        public CorrelationStatus expectedCorrelationStatus = null;

        /** If non-null, expect this correlation after response received. */
        public CorrelationStatus expectedFinalCorrelationStatus = null;

        /** Maximum number of ms to wait for a response. */
        public long maximumWaitMs = 60 * 1000;

        /** If non-null, minimum number of ms before a response should be available. */
        public Long minimumWaitMs = null;

        long invokeTime;

        Exception invokeException;

        QName requestType;

        public Invocation(String id, Invocation synchronizeWith) {
            this.id = id;
            this.synchronizeWith = synchronizeWith;
        }

        public String toString() {
            return "Invocation#" + id;
        }

    }

    class InvokerThread extends Thread {
        Invocation _invocation;

        InvokerThread(Invocation invocation) {
            _invocation = invocation;
        }

        public void run() {
            try {
                try {
                    run2();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } finally {
                synchronized (_invocation) {
                    _invocation.done = true;
                    _invocation.notify();
                }
            }
        }

        public void run2() throws Exception {
            final MyRoleMessageExchange[] mex = new MyRoleMessageExchange[1];
            final Future[] running = new Future[1];

            // Wait for it....
            try {
                Thread.sleep(_invocation.invokeDelayMs);
            } catch (Exception ex) {
            }

            if (_invocation.synchronizeWith != null) {
                synchronized (_invocation.synchronizeWith) {
                    while (!_invocation.synchronizeWith.done) {
                        try {
                            _invocation.synchronizeWith.wait(_invocation.maximumWaitMs);
                        } catch (InterruptedException e) {
                            failure(_invocation, "timed out waiting in sequence", e);
                            return;
                        }
                    }
                }
            }

            scheduler.execTransaction(new Callable<Void>() {
                public Void call() throws Exception {
                    try {
                        mex[0] = _server.getEngine().createMessageExchange(new GUID().toString(),
                                _invocation.target, _invocation.operation);
                        mexContext.clearCurrentResponse();

                        Message request = mex[0].createMessage(_invocation.requestType);
                        request.setMessage(_invocation.request);
                        _invocation.invokeTime = System.currentTimeMillis();
                        running[0] = mex[0].invoke(request);

                        Status status = mex[0].getStatus();
                        CorrelationStatus cstatus = mex[0].getCorrelationStatus();
                        if (_invocation.expectedStatus != null && !status.equals(_invocation.expectedStatus))
                            failure(_invocation, "Unexpected message exchange status", _invocation.expectedStatus,
                                    status);

                        if (_invocation.expectedCorrelationStatus != null
                                && !cstatus.equals(_invocation.expectedCorrelationStatus))
                            failure(_invocation, "Unexpected correlation status",
                                    _invocation.expectedCorrelationStatus, cstatus);
                        return null;

                    } catch (Exception ex) {
                        if (_invocation.expectedInvokeException == null)
                            failure(_invocation, "Unexpected invocation exception.", ex);
                        else if (_invocation.expectedInvokeException.isAssignableFrom(ex.getClass()))
                            failure(_invocation, "Unexpected invocation exception.",
                                    _invocation.expectedInvokeException, ex.getClass());

                        return null;
                    }
                }
            });

            if (isFailed())
                return;

            try {
                running[0].get(_invocation.maximumWaitMs, TimeUnit.MILLISECONDS);
            } catch (Exception ex) {
                failure(_invocation, "Exception on future object.", ex);
                return;
            }

            long ctime = System.currentTimeMillis();
            long itime = ctime - _invocation.invokeTime;
            if (_invocation.minimumWaitMs != null && _invocation.minimumWaitMs >= itime)
                failure(_invocation, "Response received too soon.", _invocation.minimumWaitMs, itime);

            if (_invocation.maximumWaitMs <= itime)
                failure(_invocation, "Response took too long.", _invocation.maximumWaitMs, itime);

            if (isFailed())
                return;

            if (_invocation.expectedResponsePattern != null) {
                scheduler.execTransaction(new Callable<Void>() {
                    public Void call() throws Exception {
                        Status finalstat = mex[0].getStatus();
                        if (_invocation.expectedFinalStatus != null
                                && !_invocation.expectedFinalStatus.equals(finalstat))
                            if (finalstat.equals(Status.FAULT)) {
                                failure(_invocation, "Unexpected final message exchange status",
                                        _invocation.expectedFinalStatus,
                                        "FAULT: " + mex[0].getFault() + " | " + mex[0].getFaultExplanation());
                            } else {
                                failure(_invocation, "Unexpected final message exchange status",
                                        _invocation.expectedFinalStatus, finalstat);
                            }

                        if (_invocation.expectedFinalCorrelationStatus != null
                                && !_invocation.expectedFinalCorrelationStatus
                                        .equals(mex[0].getCorrelationStatus())) {
                            failure(_invocation, "Unexpected final correlation status",
                                    _invocation.expectedFinalCorrelationStatus, mex[0].getCorrelationStatus());
                        }
                        if (mex[0].getResponse() == null)
                            failure(_invocation, "Expected response, but got none.", null);
                        String responseStr = DOMUtils.domToString(mex[0].getResponse().getMessage());
                        //System.out.println("=>" + responseStr);
                        Matcher matcher = _invocation.expectedResponsePattern.matcher(responseStr);
                        if (!matcher.matches())
                            failure(_invocation, "Response does not match expected pattern",
                                    _invocation.expectedResponsePattern, responseStr);
                        return null;
                    }
                });
            }
        }
    }
}