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 String tmName = "TM_" + ManagementFactory.getRuntimeMXBean().getName(); 094 if (tmName.length() > 30) { 095 tmName = tmName.substring(0, 30); 096 } 097 properties.setProperty(AbstractUserTransactionServiceFactory.TM_UNIQUE_NAME_PROPERTY_NAME, tmName); 098 properties.setProperty(AbstractUserTransactionServiceFactory.LOG_BASE_NAME_PROPERTY_NAME, 099 "log_"); 100 properties.setProperty(AbstractUserTransactionServiceFactory.LOG_BASE_DIR_PROPERTY_NAME, 101 logDirectory); 102 properties.setProperty(AbstractUserTransactionServiceFactory.OUTPUT_DIR_PROPERTY_NAME, 103 consoleOutputDirectory); 104 properties.setProperty(AbstractUserTransactionServiceFactory.THREADED_2PC_PROPERTY_NAME, 105 "false"); 106 properties.setProperty(AbstractUserTransactionServiceFactory.DEFAULT_JTA_TIMEOUT_PROPERTY_NAME, 107 Integer.toString(transactionTimeout * 1000)); 108 service = new UserTransactionServiceImp(properties); 109 try { 110 service.init(); 111 } catch (SysException e) { 112 throw new RuntimeException(e.getErrors().pop().toString(), e); 113 } 114 115 manager = new UserTransactionManager(); 116 manager.setStartupTransactionService(false); 117 try { 118 manager.init(); 119 } catch (SystemException e) { 120 throw new RuntimeException(e); 121 } 122 return manager; 123 } 124 } 125 126 private static class AtomikosDataSourceWrapper implements JtaDataSourceWrapper { 127 @Inject 128 UserTransactionManager userTransactionManager; 129 130 @Override 131 public DataSource wrap(String name, XADataSource xaDataSource, PoolOptions poolOptions, String testQuery) { 132 AtomikosDataSourceBean wrapped = new AtomikosDataSourceBean(); 133 wrapped.setXaDataSource(xaDataSource); 134 wrapped.setUniqueResourceName(name); 135 136 if (poolOptions == null) { 137 poolOptions = new PoolOptions(); 138 } 139 if (poolOptions.getMaxLifeTime() != null && poolOptions.getMaxLifeTime() != 0) { 140 wrapped.setMaxLifetime(poolOptions.getMaxLifeTime()); 141 } else { 142 // test query is only needed when we don't know how long Atomikos can keep connections in the pool 143 wrapped.setTestQuery(testQuery); 144 } 145 146 if (poolOptions.getMinPoolSize() != null) { 147 wrapped.setMinPoolSize(poolOptions.getMinPoolSize()); 148 } 149 if (poolOptions.getMaxPoolSize() != null) { 150 wrapped.setMaxPoolSize(poolOptions.getMaxPoolSize()); 151 } 152 if (poolOptions.getMaxIdleTime() != null) { 153 wrapped.setMaxIdleTime(poolOptions.getMaxIdleTime()); 154 } 155 if (poolOptions.getReapTimeout() != null) { 156 wrapped.setReapTimeout(poolOptions.getReapTimeout()); 157 } 158 return wrapped; 159 } 160 161 @Override 162 public void close(DataSource dataSource) { 163 ((AtomikosDataSourceBean) dataSource).close(); 164 } 165 } 166 167 private static class AtomikosConnectionFactoryWrapper implements JtaConnectionFactoryWrapper { 168 @Override 169 public ConnectionFactory wrap(String name, XAConnectionFactory xaFactory, 170 PoolOptions poolOptions) { 171 AtomikosConnectionFactoryBean wrapped = new AtomikosConnectionFactoryBean(); 172 wrapped.setXaConnectionFactory(xaFactory); 173 wrapped.setUniqueResourceName(name); 174 if (poolOptions == null) { 175 return wrapped; 176 } 177 178 if (poolOptions.getMinPoolSize() != null) { 179 wrapped.setMinPoolSize(poolOptions.getMinPoolSize()); 180 } 181 if (poolOptions.getMaxPoolSize() != null) { 182 wrapped.setMaxPoolSize(poolOptions.getMaxPoolSize()); 183 } 184 if (poolOptions.getMaxIdleTime() != null) { 185 wrapped.setMaxIdleTime(poolOptions.getMaxIdleTime()); 186 } 187 188 if (poolOptions.getReapTimeout() != null) { 189 wrapped.setReapTimeout(poolOptions.getReapTimeout()); 190 } 191 return wrapped; 192 } 193 194 @Override 195 public void close(ConnectionFactory connectionFactory) { 196 ((AtomikosConnectionFactoryBean) connectionFactory).close(); 197 } 198 } 199 200 @Override 201 public Module configure() { 202 return new PrivateModule() { 203 @Override 204 protected void configure() { 205 bind(UserTransactionManager.class).toProvider(new ManagerProvider()).in(Singleton.class); 206 bind(TransactionManager.class).to(UserTransactionManager.class); 207 expose(TransactionManager.class); 208 bind(UserTransaction.class).to(UserTransactionManager.class); 209 expose(UserTransaction.class); 210 bind(JtaDataSourceWrapper.class).to(AtomikosDataSourceWrapper.class).in(Singleton.class); 211 expose(JtaDataSourceWrapper.class); 212 bind(JtaConnectionFactoryWrapper.class).to(AtomikosConnectionFactoryWrapper.class).in(Singleton.class); 213 expose(JtaConnectionFactoryWrapper.class); 214 } 215 }; 216 } 217 218 @Override 219 public void close() { 220 if (manager != null) { 221 manager.close(); 222 } 223 if (service != null) { 224 service.shutdownWait(); 225 } 226 } 227}