001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.activemq.pool; 018 019 import java.io.IOException; 020 021 import javax.jms.ConnectionFactory; 022 import javax.jms.Session; 023 import javax.jms.JMSException; 024 import javax.transaction.TransactionManager; 025 026 import org.slf4j.Logger; 027 import org.slf4j.LoggerFactory; 028 import org.apache.activemq.ActiveMQConnectionFactory; 029 import org.apache.activemq.ActiveMQConnection; 030 import org.apache.activemq.ActiveMQSession; 031 import org.apache.activemq.util.IOExceptionSupport; 032 import org.apache.geronimo.transaction.manager.RecoverableTransactionManager; 033 import org.apache.geronimo.transaction.manager.NamedXAResource; 034 import org.apache.geronimo.transaction.manager.WrapperNamedXAResource; 035 036 037 /** 038 * This class allows wiring the ActiveMQ broker and the Geronimo transaction manager 039 * in a way that will allow the transaction manager to correctly recover XA transactions. 040 * 041 * For example, it can be used the following way: 042 * <pre> 043 * <bean id="activemqConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> 044 * <property name="brokerURL" value="tcp://localhost:61616" /> 045 * </bean> 046 * 047 * <bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactoryFactoryBean"> 048 * <property name="maxConnections" value="8" /> 049 * <property name="transactionManager" ref="transactionManager" /> 050 * <property name="connectionFactory" ref="activemqConnectionFactory" /> 051 * <property name="resourceName" value="activemq.broker" /> 052 * </bean> 053 * 054 * <bean id="resourceManager" class="org.apache.activemq.pool.ActiveMQResourceManager" init-method="recoverResource"> 055 * <property name="transactionManager" ref="transactionManager" /> 056 * <property name="connectionFactory" ref="activemqConnectionFactory" /> 057 * <property name="resourceName" value="activemq.broker" /> 058 * </bean> 059 * </pre> 060 */ 061 public class ActiveMQResourceManager { 062 063 private static final Logger LOGGER = LoggerFactory.getLogger(ActiveMQResourceManager.class); 064 065 private String resourceName; 066 067 private TransactionManager transactionManager; 068 069 private ConnectionFactory connectionFactory; 070 071 public void recoverResource() { 072 try { 073 if (!Recovery.recover(this)) { 074 LOGGER.info("Resource manager is unrecoverable"); 075 } 076 } catch (NoClassDefFoundError e) { 077 LOGGER.info("Resource manager is unrecoverable due to missing classes: " + e); 078 } catch (Throwable e) { 079 LOGGER.warn("Error while recovering resource manager", e); 080 } 081 } 082 083 public String getResourceName() { 084 return resourceName; 085 } 086 087 public void setResourceName(String resourceName) { 088 this.resourceName = resourceName; 089 } 090 091 public TransactionManager getTransactionManager() { 092 return transactionManager; 093 } 094 095 public void setTransactionManager(TransactionManager transactionManager) { 096 this.transactionManager = transactionManager; 097 } 098 099 public ConnectionFactory getConnectionFactory() { 100 return connectionFactory; 101 } 102 103 public void setConnectionFactory(ConnectionFactory connectionFactory) { 104 this.connectionFactory = connectionFactory; 105 } 106 107 /** 108 * This class will ensure the broker is properly recovered when wired with 109 * the Geronimo transaction manager. 110 */ 111 public static class Recovery { 112 113 public static boolean isRecoverable(ActiveMQResourceManager rm) { 114 return rm.getConnectionFactory() instanceof ActiveMQConnectionFactory && 115 rm.getTransactionManager() instanceof RecoverableTransactionManager && 116 rm.getResourceName() != null && !"".equals(rm.getResourceName()); 117 } 118 119 public static boolean recover(ActiveMQResourceManager rm) throws IOException { 120 if (isRecoverable(rm)) { 121 try { 122 ActiveMQConnectionFactory connFactory = (ActiveMQConnectionFactory) rm.getConnectionFactory(); 123 ActiveMQConnection activeConn = (ActiveMQConnection)connFactory.createConnection(); 124 ActiveMQSession session = (ActiveMQSession)activeConn.createSession(true, Session.SESSION_TRANSACTED); 125 NamedXAResource namedXaResource = new WrapperNamedXAResource(session.getTransactionContext(), rm.getResourceName()); 126 127 RecoverableTransactionManager rtxManager = (RecoverableTransactionManager) rm.getTransactionManager(); 128 rtxManager.recoverResourceManager(namedXaResource); 129 return true; 130 } catch (JMSException e) { 131 throw IOExceptionSupport.create(e); 132 } 133 } else { 134 return false; 135 } 136 } 137 } 138 139 }