/* * 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 WARRANTIESOR 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.aries.tx.control.itests; import static org.ops4j.pax.exam.CoreOptions.junitBundles; import static org.ops4j.pax.exam.CoreOptions.mavenBundle; import static org.ops4j.pax.exam.CoreOptions.options; import static org.ops4j.pax.exam.CoreOptions.systemProperty; import static org.ops4j.pax.exam.CoreOptions.when; import java.io.File; import java.io.IOException; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Properties; import javax.inject.Inject; import org.h2.Driver; import org.h2.tools.Server; import org.junit.After; import org.junit.Before; import org.junit.runner.RunWith; import org.ops4j.pax.exam.Configuration; import org.ops4j.pax.exam.CoreOptions; import org.ops4j.pax.exam.Option; import org.ops4j.pax.exam.junit.PaxExam; import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; import org.ops4j.pax.exam.spi.reactors.PerClass; import org.osgi.framework.BundleContext; import org.osgi.framework.Filter; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.jdbc.DataSourceFactory; import org.osgi.service.transaction.control.TransactionControl; import org.osgi.service.transaction.control.jdbc.JDBCConnectionProvider; import org.osgi.service.transaction.control.jdbc.JDBCConnectionProviderFactory; import org.osgi.util.tracker.ServiceTracker; import junit.framework.AssertionFailedError; @RunWith(PaxExam.class) @ExamReactorStrategy(PerClass.class) public abstract class AbstractTransactionTest { private static final String TX_CONTROL_FILTER = "org.apache.aries.tx.control.itests.filter"; private static final String REMOTE_DB_PROPERTY = "org.apache.aries.tx.control.itests.remotedb"; private static final String CONFIGURED_PROVIDER_PROPERTY = "org.apache.aries.tx.control.itests.configured"; @Inject BundleContext context; protected TransactionControl txControl; protected JDBCConnectionProvider provider; protected Connection connection; protected Server server; protected final List<ServiceTracker<?,?>> trackers = new ArrayList<>(); @Before public void setUp() throws Exception { txControl = getService(TransactionControl.class, System.getProperty(TX_CONTROL_FILTER), 5000); Properties jdbc = new Properties(); boolean external = System.getProperties().containsKey(REMOTE_DB_PROPERTY); String jdbcUrl; if(external) { server = Server.createTcpServer("-tcpPort", "0"); server.start(); jdbcUrl = "jdbc:h2:tcp://127.0.0.1:" + server.getPort() + "/" + System.getProperty(REMOTE_DB_PROPERTY); } else { jdbcUrl = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"; } initTestTable(jdbcUrl); jdbc.setProperty(DataSourceFactory.JDBC_URL, jdbcUrl); boolean configuredProvider = isConfigured(); connection = configuredProvider ? configuredConnection(jdbc) : programaticConnection(jdbc); } protected void initTestTable(String jdbcUrl) throws SQLException { Driver d = new Driver(); try (Connection c = d.connect(jdbcUrl, null)) { Statement s = c.createStatement(); try { s.execute("DROP TABLE TEST_TABLE"); } catch (SQLException sqle) {} s.execute("CREATE TABLE TEST_TABLE ( message varchar(255) )"); c.commit(); } } protected Map<String, Object> resourceProviderConfig() { // No extra information by default return new HashMap<>(); } public boolean isConfigured() { return System.getProperties().containsKey(CONFIGURED_PROVIDER_PROPERTY); } protected <T> T getService(Class<T> clazz, long timeout) { try { return getService(clazz, null, timeout); } catch (InvalidSyntaxException e) { throw new IllegalArgumentException(e); } } private <T> T getService(Class<T> clazz, String filter, long timeout) throws InvalidSyntaxException { Filter f = FrameworkUtil.createFilter(filter == null ? "(|(foo=bar)(!(foo=bar)))" : filter); ServiceTracker<T, T> tracker = new ServiceTracker<T, T>(context, clazz, null) { @Override public T addingService(ServiceReference<T> reference) { return f.match(reference) ? super.addingService(reference) : null; } }; tracker.open(); try { T t = tracker.waitForService(timeout); if(t == null) { throw new NoSuchElementException(clazz.getName()); } return t; } catch (InterruptedException e) { throw new RuntimeException("Error waiting for service " + clazz.getName(), e); } finally { trackers.add(tracker); } } private Connection programaticConnection(Properties jdbc) { JDBCConnectionProviderFactory resourceProviderFactory = getService(JDBCConnectionProviderFactory.class, 5000); DataSourceFactory dsf = getService(DataSourceFactory.class, 5000); provider = resourceProviderFactory.getProviderFor(dsf, jdbc, resourceProviderConfig()); return provider.getResource(txControl); } @SuppressWarnings({ "unchecked", "rawtypes" }) private Connection configuredConnection(Properties jdbc) throws IOException { String type = System.getProperty(CONFIGURED_PROVIDER_PROPERTY); jdbc.setProperty(DataSourceFactory.OSGI_JDBC_DRIVER_CLASS, "org.h2.Driver"); ConfigurationAdmin cm = getService(ConfigurationAdmin.class, 5000); String pid = "local".equals(type) ? "org.apache.aries.tx.control.jdbc.local" : "org.apache.aries.tx.control.jdbc.xa"; System.out.println("Configuring connection provider with pid " + pid); resourceProviderConfig().entrySet().stream() .forEach(e -> jdbc.put(e.getKey(), e.getValue())); org.osgi.service.cm.Configuration config = cm.createFactoryConfiguration( pid, "?"); config.update((Hashtable)jdbc); provider = getService(JDBCConnectionProvider.class, 5000); return provider.getResource(txControl); } @After public void tearDown() { if(isConfigured()) { clearConfiguration(); ServiceTracker<JDBCConnectionProvider, JDBCConnectionProvider> tracker = new ServiceTracker<>(context, JDBCConnectionProvider.class, null); tracker.open(); for(int i = 0;; i++) { if(i == 10) { throw new AssertionFailedError("The JDBCConnectionProvider was not unregistered"); } if(tracker.getService() == null) { break; } else { try { Thread.sleep(250); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } tracker.close(); } if(server != null) { server.stop(); } trackers.stream().forEach(ServiceTracker::close); connection = null; } private void clearConfiguration() { ConfigurationAdmin cm = getService(ConfigurationAdmin.class, 5000); org.osgi.service.cm.Configuration[] cfgs = null; try { cfgs = cm.listConfigurations(null); } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); } if(cfgs != null) { for(org.osgi.service.cm.Configuration cfg : cfgs) { try { cfg.delete(); } catch (Exception e) {} } try { Thread.sleep(250); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } @Configuration public Option[] localEmbeddedH2LocalTxConfiguration() { String localRepo = System.getProperty("maven.repo.local"); if (localRepo == null) { localRepo = System.getProperty("org.ops4j.pax.url.mvn.localRepository"); } Option testSpecificOptions = testSpecificOptions(); return options(junitBundles(), systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"), when(localRepo != null) .useOptions(CoreOptions.vmOption("-Dorg.ops4j.pax.url.mvn.localRepository=" + localRepo)), localTxControlService(), localJdbcResourceProviderWithH2(), when(testSpecificOptions != null).useOptions(testSpecificOptions), mavenBundle("org.ops4j.pax.logging", "pax-logging-api").versionAsInProject(), mavenBundle("org.ops4j.pax.logging", "pax-logging-service").versionAsInProject() // ,CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005") ); } @Configuration public Option[] localServerH2LocalTxConfiguration() { String localRepo = System.getProperty("maven.repo.local"); if (localRepo == null) { localRepo = System.getProperty("org.ops4j.pax.url.mvn.localRepository"); } Option testSpecificOptions = testSpecificOptions(); return options(junitBundles(), systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"), when(localRepo != null) .useOptions(CoreOptions.vmOption("-Dorg.ops4j.pax.url.mvn.localRepository=" + localRepo)), localTxControlService(), localJdbcResourceProviderWithH2(), systemProperty(REMOTE_DB_PROPERTY).value(getRemoteDBPath()), when(testSpecificOptions != null).useOptions(testSpecificOptions), mavenBundle("org.ops4j.pax.logging", "pax-logging-api").versionAsInProject(), mavenBundle("org.ops4j.pax.logging", "pax-logging-service").versionAsInProject() // ,CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005") ); } @Configuration public Option[] localConfigAdminDrivenH2LocalTxConfiguration() { String localRepo = System.getProperty("maven.repo.local"); if (localRepo == null) { localRepo = System.getProperty("org.ops4j.pax.url.mvn.localRepository"); } Option testSpecificOptions = testSpecificOptions(); return options(junitBundles(), systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"), when(localRepo != null) .useOptions(CoreOptions.vmOption("-Dorg.ops4j.pax.url.mvn.localRepository=" + localRepo)), localTxControlService(), localJdbcResourceProviderWithH2(), systemProperty(REMOTE_DB_PROPERTY).value(getRemoteDBPath()), mavenBundle("org.apache.felix", "org.apache.felix.configadmin").versionAsInProject(), systemProperty(CONFIGURED_PROVIDER_PROPERTY).value("local"), when(testSpecificOptions != null).useOptions(testSpecificOptions), mavenBundle("org.ops4j.pax.logging", "pax-logging-api").versionAsInProject(), mavenBundle("org.ops4j.pax.logging", "pax-logging-service").versionAsInProject() // ,CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005") ); } @Configuration public Option[] localEmbeddedH2XATxConfiguration() { String localRepo = System.getProperty("maven.repo.local"); if (localRepo == null) { localRepo = System.getProperty("org.ops4j.pax.url.mvn.localRepository"); } Option testSpecificOptions = testSpecificOptions(); return options(junitBundles(), systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"), when(localRepo != null) .useOptions(CoreOptions.vmOption("-Dorg.ops4j.pax.url.mvn.localRepository=" + localRepo)), xaTxControlService(), localJdbcResourceProviderWithH2(), when(testSpecificOptions != null).useOptions(testSpecificOptions), mavenBundle("org.ops4j.pax.logging", "pax-logging-api").versionAsInProject(), mavenBundle("org.ops4j.pax.logging", "pax-logging-service").versionAsInProject() // ,CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005") ); } @Configuration public Option[] localServerH2XATxConfiguration() { String localRepo = System.getProperty("maven.repo.local"); if (localRepo == null) { localRepo = System.getProperty("org.ops4j.pax.url.mvn.localRepository"); } Option testSpecificOptions = testSpecificOptions(); return options(junitBundles(), systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"), when(localRepo != null) .useOptions(CoreOptions.vmOption("-Dorg.ops4j.pax.url.mvn.localRepository=" + localRepo)), xaTxControlService(), localJdbcResourceProviderWithH2(), systemProperty(REMOTE_DB_PROPERTY).value(getRemoteDBPath()), when(testSpecificOptions != null).useOptions(testSpecificOptions), mavenBundle("org.ops4j.pax.logging", "pax-logging-api").versionAsInProject(), mavenBundle("org.ops4j.pax.logging", "pax-logging-service").versionAsInProject() // ,CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005") ); } @Configuration public Option[] localConfigAdminDrivenH2XATxConfiguration() { String localRepo = System.getProperty("maven.repo.local"); if (localRepo == null) { localRepo = System.getProperty("org.ops4j.pax.url.mvn.localRepository"); } Option testSpecificOptions = testSpecificOptions(); return options(junitBundles(), systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"), when(localRepo != null) .useOptions(CoreOptions.vmOption("-Dorg.ops4j.pax.url.mvn.localRepository=" + localRepo)), xaTxControlService(), localJdbcResourceProviderWithH2(), systemProperty(REMOTE_DB_PROPERTY).value(getRemoteDBPath()), mavenBundle("org.apache.felix", "org.apache.felix.configadmin").versionAsInProject(), systemProperty(CONFIGURED_PROVIDER_PROPERTY).value("local"), when(testSpecificOptions != null).useOptions(testSpecificOptions), mavenBundle("org.ops4j.pax.logging", "pax-logging-api").versionAsInProject(), mavenBundle("org.ops4j.pax.logging", "pax-logging-service").versionAsInProject() // ,CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005") ); } @Configuration public Option[] xaServerH2XATxConfiguration() { String localRepo = System.getProperty("maven.repo.local"); if (localRepo == null) { localRepo = System.getProperty("org.ops4j.pax.url.mvn.localRepository"); } Option testSpecificOptions = testSpecificOptions(); return options(junitBundles(), systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"), when(localRepo != null) .useOptions(CoreOptions.vmOption("-Dorg.ops4j.pax.url.mvn.localRepository=" + localRepo)), xaTxControlService(), xaJdbcResourceProviderWithH2(), systemProperty(REMOTE_DB_PROPERTY).value(getRemoteDBPath()), when(testSpecificOptions != null).useOptions(testSpecificOptions), mavenBundle("org.ops4j.pax.logging", "pax-logging-api").versionAsInProject(), mavenBundle("org.ops4j.pax.logging", "pax-logging-service").versionAsInProject() // ,CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005") ); } @Configuration public Option[] xaConfigAdminDrivenH2XATxConfiguration() { String localRepo = System.getProperty("maven.repo.local"); if (localRepo == null) { localRepo = System.getProperty("org.ops4j.pax.url.mvn.localRepository"); } Option testSpecificOptions = testSpecificOptions(); return options(junitBundles(), systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"), when(localRepo != null) .useOptions(CoreOptions.vmOption("-Dorg.ops4j.pax.url.mvn.localRepository=" + localRepo)), xaTxControlService(), xaJdbcResourceProviderWithH2(), systemProperty(REMOTE_DB_PROPERTY).value(getRemoteDBPath()), mavenBundle("org.apache.felix", "org.apache.felix.configadmin").versionAsInProject(), systemProperty(CONFIGURED_PROVIDER_PROPERTY).value("xa"), when(testSpecificOptions != null).useOptions(testSpecificOptions), mavenBundle("org.ops4j.pax.logging", "pax-logging-api").versionAsInProject(), mavenBundle("org.ops4j.pax.logging", "pax-logging-service").versionAsInProject() // ,CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005") ); } private String getRemoteDBPath() { String fullResourceName = getClass().getName().replace('.', '/') + ".class"; String resourcePath = getClass().getResource(getClass().getSimpleName() + ".class").getPath(); File testClassesDir = new File(resourcePath.substring(0, resourcePath.length() - fullResourceName.length())); String dbPath = new File(testClassesDir.getParentFile(), "testdb/db1").getAbsolutePath(); return dbPath; } public Option localTxControlService() { return CoreOptions.composite( systemProperty(TX_CONTROL_FILTER).value("(!(osgi.xa.enabled=*))"), mavenBundle("org.apache.aries.tx-control", "tx-control-service-local").versionAsInProject()); } public Option xaTxControlService() { return CoreOptions.composite( systemProperty(TX_CONTROL_FILTER).value("(osgi.xa.enabled=true)"), mavenBundle("org.apache.aries.tx-control", "tx-control-service-xa").versionAsInProject()); } public Option localJdbcResourceProviderWithH2() { return CoreOptions.composite( mavenBundle("com.h2database", "h2").versionAsInProject(), mavenBundle("org.apache.aries.tx-control", "tx-control-provider-jdbc-local").versionAsInProject()); } public Option xaJdbcResourceProviderWithH2() { return CoreOptions.composite( mavenBundle("com.h2database", "h2").versionAsInProject(), mavenBundle("org.apache.aries.tx-control", "tx-control-provider-jdbc-xa").versionAsInProject()); } protected Option testSpecificOptions() { return null; } }