com.p6spy.engine.spy.XADataSourceTest.java Source code

Java tutorial

Introduction

Here is the source code for com.p6spy.engine.spy.XADataSourceTest.java

Source

/*
 * #%L
 * P6Spy
 * %%
 * Copyright (C) 2002 - 2013 P6Spy
 * %%
 * Licensed 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.
 * #L%
 */
package com.p6spy.engine.spy;

import static org.junit.Assert.assertEquals;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.naming.NamingException;
import javax.sql.DataSource;
import javax.sql.XADataSource;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.TransactionManager;

import liquibase.exception.LiquibaseException;

import org.apache.commons.beanutils.PropertyUtils;
import org.eclipse.jetty.plus.jndi.Resource;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;

import bitronix.tm.BitronixTransactionManager;
import bitronix.tm.TransactionManagerServices;
import bitronix.tm.resource.jdbc.PoolingDataSource;

import com.p6spy.engine.common.P6Util;
import com.p6spy.engine.test.P6TestFramework;
import com.p6spy.engine.test.P6TestLoadableOptions;
import com.p6spy.engine.test.P6TestOptions;

@RunWith(Parameterized.class)
public class XADataSourceTest extends P6TestFramework {

    private static final Pattern URL_PATTERN = Pattern
            .compile("jdbc:([a-zA-Z0-9]+)://([a-zA-Z0-9]+)[:]?([0-9]*)/([a-zA-Z0-9]+)");

    private TransactionManager tm;
    private List<Resource> jndiResources;
    private List<PoolingDataSource> poolingDSs;

    public XADataSourceTest(String db) throws SQLException, IOException {
        super(db);
    }

    @Parameters(name = "{index}: {0}")
    public static Collection<Object[]> dbs() {
        Collection<Object[]> result = new ArrayList<Object[]>();
        for (Object o : P6TestFramework.dbs()) {
            // SQLite provides no datasource implementation => skip it
            if (!Arrays.equals(new Object[] { "SQLite" }, (Object[]) o)) {
                result.add((Object[]) o);
            }
        }
        return result;
    }

    @Before
    public void setUpXADataSourceTest() throws NamingException, ClassNotFoundException, IllegalAccessException,
            InvocationTargetException, NoSuchMethodException, InstantiationException {
        final P6TestLoadableOptions testOptions = P6TestOptions.getActiveInstance();
        jndiResources = new ArrayList<Resource>();
        poolingDSs = new ArrayList<PoolingDataSource>();
        tm = TransactionManagerServices.getTransactionManager();

        // in test DS setup
        {
            final XADataSource realInTestDs = (XADataSource) P6Util
                    .forName(testOptions.getXaDataSource().getClass().getName()).newInstance();
            setXADSProperties(realInTestDs, testOptions.getUrl().replace(":p6spy", ""), testOptions.getUser(),
                    testOptions.getPassword());
            jndiResources.add(new Resource("jdbc/realInTestDs", realInTestDs));

            final PoolingDataSource inTestDs = new PoolingDataSource();
            inTestDs.setClassName(P6DataSource.class.getName());
            inTestDs.setUniqueName("jdbc/inTestDs");
            inTestDs.setMaxPoolSize(10);
            inTestDs.getDriverProperties().setProperty("realDataSource", "jdbc/realInTestDs");
            inTestDs.setAllowLocalTransactions(true);
            inTestDs.init();
            jndiResources.add(new Resource("jdbc/inTestDs", inTestDs));
            poolingDSs.add(inTestDs);
        }

        // fixed DS setup
        {
            final XADataSource realFixedDs = (XADataSource) P6Util.forName("org.h2.jdbcx.JdbcDataSource")
                    .newInstance();
            setXADSProperties(realFixedDs, "jdbc:h2:mem:p6spy_realFixedDs", "sa", "sa");
            jndiResources.add(new Resource("jdbc/realFixedDs", realFixedDs));

            final PoolingDataSource fixedDs = new PoolingDataSource();
            fixedDs.setClassName(P6DataSource.class.getName());
            fixedDs.setUniqueName("jdbc/fixedDs");
            fixedDs.setMaxPoolSize(10);
            fixedDs.getDriverProperties().setProperty("realDataSource", "jdbc/realFixedDs");
            fixedDs.setAllowLocalTransactions(true);
            fixedDs.init();
            jndiResources.add(new Resource("jdbc/fixedDs", fixedDs));
            poolingDSs.add(fixedDs);
        }

        // liquibase opens it's own transaction => keep it out of ours
        try {
            P6TestUtil.setupTestData(new JndiDataSourceLookup().getDataSource("jdbc/inTestDs"));
            P6TestUtil.setupTestData(new JndiDataSourceLookup().getDataSource("jdbc/fixedDs"));
        } catch (LiquibaseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        try {
            tm.begin();
            // TODO move to liquibase?
            cleanData(new JndiDataSourceLookup().getDataSource("jdbc/inTestDs"));
            cleanData(new JndiDataSourceLookup().getDataSource("jdbc/fixedDs"));
            tm.commit();
        } catch (NotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SystemException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalStateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (HeuristicMixedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (HeuristicRollbackException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (RollbackException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @After
    public void tearDownXADataSourceTest() {
        ((BitronixTransactionManager) tm).shutdown();

        for (PoolingDataSource psd : poolingDSs) {
            try {
                psd.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        poolingDSs = null;

        for (Resource resource : jndiResources) {
            try {
                resource.release();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        jndiResources = null;
    }

    @Test
    public void twoPhaseCommitDataPersistedOnCommit() {
        try {
            tm.begin();
            insertData(new JndiDataSourceLookup().getDataSource("jdbc/inTestDs"));
            insertData(new JndiDataSourceLookup().getDataSource("jdbc/fixedDs"));
            tm.commit();

            tm.begin();
            assertEquals(1, queryForInt(new JndiDataSourceLookup().getDataSource("jdbc/inTestDs")));
            assertEquals(1, queryForInt(new JndiDataSourceLookup().getDataSource("jdbc/fixedDs")));
            tm.commit();

        } catch (NotSupportedException e) {
            e.printStackTrace();
            Assert.fail();
        } catch (SystemException e) {
            e.printStackTrace();
            Assert.fail();
        } catch (IllegalStateException e) {
            e.printStackTrace();
            Assert.fail();
        } catch (SecurityException e) {
            e.printStackTrace();
            Assert.fail();
        } catch (HeuristicMixedException e) {
            e.printStackTrace();
            Assert.fail();
        } catch (HeuristicRollbackException e) {
            e.printStackTrace();
            Assert.fail();
        } catch (RollbackException e) {
            e.printStackTrace();
            Assert.fail();
        }
    }

    @Test
    public void twoPhaseCommitDataNotPersistedOnRollback() {
        try {
            tm.begin();
            insertData(new JndiDataSourceLookup().getDataSource("jdbc/inTestDs"));
            insertData(new JndiDataSourceLookup().getDataSource("jdbc/fixedDs"));
            tm.rollback();

            tm.begin();
            assertEquals(0, queryForInt(new JndiDataSourceLookup().getDataSource("jdbc/inTestDs")));
            assertEquals(0, queryForInt(new JndiDataSourceLookup().getDataSource("jdbc/fixedDs")));
            tm.commit();

        } catch (NotSupportedException e) {
            e.printStackTrace();
            Assert.fail();
        } catch (SystemException e) {
            e.printStackTrace();
            Assert.fail();
        } catch (IllegalStateException e) {
            e.printStackTrace();
            Assert.fail();
        } catch (SecurityException e) {
            e.printStackTrace();
            Assert.fail();
        } catch (HeuristicMixedException e) {
            e.printStackTrace();
            Assert.fail();
        } catch (HeuristicRollbackException e) {
            e.printStackTrace();
            Assert.fail();
        } catch (RollbackException e) {
            e.printStackTrace();
            Assert.fail();
        }
    }

    private void cleanData(DataSource dataSource) {
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
            P6TestUtil.execute(connection, "delete from customers where id=50");
        } catch (SQLException e) {
            e.printStackTrace();
            Assert.fail();
        } finally {
            try {
                if (null != connection) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    private void insertData(DataSource ds) {
        Connection connection = null;
        try {
            connection = ds.getConnection();
            P6TestUtil.execute(connection, "insert into customers(id,name) values (50,'foo')");
        } catch (SQLException e) {
            e.printStackTrace();
            Assert.fail();
        } finally {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    private int queryForInt(DataSource ds) {
        Connection connection = null;
        try {
            connection = ds.getConnection();

            // add different data to each connection
            return P6TestUtil.queryForInt(connection, "select count(*) from customers where id=50");
        } catch (SQLException e) {
            e.printStackTrace();
            Assert.fail();
        } finally {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return -1;
    }

    public void setXADSProperties(XADataSource ds, String url, String userName, String password)
            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {

        if (url.contains(":derby:") || url.contains(":sqlfire:")) {
            PropertyUtils.setProperty(ds, "databaseName", "p6spy_xds");
            PropertyUtils.setProperty(ds, "createDatabase", "create");
            return;
        } else if (url.contains(":firebirdsql:")) {
            PropertyUtils.setProperty(ds, "databaseName", url.replace("jdbc:firebirdsql:", ""));
        } else if (PropertyUtils.isWriteable(ds, "URL")) {
            PropertyUtils.setProperty(ds, "URL", url);
        } else if (PropertyUtils.isWriteable(ds, "Url")) {
            PropertyUtils.setProperty(ds, "Url", url);
        } else if (PropertyUtils.isWriteable(ds, "url")) {
            PropertyUtils.setProperty(ds, "url", url);
        } else if (PropertyUtils.isWriteable(ds, "serverName") && PropertyUtils.isWriteable(ds, "portNumber")
                && PropertyUtils.isWriteable(ds, "databaseName") && URL_PATTERN.matcher(url).matches()) {

            final Matcher matcher = URL_PATTERN.matcher(url);
            if (!matcher.matches()) {
                throw new IllegalArgumentException("url in incorrect format: " + url);
            }
            final String host = matcher.group(2);
            final String port = matcher.group(3);
            final String db = matcher.group(4);

            PropertyUtils.setProperty(ds, "serverName", host);
            if (null != port && !port.isEmpty()) {
                PropertyUtils.setProperty(ds, "portNumber", Integer.parseInt(port));
            }
            PropertyUtils.setProperty(ds, "databaseName", db);
        } else {
            throw new IllegalArgumentException(
                    "Datasource imlpementation not supported by tests (yet) (for url setting): " + ds);
        }

        if (PropertyUtils.isWriteable(ds, "userName")) {
            PropertyUtils.setProperty(ds, "userName", userName);
        } else if (PropertyUtils.isWriteable(ds, "user")) {
            PropertyUtils.setProperty(ds, "user", userName);
        } else {
            throw new IllegalArgumentException(
                    "Datasource imlpementation not supported by tests (yet) (for username setting): " + ds);
        }

        if (PropertyUtils.isWriteable(ds, "password")) {
            PropertyUtils.setProperty(ds, "password", password);
        } else {
            throw new IllegalArgumentException(
                    "Datasource imlpementation not supported by tests (yet) (for password setting): " + ds);
        }
    }
}