001/* 002 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 003 * in compliance with the License. You may obtain a copy of the License at 004 * 005 * http://www.apache.org/licenses/LICENSE-2.0 006 * 007 * Unless required by applicable law or agreed to in writing, software distributed under the License 008 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 009 * or implied. See the License for the specific language governing permissions and limitations under 010 * the License. 011 */ 012package org.atteo.moonshine.atomikos; 013 014import java.lang.management.ManagementFactory; 015import java.util.Properties; 016 017import javax.inject.Inject; 018import javax.inject.Singleton; 019import javax.jms.ConnectionFactory; 020import javax.jms.XAConnectionFactory; 021import javax.sql.DataSource; 022import javax.sql.XADataSource; 023import javax.transaction.SystemException; 024import javax.transaction.TransactionManager; 025import javax.transaction.UserTransaction; 026import javax.xml.bind.annotation.XmlElement; 027import javax.xml.bind.annotation.XmlRootElement; 028 029import org.atteo.config.XmlDefaultValue; 030import org.atteo.moonshine.jta.JtaConnectionFactoryWrapper; 031import org.atteo.moonshine.jta.JtaDataSourceWrapper; 032import org.atteo.moonshine.jta.JtaService; 033import org.atteo.moonshine.jta.PoolOptions; 034 035import com.atomikos.icatch.SysException; 036import com.atomikos.icatch.config.UserTransactionServiceImp; 037import com.atomikos.icatch.config.imp.AbstractUserTransactionServiceFactory; 038import com.atomikos.icatch.jta.UserTransactionManager; 039import com.atomikos.icatch.standalone.UserTransactionServiceFactory; 040import com.atomikos.jdbc.AtomikosDataSourceBean; 041import com.atomikos.jms.AtomikosConnectionFactoryBean; 042import com.google.inject.Module; 043import com.google.inject.PrivateModule; 044import com.google.inject.Provider; 045 046/** 047 * Atomikos JTA implementation. 048 */ 049@XmlRootElement(name = "atomikos") 050public class Atomikos extends JtaService { 051 /** 052 * Specifies the maximum number of active transactions. 053 * <p> 054 * A negative value means infinite amount. You will get an IllegalStateException 055 * with error message "Max number of active transactions reached" if you call 056 * {@link UserTransaction#begin()} while there are already n concurrent transactions running, 057 * n being this value. 058 * </p> 059 */ 060 @XmlElement 061 private Integer maxActiveTransactions = -1; 062 063 @XmlElement 064 @XmlDefaultValue("${dataHome}/atomikos/logs") 065 private String logDirectory; 066 067 @XmlElement 068 @XmlDefaultValue("${dataHome}/atomikos/") 069 private String consoleOutputDirectory; 070 071 /** 072 * The default timeout (in seconds) that is set for transactions when no timeout is specified. 073 */ 074 @XmlElement 075 @XmlDefaultValue("60") 076 private Integer transactionTimeout; 077 078 private UserTransactionManager manager; 079 private UserTransactionServiceImp service; 080 081 public class ManagerProvider implements Provider<UserTransactionManager> { 082 @Override 083 public UserTransactionManager get() { 084 System.setProperty(UserTransactionServiceImp.NO_FILE_PROPERTY_NAME, "true"); 085 System.setProperty(UserTransactionServiceImp.HIDE_INIT_FILE_PATH_PROPERTY_NAME, 086 "true"); 087 System.setProperty("com.atomikos.icatch.service", 088 UserTransactionServiceFactory.class.getCanonicalName()); 089 090 Properties properties = new Properties(); 091 properties.setProperty(AbstractUserTransactionServiceFactory.MAX_ACTIVES_PROPERTY_NAME, 092 Integer.toString(maxActiveTransactions)); 093 properties.setProperty(AbstractUserTransactionServiceFactory.TM_UNIQUE_NAME_PROPERTY_NAME, 094 "TM_" + ManagementFactory.getRuntimeMXBean().getName()); 095 properties.setProperty(AbstractUserTransactionServiceFactory.LOG_BASE_NAME_PROPERTY_NAME, 096 "log_"); 097 properties.setProperty(AbstractUserTransactionServiceFactory.LOG_BASE_DIR_PROPERTY_NAME, 098 logDirectory); 099 properties.setProperty(AbstractUserTransactionServiceFactory.OUTPUT_DIR_PROPERTY_NAME, 100 consoleOutputDirectory); 101 properties.setProperty(AbstractUserTransactionServiceFactory.THREADED_2PC_PROPERTY_NAME, 102 "false"); 103 properties.setProperty(AbstractUserTransactionServiceFactory.DEFAULT_JTA_TIMEOUT_PROPERTY_NAME, 104 Integer.toString(transactionTimeout * 1000)); 105 service = new UserTransactionServiceImp(properties); 106 try { 107 service.init(); 108 } catch (SysException e) { 109 throw new RuntimeException(e.getErrors().pop().toString(), e); 110 } 111 112 manager = new UserTransactionManager(); 113 manager.setStartupTransactionService(false); 114 try { 115 manager.init(); 116 } catch (SystemException e) { 117 throw new RuntimeException(e); 118 } 119 return manager; 120 } 121 } 122 123 private static class AtomikosDataSourceWrapper implements JtaDataSourceWrapper { 124 @Inject 125 UserTransactionManager userTransactionManager; 126 127 @Override 128 public DataSource wrap(String name, XADataSource xaDataSource, PoolOptions poolOptions, String testQuery) { 129 AtomikosDataSourceBean wrapped = new AtomikosDataSourceBean(); 130 wrapped.setXaDataSource(xaDataSource); 131 wrapped.setUniqueResourceName(name); 132 133 if (poolOptions == null) { 134 poolOptions = new PoolOptions(); 135 } 136 if (poolOptions.getMaxLifeTime() != null && poolOptions.getMaxLifeTime() != 0) { 137 wrapped.setMaxLifetime(poolOptions.getMaxLifeTime()); 138 } else { 139 // test query is only needed when we don't know how long Atomikos can keep connections in the pool 140 wrapped.setTestQuery(testQuery); 141 } 142 143 if (poolOptions.getMinPoolSize() != null) { 144 wrapped.setMinPoolSize(poolOptions.getMinPoolSize()); 145 } 146 if (poolOptions.getMaxPoolSize() != null) { 147 wrapped.setMaxPoolSize(poolOptions.getMaxPoolSize()); 148 } 149 if (poolOptions.getMaxIdleTime() != null) { 150 wrapped.setMaxIdleTime(poolOptions.getMaxIdleTime()); 151 } 152 if (poolOptions.getReapTimeout() != null) { 153 wrapped.setReapTimeout(poolOptions.getReapTimeout()); 154 } 155 return wrapped; 156 } 157 158 @Override 159 public void close(DataSource dataSource) { 160 ((AtomikosDataSourceBean) dataSource).close(); 161 } 162 } 163 164 private static class AtomikosConnectionFactoryWrapper implements JtaConnectionFactoryWrapper { 165 @Override 166 public ConnectionFactory wrap(String name, XAConnectionFactory xaFactory, 167 PoolOptions poolOptions) { 168 AtomikosConnectionFactoryBean wrapped = new AtomikosConnectionFactoryBean(); 169 wrapped.setXaConnectionFactory(xaFactory); 170 wrapped.setUniqueResourceName(name); 171 if (poolOptions == null) { 172 return wrapped; 173 } 174 175 if (poolOptions.getMinPoolSize() != null) { 176 wrapped.setMinPoolSize(poolOptions.getMinPoolSize()); 177 } 178 if (poolOptions.getMaxPoolSize() != null) { 179 wrapped.setMaxPoolSize(poolOptions.getMaxPoolSize()); 180 } 181 if (poolOptions.getMaxIdleTime() != null) { 182 wrapped.setMaxIdleTime(poolOptions.getMaxIdleTime()); 183 } 184 185 if (poolOptions.getReapTimeout() != null) { 186 wrapped.setReapTimeout(poolOptions.getReapTimeout()); 187 } 188 return wrapped; 189 } 190 191 @Override 192 public void close(ConnectionFactory connectionFactory) { 193 ((AtomikosConnectionFactoryBean) connectionFactory).close(); 194 } 195 } 196 197 @Override 198 public Module configure() { 199 return new PrivateModule() { 200 @Override 201 protected void configure() { 202 bind(UserTransactionManager.class).toProvider(new ManagerProvider()).in(Singleton.class); 203 bind(TransactionManager.class).to(UserTransactionManager.class); 204 expose(TransactionManager.class); 205 bind(UserTransaction.class).to(UserTransactionManager.class); 206 expose(UserTransaction.class); 207 bind(JtaDataSourceWrapper.class).to(AtomikosDataSourceWrapper.class).in(Singleton.class); 208 expose(JtaDataSourceWrapper.class); 209 bind(JtaConnectionFactoryWrapper.class).to(AtomikosConnectionFactoryWrapper.class).in(Singleton.class); 210 expose(JtaConnectionFactoryWrapper.class); 211 } 212 }; 213 } 214 215 @Override 216 public void close() { 217 if (manager != null) { 218 manager.close(); 219 } 220 if (service != null) { 221 service.shutdownWait(); 222 } 223 } 224}