1 /***
2 * Copyright 2003-2010 Terracotta, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package net.sf.ehcache.transaction.manager;
17
18 import java.lang.reflect.InvocationTargetException;
19 import java.lang.reflect.Method;
20 import java.util.Properties;
21 import java.util.concurrent.locks.Lock;
22 import java.util.concurrent.locks.ReentrantLock;
23
24 import javax.naming.InitialContext;
25 import javax.naming.NamingException;
26 import javax.transaction.TransactionManager;
27 import javax.transaction.xa.XAResource;
28
29 import net.sf.ehcache.transaction.xa.EhcacheXAResource;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 /***
34 * Default {@link TransactionManagerLookup} implementation, that will be used by an {@link net.sf.ehcache.Cache#initialise() initializing}
35 * Cache should the user have not specified otherwise.<p>
36 * This implementation will:
37 * <ol>
38 * <li>try lookup an {@link javax.naming.InitialContext};
39 * <li>if successful, lookup a TransactionManager under java:/TransactionManager, this location can be overridden;
40 * <li>if it failed, or couldn't find {@link javax.transaction.TransactionManager} instance, look for a Bitronix TransactionManager;
41 * <li>and finally an Atomikos one.
42 * </ol>
43 *
44 * To specify under what specific name the TransactionManager is to be found, you can provide a jndiName property
45 * using {@link #setProperties(java.util.Properties)}. That can be set in the CacheManager's configuration file.
46 *
47 * The first TransactionManager instance is then kept and returned on each {@link #getTransactionManager()} call
48 *
49 * @author Alex Snaps
50 */
51 public class DefaultTransactionManagerLookup implements TransactionManagerLookup {
52
53 private static final Logger LOG = LoggerFactory.getLogger(DefaultTransactionManagerLookup.class.getName());
54
55 private transient TransactionManager transactionManager;
56 private transient String vendor = "";
57 private transient Properties properties = new Properties();
58 private final Lock lock = new ReentrantLock();
59
60 private final JndiSelector defaultJndiSelector = new JndiSelector("genericJNDI", "java:/TransactionManager");
61
62 private final Selector[] transactionManagerSelectors = new Selector[] {defaultJndiSelector,
63 new JndiSelector("Weblogic", "javax.transaction.TransactionManager"),
64 new FactorySelector("Bitronix", "bitronix.tm.TransactionManagerServices"),
65 new ClassSelector("Atomikos", "com.atomikos.icatch.jta.UserTransactionManager"),
66 };
67
68 /***
69 * Lookup available txnManagers
70 * @return TransactionManager
71 */
72 public TransactionManager getTransactionManager() {
73 if (transactionManager == null) {
74 lock.lock();
75 try {
76 if (transactionManager == null) {
77 lookupTransactionManager();
78 }
79 } finally {
80 lock.unlock();
81 }
82 }
83 return transactionManager;
84 }
85
86 /***
87 * {@inheritDoc}
88 */
89 public void register(EhcacheXAResource resource) {
90 if (vendor.equals("Bitronix")) {
91 registerResourceWithBitronix(resource.getCacheName(), resource);
92 }
93 }
94
95 /***
96 * {@inheritDoc}
97 */
98 public void unregister(EhcacheXAResource resource) {
99 if (vendor.equals("Bitronix")) {
100 unregisterResourceWithBitronix(resource.getCacheName(), resource);
101 }
102 }
103
104 /***
105 * {@inheritDoc}
106 */
107 public void setProperties(Properties properties) {
108 this.properties = properties;
109 parseProperties();
110 }
111
112 private void parseProperties() {
113 if (this.properties != null) {
114 String jndiName = this.properties.getProperty("jndiName");
115 if (jndiName != null) {
116 defaultJndiSelector.setJndiName(jndiName);
117 }
118 }
119 }
120
121 private void registerResourceWithBitronix(String uniqueName, EhcacheXAResource resource) {
122 ClassLoader cl = Thread.currentThread().getContextClassLoader();
123 if (cl == null) {
124 cl = ClassLoader.getSystemClassLoader();
125 }
126 try {
127
128 Class producerClass = cl.loadClass("bitronix.tm.resource.ehcache.EhCacheXAResourceProducer");
129
130 Class[] signature = new Class[] {String.class, XAResource.class};
131 Object[] args = new Object[] {uniqueName, resource};
132 Method method = producerClass.getMethod("registerXAResource", signature);
133 method.invoke(null, args);
134 } catch (Exception e) {
135 LOG.error("unable to register resource of cache " + uniqueName + " with BTM", e);
136 }
137 }
138
139 private void unregisterResourceWithBitronix(String uniqueName, EhcacheXAResource resource) {
140 ClassLoader cl = Thread.currentThread().getContextClassLoader();
141 if (cl == null) {
142 cl = ClassLoader.getSystemClassLoader();
143 }
144 try {
145
146 Class producerClass = cl.loadClass("bitronix.tm.resource.ehcache.EhCacheXAResourceProducer");
147
148 Class[] signature = new Class[] {String.class, XAResource.class};
149 Object[] args = new Object[] {uniqueName, resource};
150 Method method = producerClass.getMethod("unregisterXAResource", signature);
151 method.invoke(null, args);
152 } catch (Exception e) {
153 LOG.error("unable to unregister resource of cache " + uniqueName + " with BTM", e);
154 }
155 }
156
157 private void lookupTransactionManager() {
158 InitialContext context = null;
159 try {
160 context = new InitialContext();
161 } catch (NamingException e) {
162 LOG.debug("Couldn't create an InitialContext", e);
163 }
164
165 for (Selector selector : transactionManagerSelectors) {
166 this.transactionManager = selector.lookup(context);
167 if (this.transactionManager != null) {
168 this.vendor = selector.getVendor();
169 LOG.debug("Found TransactionManager for {}", vendor);
170 return;
171 }
172 }
173
174 LOG.warn("No TransactionManager located!");
175 }
176
177 /***
178 *
179 */
180 private abstract static class Selector {
181
182 private final String vendor;
183
184 protected Selector(final String vendor) {
185 this.vendor = vendor;
186 }
187
188 public String getVendor() {
189 return vendor;
190 }
191
192 protected abstract TransactionManager lookup(InitialContext initialContext);
193 }
194
195 /***
196 *
197 */
198 private static final class JndiSelector extends Selector {
199
200 private volatile String jndiName;
201
202 private JndiSelector(final String vendor, final String jndiName) {
203 super(vendor);
204 this.jndiName = jndiName;
205 }
206
207 public String getJndiName() {
208 return jndiName;
209 }
210
211 public void setJndiName(final String jndiName) {
212 this.jndiName = jndiName;
213 }
214
215 @Override
216 protected TransactionManager lookup(InitialContext initialContext) {
217
218 if (initialContext == null) {
219 return null;
220 }
221
222 Object jndiObject;
223 try {
224 jndiObject = initialContext.lookup(getJndiName());
225 if (jndiObject instanceof TransactionManager) {
226 return (TransactionManager) jndiObject;
227 }
228 } catch (NamingException e) {
229 LOG.debug("Couldn't locate TransactionManager for {} under {}", getVendor(), getJndiName());
230 }
231 return null;
232 }
233 }
234
235 /***
236 *
237 */
238 private static final class FactorySelector extends Selector {
239
240 private final String factoryClassName;
241
242 private FactorySelector(final String vendor, final String factoryClassName) {
243 super(vendor);
244 this.factoryClassName = factoryClassName;
245 }
246
247 @Override
248 protected TransactionManager lookup(final InitialContext initialContext) {
249 TransactionManager transactionManager = null;
250 ClassLoader cl = Thread.currentThread().getContextClassLoader();
251 if (cl == null) {
252 cl = ClassLoader.getSystemClassLoader();
253 }
254 try {
255 Class factoryClass = cl.loadClass(factoryClassName);
256 Class[] signature = null;
257 Object[] args = null;
258 Method method = factoryClass.getMethod("getTransactionManager", signature);
259 transactionManager = (TransactionManager) method.invoke(null, args);
260 } catch (ClassNotFoundException e) {
261 LOG.debug("FactorySelector failed lookup", e);
262 } catch (NoSuchMethodException e) {
263 LOG.debug("FactorySelector failed lookup", e);
264 } catch (InvocationTargetException e) {
265 LOG.debug("FactorySelector failed lookup", e);
266 } catch (IllegalAccessException e) {
267 LOG.debug("FactorySelector failed lookup", e);
268 }
269 return transactionManager;
270 }
271 }
272
273 /***
274 *
275 */
276 private static final class ClassSelector extends Selector {
277
278 private final String classname;
279
280 private ClassSelector(final String vendor, final String classname) {
281 super(vendor);
282 this.classname = classname;
283 }
284
285 @Override
286 protected TransactionManager lookup(final InitialContext initialContext) {
287 TransactionManager transactionManager = null;
288 ClassLoader cl = Thread.currentThread().getContextClassLoader();
289 if (cl == null) {
290 cl = ClassLoader.getSystemClassLoader();
291 }
292 try {
293 Class txManagerClass = cl.loadClass(classname);
294 transactionManager = (TransactionManager) txManagerClass.newInstance();
295 } catch (ClassNotFoundException e) {
296 LOG.debug("FactorySelector failed lookup", e);
297 } catch (InstantiationException e) {
298 LOG.debug("FactorySelector failed lookup", e);
299 } catch (IllegalAccessException e) {
300 LOG.debug("FactorySelector failed lookup", e);
301 }
302 return transactionManager;
303 }
304 }
305
306 }