Commit the Transaction Service (Felix-1012) donated by Guillaume Nodet

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@763278 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/transaction/org.apache.felix.transaction/pom.xml b/transaction/org.apache.felix.transaction/pom.xml
new file mode 100644
index 0000000..955627b
--- /dev/null
+++ b/transaction/org.apache.felix.transaction/pom.xml
@@ -0,0 +1,225 @@
+<!--
+ 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.
+-->
+<project>
+    <parent>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>transaction</artifactId>
+        <version>0.9.0-SNAPSHOT</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>org.apache.felix.transaction</artifactId>
+    <groupId>org.apache.felix</groupId>
+    <name>Apache Felix Transaction</name>
+    <version>0.9.0-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+
+    <properties>
+        <geronimo-transaction-version>2.1.2</geronimo-transaction-version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <version>1.2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <version>1.2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.components</groupId>
+            <artifactId>geronimo-transaction</artifactId>
+            <version>2.1.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.objectweb.howl</groupId>
+            <artifactId>howl</artifactId>
+            <version>1.0.1-1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-tx</artifactId>
+            <version>2.5.6</version>
+            <optional>true</optional>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.5</source>
+                    <target>1.5</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>2.0.0</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Import-Package>
+                            org.springframework.*;resolution:=optional,
+                            *
+                        </Import-Package>
+                        <Export-Package>
+                            org.apache.geronimo.transaction*;version="${geronimo-transaction-version}"
+                        </Export-Package>
+                        <Private-Package>
+                            org.objectweb.howl.log*,
+                            org.apache.felix.transaction.internal
+                        </Private-Package>
+                        <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
+                        <Bundle-Activator>org.apache.felix.transaction.internal.Activator</Bundle-Activator>
+                        <Export-Service>
+                            javax.transaction.TransactionManager,
+                            javax.transaction.TransactionSynchronizationRegistry,
+                            javax.transaction.UserTransaction,
+                            org.apache.geronimo.transaction.manager.RecoverableTransactionManager
+                        </Export-Service>
+                    </instructions>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>unpack-sources</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>unpack</goal>
+                        </goals>
+                        <configuration>
+                            <artifactItems>
+                                <artifactItem>
+                                    <groupId>org.apache.geronimo.components</groupId>
+                                    <artifactId>geronimo-transaction</artifactId>
+                                    <classifier>sources</classifier>
+                                </artifactItem>
+                                <artifactItem>
+                                    <groupId>org.objectweb.howl</groupId>
+                                    <artifactId>howl</artifactId>
+                                    <classifier>sources</classifier>
+                                </artifactItem>
+                            </artifactItems>
+                            <outputDirectory>${project.build.directory}/sources</outputDirectory>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-antrun-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>generate-sources</phase>
+                        <configuration>
+                            <tasks>
+                                <copy todir="${project.build.directory}/sources">
+                                    <fileset dir="${basedir}/src/main/java" />
+                                </copy>
+                            </tasks>
+                        </configuration>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>attach-sources</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>package1</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                        <configuration>
+                            <minmemory>128m</minmemory>
+                            <maxmemory>512m</maxmemory>
+                            <sourcepath>${project.build.directory}/sources</sourcepath>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>1.1</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <artifactSet>
+                                <includes>
+                                    <include>org.apache.geronimo.components:geronimo-transaction</include>
+                                    <include>org.objectweb.howl:howl</include>
+                                    <include>${project.groupId}:${project.artifactId}</include>
+                                </includes>
+                            </artifactSet>
+                            <filters>
+                                <filter>
+                                    <artifact>org.apache.geronimo.components:geronimo-transaction</artifact>
+                                    <excludes>
+                                        <exclude>org/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>org.objectweb.howl:howl</artifact>
+                                    <excludes>
+                                        <exclude>org/**</exclude>
+                                    </excludes>
+                                </filter>
+                            </filters>
+                            <createSourcesJar>true</createSourcesJar>
+                            <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
+                            <createDependencyReducedPom>true</createDependencyReducedPom>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/transaction/org.apache.felix.transaction/src/main/java/org/apache/felix/transaction/internal/Activator.java b/transaction/org.apache.felix.transaction/src/main/java/org/apache/felix/transaction/internal/Activator.java
new file mode 100644
index 0000000..4bff950
--- /dev/null
+++ b/transaction/org.apache.felix.transaction/src/main/java/org/apache/felix/transaction/internal/Activator.java
@@ -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.felix.transaction.internal;
+
+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.felix.transaction");
+
+    private BundleContext bundleContext;
+    private Map managers = new HashMap<String, TransactionManagerService>();
+
+    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.felix.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
diff --git a/transaction/org.apache.felix.transaction/src/main/java/org/apache/felix/transaction/internal/GeronimoPlatformTransactionManager.java b/transaction/org.apache.felix.transaction/src/main/java/org/apache/felix/transaction/internal/GeronimoPlatformTransactionManager.java
new file mode 100644
index 0000000..77bde50
--- /dev/null
+++ b/transaction/org.apache.felix.transaction/src/main/java/org/apache/felix/transaction/internal/GeronimoPlatformTransactionManager.java
@@ -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.felix.transaction.internal;
+
+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<Transaction, SuspendedResourcesHolder> suspendedResources = new ConcurrentHashMap<Transaction, SuspendedResourcesHolder>();
+
+    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 <code>suspend</code> and <code>resume</code>.
+     */
+    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;
+        }
+    }
+
+}
diff --git a/transaction/org.apache.felix.transaction/src/main/java/org/apache/felix/transaction/internal/TransactionManagerService.java b/transaction/org.apache.felix.transaction/src/main/java/org/apache/felix/transaction/internal/TransactionManagerService.java
new file mode 100644
index 0000000..22997b2
--- /dev/null
+++ b/transaction/org.apache.felix.transaction/src/main/java/org/apache/felix/transaction/internal/TransactionManagerService.java
@@ -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.felix.transaction.internal;
+
+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 = "felix.transaction.timeout";
+    public static final String RECOVERABLE = "felix.transaction.recoverable";
+    public static final String HOWL_BUFFER_CLASS_NAME = "felix.transaction.howl.bufferClassName";
+    public static final String HOWL_BUFFER_SIZE = "felix.transaction.howl.bufferSize";
+    public static final String HOWL_CHECKSUM_ENABLED = "felix.transaction.howl.checksumEnabled";
+    public static final String HOWL_ADLER32_CHECKSUM = "felix.transaction.howl.adler32Checksum";
+    public static final String HOWL_FLUSH_SLEEP_TIME = "felix.transaction.howl.flushSleepTime";
+    public static final String HOWL_LOG_FILE_EXT = "felix.transaction.howl.logFileExt";
+    public static final String HOWL_LOG_FILE_NAME = "felix.transaction.howl.logFileName";
+    public static final String HOWL_MAX_BLOCKS_PER_FILE = "felix.transaction.howl.maxBlocksPerFile";
+    public static final String HOWL_MAX_LOG_FILES = "felix.transaction.howl.maxLogFiles";
+    public static final String HOWL_MAX_BUFFERS = "felix.transaction.howl.maxBuffers";
+    public static final String HOWL_MIN_BUFFERS = "felix.transaction.howl.minBuffers";
+    public static final String HOWL_THREADS_WAITING_FORCE_THRESHOLD = "felix.transaction.howl.threadsWaitingForceThreshold";
+    public static final String HOWL_LOG_FILE_DIR = "felix.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<String> clazzes = new ArrayList<String>();
+        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);
+        }
+
+    }
+}