| /* |
| * 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); |
| } |
| |
| } |
| } |