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.apache.commons.logging.Log;
027 import org.apache.commons.logging.LogFactory;
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 Log LOGGER = LogFactory.getLog(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 }