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 */
017package org.apache.activemq.pool;
018
019import java.io.IOException;
020
021import javax.jms.ConnectionFactory;
022import javax.jms.Session;
023import javax.jms.JMSException;
024import javax.transaction.TransactionManager;
025
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028import org.apache.activemq.ActiveMQConnectionFactory;
029import org.apache.activemq.ActiveMQConnection;
030import org.apache.activemq.ActiveMQSession;
031import org.apache.activemq.util.IOExceptionSupport;
032import org.apache.geronimo.transaction.manager.RecoverableTransactionManager;
033import org.apache.geronimo.transaction.manager.NamedXAResource;
034import 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 */
061public 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}