Return-Path: Delivered-To: apmail-aries-commits-archive@www.apache.org Received: (qmail 4440 invoked from network); 27 Feb 2011 18:37:40 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 27 Feb 2011 18:37:40 -0000 Received: (qmail 17205 invoked by uid 500); 27 Feb 2011 18:37:40 -0000 Delivered-To: apmail-aries-commits-archive@aries.apache.org Received: (qmail 17111 invoked by uid 500); 27 Feb 2011 18:37:38 -0000 Mailing-List: contact commits-help@aries.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@aries.apache.org Delivered-To: mailing list commits@aries.apache.org Received: (qmail 17101 invoked by uid 99); 27 Feb 2011 18:37:38 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 27 Feb 2011 18:37:38 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 27 Feb 2011 18:37:35 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 23DEA2388A68; Sun, 27 Feb 2011 18:37:15 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1075109 [3/3] - in /aries/tags/transaction-0.1-incubating: ./ transaction-blueprint/ transaction-blueprint/src/ transaction-blueprint/src/main/ transaction-blueprint/src/main/java/ transaction-blueprint/src/main/java/org/ transaction-bluep... Date: Sun, 27 Feb 2011 18:37:14 -0000 To: commits@aries.apache.org From: zoe@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20110227183715.23DEA2388A68@eris.apache.org> Added: aries/tags/transaction-0.1-incubating/transaction-manager/src/main/java/org/apache/aries/transaction/Activator.java URL: http://svn.apache.org/viewvc/aries/tags/transaction-0.1-incubating/transaction-manager/src/main/java/org/apache/aries/transaction/Activator.java?rev=1075109&view=auto ============================================================================== --- aries/tags/transaction-0.1-incubating/transaction-manager/src/main/java/org/apache/aries/transaction/Activator.java (added) +++ aries/tags/transaction-0.1-incubating/transaction-manager/src/main/java/org/apache/aries/transaction/Activator.java Sun Feb 27 18:37:11 2011 @@ -0,0 +1,101 @@ +/* + * 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.aries.transaction; + +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.Map; +import java.util.Iterator; +import java.util.HashMap; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.service.cm.ManagedServiceFactory; +import org.osgi.service.cm.ConfigurationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + */ +public class Activator implements BundleActivator, ManagedServiceFactory { + + private static final Logger log = LoggerFactory.getLogger("org.apache.aries.transaction"); + + private BundleContext bundleContext; + private Map managers = new HashMap(); + + public void start(BundleContext bundleContext) throws Exception { + this.bundleContext = bundleContext; + Hashtable props = new Hashtable(); + props.put(Constants.SERVICE_PID, getName()); + bundleContext.registerService(ManagedServiceFactory.class.getName(), this, props); + + Hashtable ht = new Hashtable(); + updated("initial", ht); + } + + private void set(Hashtable ht, String key) { + String o = bundleContext.getProperty(key); + if (o == null) { + o = System.getenv(key.toUpperCase().replaceAll(".", "_")); + if (o == null) { + return; + } + } + ht.put(key, o); + } + + public void stop(BundleContext context) throws Exception { + for (Iterator w = managers.values().iterator(); w.hasNext();) { + try { + TransactionManagerService mgr = (TransactionManagerService) w.next(); + w.remove(); + mgr.close(); + } catch (Exception e) { + // Ignore + } + } + } + + public String getName() { + return "org.apache.aries.transaction"; + } + + public void updated(String pid, Dictionary properties) throws ConfigurationException { + deleted(pid); + TransactionManagerService mgr = new TransactionManagerService(pid, properties, bundleContext); + managers.put(pid, mgr); + try { + mgr.start(); + } catch (Exception e) { + log.error("Error starting transaction manager", e); + } + } + + public void deleted(String pid) { + TransactionManagerService mgr = (TransactionManagerService) managers.remove(pid); + if (mgr != null) { + try { + mgr.close(); + } catch (Exception e) { + log.error("Error stopping transaction manager", e); + } + } + } + +} \ No newline at end of file Added: aries/tags/transaction-0.1-incubating/transaction-manager/src/main/java/org/apache/aries/transaction/GeronimoPlatformTransactionManager.java URL: http://svn.apache.org/viewvc/aries/tags/transaction-0.1-incubating/transaction-manager/src/main/java/org/apache/aries/transaction/GeronimoPlatformTransactionManager.java?rev=1075109&view=auto ============================================================================== --- aries/tags/transaction-0.1-incubating/transaction-manager/src/main/java/org/apache/aries/transaction/GeronimoPlatformTransactionManager.java (added) +++ aries/tags/transaction-0.1-incubating/transaction-manager/src/main/java/org/apache/aries/transaction/GeronimoPlatformTransactionManager.java Sun Feb 27 18:37:11 2011 @@ -0,0 +1,170 @@ +/* + * 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.aries.transaction; + +import java.util.Map; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +import javax.transaction.Transaction; +import javax.transaction.Status; +import javax.transaction.SystemException; +import javax.transaction.xa.XAException; + +import org.apache.geronimo.transaction.manager.GeronimoTransactionManager; +import org.apache.geronimo.transaction.manager.TransactionLog; +import org.apache.geronimo.transaction.manager.XidFactory; +import org.apache.geronimo.transaction.manager.TransactionManagerMonitor; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionException; +import org.springframework.transaction.support.TransactionSynchronizationManager; +import org.springframework.transaction.support.TransactionSynchronization; +import org.springframework.transaction.jta.JtaTransactionManager; + +/** + */ +public class GeronimoPlatformTransactionManager extends GeronimoTransactionManager implements PlatformTransactionManager { + + private final PlatformTransactionManager platformTransactionManager; + private final Map suspendedResources = new ConcurrentHashMap(); + + public GeronimoPlatformTransactionManager() throws XAException { + platformTransactionManager = new JtaTransactionManager(this, this); + registerTransactionAssociationListener(); + } + + public GeronimoPlatformTransactionManager(int defaultTransactionTimeoutSeconds) throws XAException { + super(defaultTransactionTimeoutSeconds); + platformTransactionManager = new JtaTransactionManager(this, this); + registerTransactionAssociationListener(); + } + + public GeronimoPlatformTransactionManager(int defaultTransactionTimeoutSeconds, TransactionLog transactionLog) throws XAException { + super(defaultTransactionTimeoutSeconds, transactionLog); + platformTransactionManager = new JtaTransactionManager(this, this); + registerTransactionAssociationListener(); + } + + public GeronimoPlatformTransactionManager(int defaultTransactionTimeoutSeconds, XidFactory xidFactory, TransactionLog transactionLog) throws XAException { + super(defaultTransactionTimeoutSeconds, xidFactory, transactionLog); + platformTransactionManager = new JtaTransactionManager(this, this); + registerTransactionAssociationListener(); + } + + public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { + return platformTransactionManager.getTransaction(definition); + } + + public void commit(TransactionStatus status) throws TransactionException { + platformTransactionManager.commit(status); + } + + public void rollback(TransactionStatus status) throws TransactionException { + platformTransactionManager.rollback(status); + } + + protected void registerTransactionAssociationListener() { + addTransactionAssociationListener(new TransactionManagerMonitor() { + public void threadAssociated(Transaction transaction) { + try { + if (transaction.getStatus() == Status.STATUS_ACTIVE) { + SuspendedResourcesHolder holder = suspendedResources.remove(transaction); + if (holder != null && holder.getSuspendedSynchronizations() != null) { + TransactionSynchronizationManager.setActualTransactionActive(true); + TransactionSynchronizationManager.setCurrentTransactionReadOnly(holder.isReadOnly()); + TransactionSynchronizationManager.setCurrentTransactionName(holder.getName()); + TransactionSynchronizationManager.initSynchronization(); + for (Iterator it = holder.getSuspendedSynchronizations().iterator(); it.hasNext();) { + TransactionSynchronization synchronization = (TransactionSynchronization) it.next(); + synchronization.resume(); + TransactionSynchronizationManager.registerSynchronization(synchronization); + } + } + } + } catch (SystemException e) { + return; + } + } + public void threadUnassociated(Transaction transaction) { + try { + if (transaction.getStatus() == Status.STATUS_ACTIVE) { + if (TransactionSynchronizationManager.isSynchronizationActive()) { + List suspendedSynchronizations = TransactionSynchronizationManager.getSynchronizations(); + for (Iterator it = suspendedSynchronizations.iterator(); it.hasNext();) { + ((TransactionSynchronization) it.next()).suspend(); + } + TransactionSynchronizationManager.clearSynchronization(); + String name = TransactionSynchronizationManager.getCurrentTransactionName(); + TransactionSynchronizationManager.setCurrentTransactionName(null); + boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); + TransactionSynchronizationManager.setCurrentTransactionReadOnly(false); + TransactionSynchronizationManager.setActualTransactionActive(false); + SuspendedResourcesHolder holder = new SuspendedResourcesHolder(null, suspendedSynchronizations, name, readOnly); + suspendedResources.put(transaction, holder); + } + } + } catch (SystemException e) { + return; + } + } + }); + } + + /** + * Holder for suspended resources. + * Used internally by suspend and resume. + */ + private static class SuspendedResourcesHolder { + + private final Object suspendedResources; + + private final List suspendedSynchronizations; + + private final String name; + + private final boolean readOnly; + + public SuspendedResourcesHolder( + Object suspendedResources, List suspendedSynchronizations, String name, boolean readOnly) { + + this.suspendedResources = suspendedResources; + this.suspendedSynchronizations = suspendedSynchronizations; + this.name = name; + this.readOnly = readOnly; + } + + public Object getSuspendedResources() { + return suspendedResources; + } + + public List getSuspendedSynchronizations() { + return suspendedSynchronizations; + } + + public String getName() { + return name; + } + + public boolean isReadOnly() { + return readOnly; + } + } + +} Added: aries/tags/transaction-0.1-incubating/transaction-manager/src/main/java/org/apache/aries/transaction/TransactionManagerService.java URL: http://svn.apache.org/viewvc/aries/tags/transaction-0.1-incubating/transaction-manager/src/main/java/org/apache/aries/transaction/TransactionManagerService.java?rev=1075109&view=auto ============================================================================== --- aries/tags/transaction-0.1-incubating/transaction-manager/src/main/java/org/apache/aries/transaction/TransactionManagerService.java (added) +++ aries/tags/transaction-0.1-incubating/transaction-manager/src/main/java/org/apache/aries/transaction/TransactionManagerService.java Sun Feb 27 18:37:11 2011 @@ -0,0 +1,220 @@ +/* + * 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.aries.transaction; + +import java.util.Dictionary; +import java.util.List; +import java.util.ArrayList; +import java.util.Properties; +import java.io.File; +import java.io.IOException; + +import javax.transaction.xa.XAException; +import javax.transaction.TransactionManager; +import javax.transaction.TransactionSynchronizationRegistry; +import javax.transaction.UserTransaction; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.cm.ConfigurationException; +import org.apache.geronimo.transaction.manager.GeronimoTransactionManager; +import org.apache.geronimo.transaction.manager.TransactionLog; +import org.apache.geronimo.transaction.manager.XidFactory; +import org.apache.geronimo.transaction.manager.XidFactoryImpl; +import org.apache.geronimo.transaction.manager.RecoverableTransactionManager; +import org.apache.geronimo.transaction.log.HOWLLog; +import org.apache.geronimo.transaction.log.UnrecoverableLog; +import org.objectweb.howl.log.LogConfigurationException; + +/** + */ +public class TransactionManagerService { + + public static final String TRANSACTION_TIMEOUT = "aries.transaction.timeout"; + public static final String RECOVERABLE = "aries.transaction.recoverable"; + public static final String HOWL_BUFFER_CLASS_NAME = "aries.transaction.howl.bufferClassName"; + public static final String HOWL_BUFFER_SIZE = "aries.transaction.howl.bufferSize"; + public static final String HOWL_CHECKSUM_ENABLED = "aries.transaction.howl.checksumEnabled"; + public static final String HOWL_ADLER32_CHECKSUM = "aries.transaction.howl.adler32Checksum"; + public static final String HOWL_FLUSH_SLEEP_TIME = "aries.transaction.howl.flushSleepTime"; + public static final String HOWL_LOG_FILE_EXT = "aries.transaction.howl.logFileExt"; + public static final String HOWL_LOG_FILE_NAME = "aries.transaction.howl.logFileName"; + public static final String HOWL_MAX_BLOCKS_PER_FILE = "aries.transaction.howl.maxBlocksPerFile"; + public static final String HOWL_MAX_LOG_FILES = "aries.transaction.howl.maxLogFiles"; + public static final String HOWL_MAX_BUFFERS = "aries.transaction.howl.maxBuffers"; + public static final String HOWL_MIN_BUFFERS = "aries.transaction.howl.minBuffers"; + public static final String HOWL_THREADS_WAITING_FORCE_THRESHOLD = "aries.transaction.howl.threadsWaitingForceThreshold"; + public static final String HOWL_LOG_FILE_DIR = "aries.transaction.howl.logFileDir"; + + public static final int DEFAULT_TRANSACTION_TIMEOUT = 600; // 600 seconds -> 10 minutes + public static final boolean DEFAULT_RECOVERABLE = false; // not recoverable by default + + private static final String PLATFORM_TRANSACTION_MANAGER_CLASS = "org.springframework.transaction.PlatformTransactionManager"; + + private final String pid; + private final Dictionary properties; + private final BundleContext bundleContext; + private boolean useSpring; + private GeronimoTransactionManager transactionManager; + private TransactionLog transactionLog; + private ServiceRegistration serviceRegistration; + + public TransactionManagerService(String pid, Dictionary properties, BundleContext bundleContext) throws ConfigurationException { + this.pid = pid; + this.properties = properties; + this.bundleContext = bundleContext; + // Transaction timeout + int transactionTimeout = getInt(TRANSACTION_TIMEOUT, DEFAULT_TRANSACTION_TIMEOUT); + if (transactionTimeout <= 0) { + throw new ConfigurationException(TRANSACTION_TIMEOUT, "Property " + TRANSACTION_TIMEOUT + " must be > 0"); + } + // XID factory + XidFactory xidFactory = new XidFactoryImpl(pid.getBytes()); + // Transaction log + if (getBool(RECOVERABLE, DEFAULT_RECOVERABLE)) { + String bufferClassName = getString(HOWL_BUFFER_CLASS_NAME, "org.objectweb.howl.log.BlockLogBuffer"); + int bufferSizeKBytes = getInt(HOWL_BUFFER_SIZE, 32); + if (bufferSizeKBytes < 1 || bufferSizeKBytes > 32) { + throw new ConfigurationException(HOWL_BUFFER_SIZE, "bufferSize must be between 1 and 32"); + } + boolean checksumEnabled = getBool(HOWL_CHECKSUM_ENABLED, true); + boolean adler32Checksum = getBool(HOWL_ADLER32_CHECKSUM, true); + int flushSleepTimeMilliseconds = getInt(HOWL_FLUSH_SLEEP_TIME, 50); + String logFileExt = getString(HOWL_LOG_FILE_EXT, "log"); + String logFileName = getString(HOWL_LOG_FILE_NAME, "transaction"); + int maxBlocksPerFile = getInt(HOWL_MAX_BLOCKS_PER_FILE, -1); + int maxLogFiles = getInt(HOWL_MAX_LOG_FILES, 2); + int minBuffers = getInt(HOWL_MIN_BUFFERS, 4); + if (minBuffers < 0) { + throw new ConfigurationException(HOWL_MIN_BUFFERS, "minBuffers must be > 0"); + } + int maxBuffers = getInt(HOWL_MAX_BUFFERS, 0); + if (maxBuffers > 0 && minBuffers < maxBuffers) { + throw new ConfigurationException(HOWL_MAX_BUFFERS, "minBuffers must be <= maxBuffers"); + } + int threadsWaitingForceThreshold = getInt(HOWL_THREADS_WAITING_FORCE_THRESHOLD, -1); + String logFileDir = getString(HOWL_LOG_FILE_DIR, null); + if (logFileDir == null || logFileDir.length() == 0 || !new File(logFileDir).isAbsolute()) { + throw new ConfigurationException(HOWL_LOG_FILE_DIR, "Property should be set to an absolute directory"); + } + try { + transactionLog = new HOWLLog(bufferClassName, + bufferSizeKBytes, + checksumEnabled, + adler32Checksum, + flushSleepTimeMilliseconds, + logFileDir, + logFileExt, + logFileName, + maxBlocksPerFile, + maxBuffers, + maxLogFiles, + minBuffers, + threadsWaitingForceThreshold, + xidFactory != null ? xidFactory : new XidFactoryImpl(), + null); + } catch (LogConfigurationException e) { + // This should not really happen as we've checked properties earlier + throw new ConfigurationException(null, null, e); + } catch (IOException e) { + // This should not really happen as we've checked properties earlier + throw new ConfigurationException(null, null, e); + } + } else { + transactionLog = new UnrecoverableLog(); + } + // Create transaction manager + try { + try { + transactionManager = new SpringTransactionManagerCreator().create(transactionTimeout, xidFactory, transactionLog); + useSpring = true; + } catch (NoClassDefFoundError e) { + transactionManager = new GeronimoTransactionManager(transactionTimeout, xidFactory, transactionLog); + } + } catch (XAException e) { + throw new RuntimeException("Error recovering transaction log", e); + } + } + + public void start() throws Exception { + if (transactionLog instanceof HOWLLog) { + ((HOWLLog) transactionLog).doStart(); + } + List clazzes = new ArrayList(); + clazzes.add(TransactionManager.class.getName()); + clazzes.add(TransactionSynchronizationRegistry.class.getName()); + clazzes.add(UserTransaction.class.getName()); + clazzes.add(RecoverableTransactionManager.class.getName()); + if (useSpring) { + clazzes.add(PLATFORM_TRANSACTION_MANAGER_CLASS); + } + serviceRegistration = bundleContext.registerService(clazzes.toArray(new String[clazzes.size()]), transactionManager, new Properties()); + } + + public void close() throws Exception { + if (serviceRegistration != null) { + serviceRegistration.unregister(); + } + if (transactionLog instanceof HOWLLog) { + ((HOWLLog) transactionLog).doStop(); + } + } + + private String getString(String property, String dflt) throws ConfigurationException { + String value = (String) properties.get(property); + if (value != null) { + return value; + } + return dflt; + } + + private int getInt(String property, int dflt) throws ConfigurationException { + String value = (String) properties.get(property); + if (value != null) { + try { + return Integer.parseInt(value); + } catch (Exception e) { + throw new ConfigurationException(property, "Error parsing " + property + "(" + value + ") property as an integer", e); + } + } + return dflt; + } + + private boolean getBool(String property, boolean dflt) throws ConfigurationException { + String value = (String) properties.get(property); + if (value != null) { + try { + return Boolean.parseBoolean(value); + } catch (Exception e) { + throw new ConfigurationException(property, "Error parsing " + property + "(" + value + ") property as a boolean", e); + } + } + return dflt; + } + + /** + * We use an inner static class to decouple this class from the spring-tx classes + * in order to not have NoClassDefFoundError if those are not present. + */ + public static class SpringTransactionManagerCreator { + + public GeronimoTransactionManager create(int defaultTransactionTimeoutSeconds, XidFactory xidFactory, TransactionLog transactionLog) throws XAException { + return new GeronimoPlatformTransactionManager(defaultTransactionTimeoutSeconds, xidFactory, transactionLog); + } + + } +} Added: aries/tags/transaction-0.1-incubating/transaction-testbundle/pom.xml URL: http://svn.apache.org/viewvc/aries/tags/transaction-0.1-incubating/transaction-testbundle/pom.xml?rev=1075109&view=auto ============================================================================== --- aries/tags/transaction-0.1-incubating/transaction-testbundle/pom.xml (added) +++ aries/tags/transaction-0.1-incubating/transaction-testbundle/pom.xml Sun Feb 27 18:37:11 2011 @@ -0,0 +1,41 @@ + + + + 4.0.0 + + org.apache.aries.transaction + transaction + 0.1-incubating + + org.apache.aries.transaction + org.apache.aries.transaction.testbundle + bundle + Apache Aries Transaction Test Bundle + + + + org.apache.aries.transaction.test + + + org.apache.aries.transaction.test.impl + + + + Added: aries/tags/transaction-0.1-incubating/transaction-testbundle/src/main/java/org/apache/aries/transaction/test/TestBean.java URL: http://svn.apache.org/viewvc/aries/tags/transaction-0.1-incubating/transaction-testbundle/src/main/java/org/apache/aries/transaction/test/TestBean.java?rev=1075109&view=auto ============================================================================== --- aries/tags/transaction-0.1-incubating/transaction-testbundle/src/main/java/org/apache/aries/transaction/test/TestBean.java (added) +++ aries/tags/transaction-0.1-incubating/transaction-testbundle/src/main/java/org/apache/aries/transaction/test/TestBean.java Sun Feb 27 18:37:11 2011 @@ -0,0 +1,39 @@ +/* + * 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.aries.transaction.test; + +import java.sql.SQLException; + +/** + * This interface is used by the transactions integration tests + * + */ +public interface TestBean { + public void insertRow(String name, int value) throws SQLException; + + public void insertRow(String name, int value, boolean delegate) throws SQLException; + + public void insertRow(String name, int value, Exception e) throws SQLException; + + public int countRows() throws SQLException; + + public void throwApplicationException() throws SQLException; + + public void throwRuntimeException(); +} Added: aries/tags/transaction-0.1-incubating/transaction-testbundle/src/main/java/org/apache/aries/transaction/test/impl/TestBeanImpl.java URL: http://svn.apache.org/viewvc/aries/tags/transaction-0.1-incubating/transaction-testbundle/src/main/java/org/apache/aries/transaction/test/impl/TestBeanImpl.java?rev=1075109&view=auto ============================================================================== --- aries/tags/transaction-0.1-incubating/transaction-testbundle/src/main/java/org/apache/aries/transaction/test/impl/TestBeanImpl.java (added) +++ aries/tags/transaction-0.1-incubating/transaction-testbundle/src/main/java/org/apache/aries/transaction/test/impl/TestBeanImpl.java Sun Feb 27 18:37:11 2011 @@ -0,0 +1,196 @@ +/* + * 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.aries.transaction.test.impl; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import javax.sql.DataSource; + +import org.apache.aries.transaction.test.TestBean; + +public class TestBeanImpl implements TestBean { + private DataSource xads; + + private DataSource ds; + + private String user; + + private String password; + + private TestBean bean; + + public TestBeanImpl() { + } + + public void initialize() { + Connection conn = null; + Statement stmt = null; + + try { + conn = ds.getConnection(user, password); + conn.setAutoCommit(true); + stmt = conn.createStatement(); + stmt.executeUpdate("DROP TABLE TESTTABLE"); + } + catch (Exception e) { + e.printStackTrace(); + } + finally { + try { + if (stmt != null) + stmt.close(); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + try { + if (conn != null) + conn.close(); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + try { + conn = ds.getConnection(user, password); + conn.setAutoCommit(true); + stmt = conn.createStatement(); + stmt.executeUpdate("CREATE TABLE TESTTABLE (NAME VARCHAR(64), VALUE INTEGER, PRIMARY KEY(NAME, VALUE))"); + } + catch (Exception e) { + e.printStackTrace(); + } + finally { + try { + if (stmt != null) + stmt.close(); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + try { + if (conn != null) + conn.close(); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + public void insertRow(String name, int value) throws SQLException { + insertRow(name, value, false); + } + + public void insertRow(String name, int value, Exception e) throws SQLException { + insertRow(name, value, false); + + if (e instanceof SQLException) + throw (SQLException) e; + else if (e instanceof RuntimeException) + throw (RuntimeException) e; + } + + public void insertRow(String name, int value, boolean delegate) throws SQLException { + if (delegate) { + bean.insertRow(name, value); + } + else { + Connection conn = null; + PreparedStatement stmt = null; + + try { + conn = xads.getConnection(user, password); + stmt = conn.prepareStatement("INSERT INTO TESTTABLE VALUES (?, ?)"); + stmt.setString(1, name); + stmt.setInt(2, value); + stmt.executeUpdate(); + } + finally { + if (stmt != null) + stmt.close(); + + if (conn != null) + conn.close(); + } + } + } + + public int countRows() throws SQLException { + Connection conn = null; + PreparedStatement stmt = null; + ResultSet rs = null; + int count = -1; + + try { + conn = ds.getConnection(user, password); + stmt = conn.prepareStatement("SELECT * FROM TESTTABLE", ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + rs = stmt.executeQuery(); + rs.last(); + count = rs.getRow(); + } + finally { + if (rs != null) + rs.close(); + + if (stmt != null) + stmt.close(); + + if (conn != null) + conn.close(); + } + + return count; + } + + public void throwApplicationException() throws SQLException { + throw new SQLException("Test exception"); + } + + public void throwRuntimeException() { + throw new RuntimeException("Test exception"); + } + + public void setEnlistingDataSource(DataSource xads) { + this.xads = xads; + } + + public void setDataSource(DataSource ds) { + this.ds = ds; + } + + public void setUser(String user) { + this.user = user; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setTestBean(TestBean bean) { + this.bean = bean; + } +} Added: aries/tags/transaction-0.1-incubating/transaction-testbundle/src/main/resources/OSGI-INF/blueprint/config.xml URL: http://svn.apache.org/viewvc/aries/tags/transaction-0.1-incubating/transaction-testbundle/src/main/resources/OSGI-INF/blueprint/config.xml?rev=1075109&view=auto ============================================================================== --- aries/tags/transaction-0.1-incubating/transaction-testbundle/src/main/resources/OSGI-INF/blueprint/config.xml (added) +++ aries/tags/transaction-0.1-incubating/transaction-testbundle/src/main/resources/OSGI-INF/blueprint/config.xml Sun Feb 27 18:37:11 2011 @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Added: aries/tags/transaction-0.1-incubating/transaction-testds/pom.xml URL: http://svn.apache.org/viewvc/aries/tags/transaction-0.1-incubating/transaction-testds/pom.xml?rev=1075109&view=auto ============================================================================== --- aries/tags/transaction-0.1-incubating/transaction-testds/pom.xml (added) +++ aries/tags/transaction-0.1-incubating/transaction-testds/pom.xml Sun Feb 27 18:37:11 2011 @@ -0,0 +1,34 @@ + + + + 4.0.0 + + org.apache.aries.transaction + transaction + 0.1-incubating + + org.apache.aries.transaction + org.apache.aries.transaction.testds + Apache Aries Transaction Test Datasource Configuration + bundle + + + org.apache.derby + derby + + + Added: aries/tags/transaction-0.1-incubating/transaction-testds/src/main/resources/OSGI-INF/blueprint/dataSource.xml URL: http://svn.apache.org/viewvc/aries/tags/transaction-0.1-incubating/transaction-testds/src/main/resources/OSGI-INF/blueprint/dataSource.xml?rev=1075109&view=auto ============================================================================== --- aries/tags/transaction-0.1-incubating/transaction-testds/src/main/resources/OSGI-INF/blueprint/dataSource.xml (added) +++ aries/tags/transaction-0.1-incubating/transaction-testds/src/main/resources/OSGI-INF/blueprint/dataSource.xml Sun Feb 27 18:37:11 2011 @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file Added: aries/tags/transaction-0.1-incubating/transaction-wrappers/pom.xml URL: http://svn.apache.org/viewvc/aries/tags/transaction-0.1-incubating/transaction-wrappers/pom.xml?rev=1075109&view=auto ============================================================================== --- aries/tags/transaction-0.1-incubating/transaction-wrappers/pom.xml (added) +++ aries/tags/transaction-0.1-incubating/transaction-wrappers/pom.xml Sun Feb 27 18:37:11 2011 @@ -0,0 +1,61 @@ + + + + 4.0.0 + + org.apache.aries.transaction + transaction + 0.1-incubating + + org.apache.aries.transaction + org.apache.aries.transaction.wrappers + Apache Aries Transaction Enlisting JDBC Datasource + bundle + + + + !org.apache.aries.transaction.jdbc + + + org.apache.aries.transaction.jdbc + + + org.apache.aries.transaction.jdbc.Activator + + + + + + org.apache.aries.transaction + org.apache.aries.transaction.manager + + + org.osgi + org.osgi.core + provided + + + org.osgi + org.osgi.compendium + provided + + + + Added: aries/tags/transaction-0.1-incubating/transaction-wrappers/src/main/java/org/apache/aries/transaction/jdbc/Activator.java URL: http://svn.apache.org/viewvc/aries/tags/transaction-0.1-incubating/transaction-wrappers/src/main/java/org/apache/aries/transaction/jdbc/Activator.java?rev=1075109&view=auto ============================================================================== --- aries/tags/transaction-0.1-incubating/transaction-wrappers/src/main/java/org/apache/aries/transaction/jdbc/Activator.java (added) +++ aries/tags/transaction-0.1-incubating/transaction-wrappers/src/main/java/org/apache/aries/transaction/jdbc/Activator.java Sun Feb 27 18:37:11 2011 @@ -0,0 +1,127 @@ +/* + * 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.aries.transaction.jdbc; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceEvent; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; +import java.util.Hashtable; +import javax.sql.DataSource; +import javax.sql.XADataSource; +import javax.transaction.TransactionManager; + +public class Activator implements BundleActivator, ServiceTrackerCustomizer, ServiceListener +{ + private TransactionManager tm; + private ServiceTracker t; + private ServiceReference ref; + private BundleContext context; + + public void start(BundleContext ctx) + { + context = ctx; + + t = new ServiceTracker(ctx, javax.sql.XADataSource.class.getName(), this); + + try { + ctx.addServiceListener(this, "(objectClass=javax.transaction.TransactionManager)"); + } catch (InvalidSyntaxException e) { + } + ref = ctx.getServiceReference(TransactionManager.class.getName()); + if (ref != null) { + tm = (TransactionManager) ctx.getService(ref); + } + + if (tm != null) { + t.open(); + } + } + + public void stop(BundleContext ctx) + { + // it is possible these are not cleaned by serviceChanged method when the + // tm service is still active + if (t != null) { + t.close(); + } + if (ref != null) { + context.ungetService(ref); + } + } + + public Object addingService(ServiceReference ref) + { + BundleContext ctx = ref.getBundle().getBundleContext(); + + Hashtable map = new Hashtable(); + for (String key : ref.getPropertyKeys()) { + map.put(key, ref.getProperty(key)); + } + map.put("aries.xa.aware", "true"); + + XADatasourceEnlistingWrapper wrapper = new XADatasourceEnlistingWrapper(); + wrapper.setTxManager(tm); + wrapper.setDataSource((XADataSource) ctx.getService(ref)); + + ServiceRegistration reg = ctx.registerService(DataSource.class.getName(), wrapper, map); + + return reg; + } + + public void modifiedService(ServiceReference ref, Object service) + { + ServiceRegistration reg = (ServiceRegistration) service; + + Hashtable map = new Hashtable(); + for (String key : ref.getPropertyKeys()) { + map.put(key, ref.getProperty(key)); + } + map.put("aries.xa.aware", "true"); + + reg.setProperties(map); + } + + public void removedService(ServiceReference ref, Object service) + { + ((ServiceRegistration)service).unregister(); + } + + public void serviceChanged(ServiceEvent event) + { + if (event.getType() == ServiceEvent.REGISTERED && tm == null) { + ref = event.getServiceReference(); + tm = (TransactionManager) context.getService(ref); + + if (tm == null) ref = null; + else t.open(); + } else if (event.getType() == ServiceEvent.UNREGISTERING && tm != null && + ref.getProperty("service.id").equals(event.getServiceReference().getProperty("service.id"))) { + t.close(); + context.ungetService(ref); + ref = null; + tm = null; + } + } +} Added: aries/tags/transaction-0.1-incubating/transaction-wrappers/src/main/java/org/apache/aries/transaction/jdbc/ConnectionWrapper.java URL: http://svn.apache.org/viewvc/aries/tags/transaction-0.1-incubating/transaction-wrappers/src/main/java/org/apache/aries/transaction/jdbc/ConnectionWrapper.java?rev=1075109&view=auto ============================================================================== --- aries/tags/transaction-0.1-incubating/transaction-wrappers/src/main/java/org/apache/aries/transaction/jdbc/ConnectionWrapper.java (added) +++ aries/tags/transaction-0.1-incubating/transaction-wrappers/src/main/java/org/apache/aries/transaction/jdbc/ConnectionWrapper.java Sun Feb 27 18:37:11 2011 @@ -0,0 +1,312 @@ +/* + * 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.aries.transaction.jdbc; + +import java.sql.Array; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.NClob; +import java.sql.PreparedStatement; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Savepoint; +import java.sql.Statement; +import java.sql.Struct; +import java.util.Map; +import java.util.Properties; + +import javax.sql.XAConnection; +import javax.transaction.Status; +import javax.transaction.SystemException; +import javax.transaction.Transaction; +import javax.transaction.TransactionManager; +import javax.transaction.xa.XAResource; + +/** + * This class is a wrapper around a {@link Connection} that performs + * enlistment/delistment of an {@link XAResource} from a transaction. + * + * @see XADatasourceEnlistingWrapper + */ +public class ConnectionWrapper implements Connection { + private Connection connection; + + private XAConnection xaConnection; + + private TransactionManager tm; + + public ConnectionWrapper(XAConnection xaConnection, TransactionManager tm) { + try { + this.xaConnection = xaConnection; + this.tm = tm; + this.connection = xaConnection.getConnection(); + + if (tm.getStatus() == Status.STATUS_ACTIVE) { + Transaction tx = tm.getTransaction(); + tx.enlistResource(xaConnection.getXAResource()); + } + } catch (Exception e) { + try { + if (tm != null) + tm.setRollbackOnly(); + } catch (IllegalStateException e1) { + e1.printStackTrace(); + } catch (SystemException e1) { + e1.printStackTrace(); + } + } + } + + public void clearWarnings() throws SQLException { + connection.clearWarnings(); + } + + public void close() throws SQLException { + try { + if (tm.getStatus() == Status.STATUS_ACTIVE) { + Transaction tx = tm.getTransaction(); + tx.delistResource(xaConnection.getXAResource(), XAResource.TMSUCCESS); + } + } catch (Exception e) { + try { + if (tm != null) + tm.setRollbackOnly(); + } catch (IllegalStateException e1) { + e1.printStackTrace(); + } catch (SystemException e1) { + e1.printStackTrace(); + } + } + + connection.close(); + } + + public void commit() throws SQLException { + connection.commit(); + } + + public Array createArrayOf(String typeName, Object[] elements) + throws SQLException { + return connection.createArrayOf(typeName, elements); + } + + public Blob createBlob() throws SQLException { + return connection.createBlob(); + } + + public Clob createClob() throws SQLException { + return connection.createClob(); + } + + public NClob createNClob() throws SQLException { + return connection.createNClob(); + } + + public SQLXML createSQLXML() throws SQLException { + return connection.createSQLXML(); + } + + public Statement createStatement() throws SQLException { + return connection.createStatement(); + } + + public Statement createStatement(int resultSetType, + int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + return connection.createStatement(resultSetType, resultSetConcurrency, + resultSetHoldability); + } + + public Statement createStatement(int resultSetType, int resultSetConcurrency) + throws SQLException { + return connection.createStatement(resultSetType, resultSetConcurrency); + } + + public Struct createStruct(String typeName, Object[] attributes) + throws SQLException { + return connection.createStruct(typeName, attributes); + } + + public boolean getAutoCommit() throws SQLException { + return connection.getAutoCommit(); + } + + public String getCatalog() throws SQLException { + return connection.getCatalog(); + } + + public Properties getClientInfo() throws SQLException { + return connection.getClientInfo(); + } + + public String getClientInfo(String name) throws SQLException { + return connection.getClientInfo(name); + } + + public int getHoldability() throws SQLException { + return connection.getHoldability(); + } + + public DatabaseMetaData getMetaData() throws SQLException { + return connection.getMetaData(); + } + + public int getTransactionIsolation() throws SQLException { + return connection.getTransactionIsolation(); + } + + public Map> getTypeMap() throws SQLException { + return connection.getTypeMap(); + } + + public SQLWarning getWarnings() throws SQLException { + return connection.getWarnings(); + } + + public boolean isClosed() throws SQLException { + return connection.isClosed(); + } + + public boolean isReadOnly() throws SQLException { + return connection.isReadOnly(); + } + + public boolean isValid(int timeout) throws SQLException { + return connection.isValid(timeout); + } + + public boolean isWrapperFor(Class iface) throws SQLException { + return connection.isWrapperFor(iface); + } + + public String nativeSQL(String sql) throws SQLException { + return connection.nativeSQL(sql); + } + + public CallableStatement prepareCall(String sql, int resultSetType, + int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + return connection.prepareCall(sql, resultSetType, resultSetConcurrency, + resultSetHoldability); + } + + public CallableStatement prepareCall(String sql, int resultSetType, + int resultSetConcurrency) throws SQLException { + return connection.prepareCall(sql, resultSetType, resultSetConcurrency); + } + + public CallableStatement prepareCall(String sql) throws SQLException { + return connection.prepareCall(sql); + } + + public PreparedStatement prepareStatement(String sql, int resultSetType, + int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + return connection.prepareStatement(sql, resultSetType, + resultSetConcurrency, resultSetHoldability); + } + + public PreparedStatement prepareStatement(String sql, int resultSetType, + int resultSetConcurrency) throws SQLException { + return connection.prepareStatement(sql, resultSetType, + resultSetConcurrency); + } + + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) + throws SQLException { + return connection.prepareStatement(sql, autoGeneratedKeys); + } + + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) + throws SQLException { + return connection.prepareStatement(sql, columnIndexes); + } + + public PreparedStatement prepareStatement(String sql, String[] columnNames) + throws SQLException { + return connection.prepareStatement(sql, columnNames); + } + + public PreparedStatement prepareStatement(String sql) throws SQLException { + return connection.prepareStatement(sql); + } + + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + connection.releaseSavepoint(savepoint); + } + + public void rollback() throws SQLException { + connection.rollback(); + } + + public void rollback(Savepoint savepoint) throws SQLException { + connection.rollback(savepoint); + } + + public void setAutoCommit(boolean autoCommit) throws SQLException { + connection.setAutoCommit(autoCommit); + } + + public void setCatalog(String catalog) throws SQLException { + connection.setCatalog(catalog); + } + + public void setClientInfo(Properties properties) + throws SQLClientInfoException { + connection.setClientInfo(properties); + } + + public void setClientInfo(String name, String value) + throws SQLClientInfoException { + connection.setClientInfo(name, value); + } + + public void setHoldability(int holdability) throws SQLException { + connection.setHoldability(holdability); + } + + public void setReadOnly(boolean readOnly) throws SQLException { + connection.setReadOnly(readOnly); + } + + public Savepoint setSavepoint() throws SQLException { + return connection.setSavepoint(); + } + + public Savepoint setSavepoint(String name) throws SQLException { + return connection.setSavepoint(name); + } + + public void setTransactionIsolation(int level) throws SQLException { + connection.setTransactionIsolation(level); + } + + public void setTypeMap(Map> map) throws SQLException { + connection.setTypeMap(map); + } + + public T unwrap(Class iface) throws SQLException { + return connection.unwrap(iface); + } +} Added: aries/tags/transaction-0.1-incubating/transaction-wrappers/src/main/java/org/apache/aries/transaction/jdbc/XADatasourceEnlistingWrapper.java URL: http://svn.apache.org/viewvc/aries/tags/transaction-0.1-incubating/transaction-wrappers/src/main/java/org/apache/aries/transaction/jdbc/XADatasourceEnlistingWrapper.java?rev=1075109&view=auto ============================================================================== --- aries/tags/transaction-0.1-incubating/transaction-wrappers/src/main/java/org/apache/aries/transaction/jdbc/XADatasourceEnlistingWrapper.java (added) +++ aries/tags/transaction-0.1-incubating/transaction-wrappers/src/main/java/org/apache/aries/transaction/jdbc/XADatasourceEnlistingWrapper.java Sun Feb 27 18:37:11 2011 @@ -0,0 +1,127 @@ +/* + * 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.aries.transaction.jdbc; + +import java.io.PrintWriter; +import java.io.Serializable; +import java.sql.Connection; +import java.sql.SQLException; + +import javax.sql.DataSource; +import javax.sql.XAConnection; +import javax.sql.XADataSource; +import javax.transaction.TransactionManager; + +/** + * This class allows JDBC XA data sources to participate in global transactions, + * via the {@link ConnectionWrapper} that is returned. The only service provided + * is enlistment/delistment of the associated {@link XAResource} in transactions. + * Important consideration such as connection pooling and error handling are + * completely ignored. + * + */ +public class XADatasourceEnlistingWrapper implements DataSource, Serializable { + /** The serial version UID */ + private static final long serialVersionUID = -3200389791205501228L; + + private XADataSource wrappedDS; + + private transient TransactionManager tm; + + public Connection getConnection() throws SQLException + { + XAConnection xaConn = wrappedDS.getXAConnection(); + Connection conn = getEnlistedConnection(xaConn); + + return conn; + } + + public Connection getConnection(String username, String password) throws SQLException + { + XAConnection xaConn = wrappedDS.getXAConnection(username, password); + Connection conn = getEnlistedConnection(xaConn); + + return conn; + } + + public PrintWriter getLogWriter() throws SQLException + { + return wrappedDS.getLogWriter(); + } + + public int getLoginTimeout() throws SQLException + { + return wrappedDS.getLoginTimeout(); + } + + public void setLogWriter(PrintWriter out) throws SQLException + { + wrappedDS.setLogWriter(out); + } + + public void setLoginTimeout(int seconds) throws SQLException + { + wrappedDS.setLoginTimeout(seconds); + } + + private Connection getEnlistedConnection(XAConnection xaConn) throws SQLException + { + return new ConnectionWrapper(xaConn, tm); + } + + public void setDataSource(XADataSource dsToWrap) + { + wrappedDS = dsToWrap; + } + + + public void setTxManager(TransactionManager txMgr) + { + tm = txMgr; + } + + @Override + public boolean equals(Object other) + { + if (other == this) return true; + if (other == null) return false; + + if (other.getClass() == this.getClass()) { + return wrappedDS.equals(((XADatasourceEnlistingWrapper)other).wrappedDS); + } + + return false; + } + + @Override + public int hashCode() + { + return wrappedDS.hashCode(); + } + + public boolean isWrapperFor(Class arg0) throws SQLException + { + return false; + } + + public T unwrap(Class arg0) throws SQLException + { + return null; + } +}