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    }