/* * 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.geode.internal.jndi; import org.apache.geode.distributed.DistributedSystem; import org.apache.geode.distributed.internal.DistributionConfig; import org.apache.geode.i18n.LogWriterI18n; import org.apache.geode.internal.ClassPathLoader; import org.apache.geode.internal.datasource.AbstractDataSource; import org.apache.geode.internal.datasource.ClientConnectionFactoryWrapper; import org.apache.geode.internal.datasource.DataSourceCreateException; import org.apache.geode.internal.datasource.DataSourceFactory; import org.apache.geode.internal.i18n.LocalizedStrings; import org.apache.geode.internal.jta.TransactionManagerImpl; import org.apache.geode.internal.jta.TransactionUtils; import org.apache.geode.internal.jta.UserTransactionImpl; import javax.naming.*; import javax.transaction.SystemException; import javax.transaction.TransactionManager; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Map; /** * <p> * Utility class for binding DataSources and Transactional resources to JNDI Tree. If there is a * pre-existing JNDI tree in the system, the GemFire JNDI tree is not not generated. * </p> * <p> * Datasources are bound to jndi tree after getting initialised. The initialisation parameters are * read from cache.xml. * </p> * <p> * If there is a pre-existing TransactionManager/UserTransaction (possible in presence of * application server scenerio), the GemFire TransactionManager and UserTransaction will not be * initialised. In that case, application server TransactionManager/UserTransaction will handle the * transactional activity. But even in this case the datasource element will be bound to the * available JNDI tree. The transactional datasource (XADataSource) will make use of available * TransactionManager. * </p> * */ public class JNDIInvoker { // private static boolean DEBUG = false; /** * JNDI Context, this may refer to GemFire JNDI Context or external Context, in case the external * JNDI tree exists. */ private static Context ctx; /** * transactionManager TransactionManager, this refers to GemFire TransactionManager only. */ private static TransactionManager transactionManager; // most of the following came from the javadocs at: // http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/transaction/jta/JtaTransactionManager.html private static String[][] knownJNDIManagers = {{"java:/TransactionManager", "JBoss"}, {"java:comp/TransactionManager", "Cosminexus"}, // and many others {"java:appserver/TransactionManager", "GlassFish"}, {"java:pm/TransactionManager", "SunONE"}, {"java:comp/UserTransaction", "Orion, JTOM, BEA WebLogic"}, // not sure about the following but leaving it for backwards compat {"javax.transaction.TransactionManager", "BEA WebLogic"}}; /** ************************************************* */ /** * WebSphere 5.1 TransactionManagerFactory */ private static final String WS_FACTORY_CLASS_5_1 = "com.ibm.ws.Transaction.TransactionManagerFactory"; /** * WebSphere 5.0 TransactionManagerFactory */ private static final String WS_FACTORY_CLASS_5_0 = "com.ibm.ejs.jts.jta.TransactionManagerFactory"; /** * WebSphere 4.0 TransactionManagerFactory */ private static final String WS_FACTORY_CLASS_4 = "com.ibm.ejs.jts.jta.JTSXA"; /** * List of DataSource bound to the context, used for cleaning gracefully closing datasource and * associated threads. */ private static List dataSourceList = new ArrayList(); /** * If this system property is set to true, GemFire will not try to lookup for an existing JTA * transaction manager bound to JNDI context or try to bind itself as a JTA transaction manager. * Also region operations will <b>not</b> participate in an ongoing JTA transaction. */ private static Boolean IGNORE_JTA = Boolean.getBoolean(DistributionConfig.GEMFIRE_PREFIX + "ignoreJTA"); /** * Bind the transaction resources. Bind UserTransaction and TransactionManager. * <p> * If there is pre-existing JNDI tree in the system and TransactionManager / UserTransaction is * already bound, GemFire will make use of these resources, but if TransactionManager / * UserTransaction is not available, the GemFire TransactionManager / UserTransaction will be * bound to the JNDI tree. * </p> * */ public static void mapTransactions(DistributedSystem distSystem) { try { TransactionUtils.setLogWriter(distSystem.getLogWriter().convertToLogWriterI18n()); cleanup(); if (IGNORE_JTA) { return; } ctx = new InitialContext(); doTransactionLookup(); } catch (NamingException ne) { LogWriterI18n writer = TransactionUtils.getLogWriterI18n(); if (ne instanceof NoInitialContextException) { String exception = "JNDIInvoker::mapTransactions:: No application server context found, Starting GemFire JNDI Context Context "; if (writer.finerEnabled()) writer.finer(exception); try { initializeGemFireContext(); transactionManager = TransactionManagerImpl.getTransactionManager(); ctx.rebind("java:/TransactionManager", transactionManager); if (writer.fineEnabled()) writer.fine( "JNDIInvoker::mapTransactions::Bound TransactionManager to Context GemFire JNDI Tree"); UserTransactionImpl utx = new UserTransactionImpl(); ctx.rebind("java:/UserTransaction", utx); if (writer.fineEnabled()) writer.fine( "JNDIInvoker::mapTransactions::Bound Transaction to Context GemFire JNDI Tree"); } catch (NamingException ne1) { if (writer.infoEnabled()) writer.info( LocalizedStrings.JNDIInvoker_JNDIINVOKERMAPTRANSACTIONSNAMINGEXCEPTION_WHILE_BINDING_TRANSACTIONMANAGERUSERTRANSACTION_TO_GEMFIRE_JNDI_TREE); } catch (SystemException se1) { if (writer.infoEnabled()) writer.info( LocalizedStrings.JNDIInvoker_JNDIINVOKERMAPTRANSACTIONSSYSTEMEXCEPTION_WHILE_BINDING_USERTRANSACTION_TO_GEMFIRE_JNDI_TREE); } } else if (ne instanceof NameNotFoundException) { String exception = "JNDIInvoker::mapTransactions:: No TransactionManager associated to Application server context, trying to bind GemFire TransactionManager"; if (writer.finerEnabled()) writer.finer(exception); try { transactionManager = TransactionManagerImpl.getTransactionManager(); ctx.rebind("java:/TransactionManager", transactionManager); if (writer.fineEnabled()) writer.fine( "JNDIInvoker::mapTransactions::Bound TransactionManager to Application Server Context"); UserTransactionImpl utx = new UserTransactionImpl(); ctx.rebind("java:/UserTransaction", utx); if (writer.fineEnabled()) writer.fine( "JNDIInvoker::mapTransactions::Bound UserTransaction to Application Server Context"); } catch (NamingException ne1) { if (writer.infoEnabled()) writer.info( LocalizedStrings.JNDIInvoker_JNDIINVOKERMAPTRANSACTIONSNAMINGEXCEPTION_WHILE_BINDING_TRANSACTIONMANAGERUSERTRANSACTION_TO_APPLICATION_SERVER_JNDI_TREE); } catch (SystemException se1) { if (writer.infoEnabled()) writer.info( LocalizedStrings.JNDIInvoker_JNDIINVOKERMAPTRANSACTIONSSYSTEMEXCEPTION_WHILE_BINDING_TRANSACTIONMANAGERUSERTRANSACTION_TO_APPLICATION_SERVER_JNDI_TREE); } } } } /* * Cleans all the DataSource ans its associated threads gracefully, before start of the GemFire * system. Also kills all of the threads associated with the TransactionManager before making it * null. */ private static void cleanup() { if (transactionManager instanceof TransactionManagerImpl) { TransactionManagerImpl.refresh(); // unbind and set TransactionManager to null, so as to // initialize TXManager correctly if the cache is being restarted transactionManager = null; try { if (ctx != null) { ctx.unbind("java:/TransactionManager"); } } catch (NamingException e) { // ok to ignore, rebind will be tried later } } int len = dataSourceList.size(); for (int i = 0; i < len; i++) { if (dataSourceList.get(i) instanceof AbstractDataSource) ((AbstractDataSource) dataSourceList.get(i)).clearUp(); else if (dataSourceList.get(i) instanceof ClientConnectionFactoryWrapper) { ((ClientConnectionFactoryWrapper) dataSourceList.get(i)).clearUp(); } } dataSourceList.clear(); IGNORE_JTA = Boolean.getBoolean(DistributionConfig.GEMFIRE_PREFIX + "ignoreJTA"); } /* * Helps in locating TransactionManager in presence of application server scenario. Stores the * value of TransactionManager for reference in GemFire system. */ private static void doTransactionLookup() throws NamingException { Object jndiObject = null; LogWriterI18n writer = TransactionUtils.getLogWriterI18n(); for (int i = 0; i < knownJNDIManagers.length; i++) { try { jndiObject = ctx.lookup(knownJNDIManagers[i][0]); } catch (NamingException e) { String exception = "JNDIInvoker::doTransactionLookup::Couldn't lookup [" + knownJNDIManagers[i][0] + " (" + knownJNDIManagers[i][1] + ")]"; if (writer.finerEnabled()) writer.finer(exception); } if (jndiObject instanceof TransactionManager) { transactionManager = (TransactionManager) jndiObject; String exception = "JNDIInvoker::doTransactionLookup::Found TransactionManager for " + knownJNDIManagers[i][1]; if (writer.fineEnabled()) writer.fine(exception); return; } else { String exception = "JNDIInvoker::doTransactionLookup::Found TransactionManager of class " + (jndiObject == null ? "null" : jndiObject.getClass()) + " but is not of type javax.transaction.TransactionManager"; if (writer.fineEnabled()) writer.fine(exception); } } Class clazz; try { if (writer.finerEnabled()) writer.finer( "JNDIInvoker::doTransactionLookup::Trying WebSphere 5.1: " + WS_FACTORY_CLASS_5_1); clazz = ClassPathLoader.getLatest().forName(WS_FACTORY_CLASS_5_1); if (writer.fineEnabled()) writer .fine("JNDIInvoker::doTransactionLookup::Found WebSphere 5.1: " + WS_FACTORY_CLASS_5_1); } catch (ClassNotFoundException ex) { try { if (writer.finerEnabled()) writer.finer( "JNDIInvoker::doTransactionLookup::Trying WebSphere 5.0: " + WS_FACTORY_CLASS_5_0); clazz = ClassPathLoader.getLatest().forName(WS_FACTORY_CLASS_5_0); if (writer.fineEnabled()) writer.fine( "JNDIInvoker::doTransactionLookup::Found WebSphere 5.0: " + WS_FACTORY_CLASS_5_0); } catch (ClassNotFoundException ex2) { try { clazz = ClassPathLoader.getLatest().forName(WS_FACTORY_CLASS_4); String exception = "JNDIInvoker::doTransactionLookup::Found WebSphere 4: " + WS_FACTORY_CLASS_4; if (writer.fineEnabled()) writer.fine(exception, ex); } catch (ClassNotFoundException ex3) { if (writer.finerEnabled()) writer.finer( "JNDIInvoker::doTransactionLookup::Couldn't find any WebSphere TransactionManager factory class, neither for WebSphere version 5.1 nor 5.0 nor 4"); throw new NoInitialContextException(); } } } try { Method method = clazz.getMethod("getTransactionManager", (Class[]) null); transactionManager = (TransactionManager) method.invoke(null, (Object[]) null); } catch (Exception ex) { writer.warning( LocalizedStrings.JNDIInvoker_JNDIINVOKER_DOTRANSACTIONLOOKUP_FOUND_WEBSPHERE_TRANSACTIONMANAGER_FACTORY_CLASS_0_BUT_COULDNT_INVOKE_ITS_STATIC_GETTRANSACTIONMANAGER_METHOD, clazz.getName(), ex); throw new NameNotFoundException( LocalizedStrings.JNDIInvoker_JNDIINVOKER_DOTRANSACTIONLOOKUP_FOUND_WEBSPHERE_TRANSACTIONMANAGER_FACTORY_CLASS_0_BUT_COULDNT_INVOKE_ITS_STATIC_GETTRANSACTIONMANAGER_METHOD .toLocalizedString(new Object[] {clazz.getName()})); } } /** * Initialises the GemFire context. This is called when no external JNDI Context is found. * * @throws NamingException */ private static void initializeGemFireContext() throws NamingException { Hashtable table = new Hashtable(); table.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.geode.internal.jndi.InitialContextFactoryImpl"); ctx = new InitialContext(table); } /** * Binds a single Datasource to the existing JNDI tree. The JNDI tree may be The Datasource * properties are contained in the map. The Datasource implementation class is populated based on * properties in the map. * * @param map contains Datasource configuration properties. */ public static void mapDatasource(Map map, List props) { String value = (String) map.get("type"); String jndiName = ""; LogWriterI18n writer = TransactionUtils.getLogWriterI18n(); Object ds = null; try { jndiName = (String) map.get("jndi-name"); if (value.equals("PooledDataSource")) { ds = DataSourceFactory.getPooledDataSource(map, props); ctx.rebind("java:/" + jndiName, ds); dataSourceList.add(ds); if (writer.fineEnabled()) writer.fine("Bound java:/" + jndiName + " to Context"); } else if (value.equals("XAPooledDataSource")) { ds = DataSourceFactory.getTranxDataSource(map, props); ctx.rebind("java:/" + jndiName, ds); dataSourceList.add(ds); if (writer.fineEnabled()) writer.fine("Bound java:/" + jndiName + " to Context"); } else if (value.equals("SimpleDataSource")) { ds = DataSourceFactory.getSimpleDataSource(map, props); ctx.rebind("java:/" + jndiName, ds); if (writer.fineEnabled()) writer.fine("Bound java:/" + jndiName + " to Context"); } else if (value.equals("ManagedDataSource")) { ClientConnectionFactoryWrapper ds1 = DataSourceFactory.getManagedDataSource(map, props); ctx.rebind("java:/" + jndiName, ds1.getClientConnFactory()); dataSourceList.add(ds1); if (writer.fineEnabled()) writer.fine("Bound java:/" + jndiName + " to Context"); } else { String exception = "JNDIInvoker::mapDataSource::No correct type of DataSource"; if (writer.fineEnabled()) writer.fine(exception); throw new DataSourceCreateException(exception); } ds = null; } catch (NamingException ne) { if (writer.infoEnabled()) writer.info( LocalizedStrings.JNDIInvoker_JNDIINVOKER_MAPDATASOURCE_0_WHILE_BINDING_1_TO_JNDI_CONTEXT, new Object[] {"NamingException", jndiName}); } catch (DataSourceCreateException dsce) { if (writer.infoEnabled()) writer.info( LocalizedStrings.JNDIInvoker_JNDIINVOKER_MAPDATASOURCE_0_WHILE_BINDING_1_TO_JNDI_CONTEXT, new Object[] {"DataSourceCreateException", jndiName}); } } /** * @return Context the existing JNDI Context. If there is no pre-esisting JNDI Context, the * GemFire JNDI Context is returned. */ public static Context getJNDIContext() { return ctx; } /** * returns the GemFire TransactionManager. * * @return TransactionManager */ public static TransactionManager getTransactionManager() { return transactionManager; } // try to find websphere lookups since we came here /* * private static void print(String str) { if (DEBUG) { System.err.println(str); } } */ }