001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.jms.connection;
018
019import javax.jms.Connection;
020import javax.jms.ConnectionFactory;
021import javax.jms.JMSContext;
022import javax.jms.JMSException;
023import javax.jms.QueueConnection;
024import javax.jms.QueueConnectionFactory;
025import javax.jms.TopicConnection;
026import javax.jms.TopicConnectionFactory;
027
028import org.springframework.beans.factory.InitializingBean;
029import org.springframework.lang.Nullable;
030import org.springframework.util.Assert;
031
032/**
033 * {@link javax.jms.ConnectionFactory} implementation that delegates all calls
034 * to a given target {@link javax.jms.ConnectionFactory}, adapting specific
035 * {@code create(Queue/Topic)Connection} calls to the target ConnectionFactory
036 * if necessary (e.g. when running JMS 1.0.2 API based code against a generic
037 * JMS 1.1 ConnectionFactory, such as ActiveMQ's PooledConnectionFactory).
038 *
039 * <p>As of Spring Framework 5, this class supports JMS 2.0 {@code JMSContext}
040 * calls and therefore requires the JMS 2.0 API to be present at runtime.
041 * It may nevertheless run against a JMS 1.1 driver (bound to the JMS 2.0 API)
042 * as long as no actual JMS 2.0 calls are triggered by the application's setup.
043 *
044 * <p>This class allows for being subclassed, with subclasses overriding only
045 * those methods (such as {@link #createConnection()}) that should not simply
046 * delegate to the target ConnectionFactory.
047 *
048 * <p>Can also be defined as-is, wrapping a specific target ConnectionFactory,
049 * using the "shouldStopConnections" flag to indicate whether Connections
050 * obtained from the target factory are supposed to be stopped before closed.
051 * The latter may be necessary for some connection pools that simply return
052 * released connections to the pool, not stopping them while they sit in the pool.
053 *
054 * @author Juergen Hoeller
055 * @since 2.0.2
056 * @see #createConnection()
057 * @see #setShouldStopConnections
058 * @see ConnectionFactoryUtils#releaseConnection
059 */
060public class DelegatingConnectionFactory
061                implements SmartConnectionFactory, QueueConnectionFactory, TopicConnectionFactory, InitializingBean {
062
063        @Nullable
064        private ConnectionFactory targetConnectionFactory;
065
066        private boolean shouldStopConnections = false;
067
068
069        /**
070         * Set the target ConnectionFactory that this ConnectionFactory should delegate to.
071         */
072        public void setTargetConnectionFactory(@Nullable ConnectionFactory targetConnectionFactory) {
073                this.targetConnectionFactory = targetConnectionFactory;
074        }
075
076        /**
077         * Return the target ConnectionFactory that this ConnectionFactory delegates to.
078         */
079        @Nullable
080        public ConnectionFactory getTargetConnectionFactory() {
081                return this.targetConnectionFactory;
082        }
083
084        private ConnectionFactory obtainTargetConnectionFactory() {
085                ConnectionFactory target = getTargetConnectionFactory();
086                Assert.state(target != null, "No 'targetConnectionFactory' set");
087                return target;
088        }
089
090        /**
091         * Indicate whether Connections obtained from the target factory are supposed
092         * to be stopped before closed ("true") or simply closed ("false").
093         * An extra stop call may be necessary for some connection pools that simply return
094         * released connections to the pool, not stopping them while they sit in the pool.
095         * <p>Default is "false", simply closing Connections.
096         * @see ConnectionFactoryUtils#releaseConnection
097         */
098        public void setShouldStopConnections(boolean shouldStopConnections) {
099                this.shouldStopConnections = shouldStopConnections;
100        }
101
102        @Override
103        public void afterPropertiesSet() {
104                if (getTargetConnectionFactory() == null) {
105                        throw new IllegalArgumentException("'targetConnectionFactory' is required");
106                }
107        }
108
109
110        @Override
111        public Connection createConnection() throws JMSException {
112                return obtainTargetConnectionFactory().createConnection();
113        }
114
115        @Override
116        public Connection createConnection(String username, String password) throws JMSException {
117                return obtainTargetConnectionFactory().createConnection(username, password);
118        }
119
120        @Override
121        public QueueConnection createQueueConnection() throws JMSException {
122                ConnectionFactory target = obtainTargetConnectionFactory();
123                if (target instanceof QueueConnectionFactory) {
124                        return ((QueueConnectionFactory) target).createQueueConnection();
125                }
126                else {
127                        Connection con = target.createConnection();
128                        if (!(con instanceof QueueConnection)) {
129                                throw new javax.jms.IllegalStateException("'targetConnectionFactory' is not a QueueConnectionFactory");
130                        }
131                        return (QueueConnection) con;
132                }
133        }
134
135        @Override
136        public QueueConnection createQueueConnection(String username, String password) throws JMSException {
137                ConnectionFactory target = obtainTargetConnectionFactory();
138                if (target instanceof QueueConnectionFactory) {
139                        return ((QueueConnectionFactory) target).createQueueConnection(username, password);
140                }
141                else {
142                        Connection con = target.createConnection(username, password);
143                        if (!(con instanceof QueueConnection)) {
144                                throw new javax.jms.IllegalStateException("'targetConnectionFactory' is not a QueueConnectionFactory");
145                        }
146                        return (QueueConnection) con;
147                }
148        }
149
150        @Override
151        public TopicConnection createTopicConnection() throws JMSException {
152                ConnectionFactory target = obtainTargetConnectionFactory();
153                if (target instanceof TopicConnectionFactory) {
154                        return ((TopicConnectionFactory) target).createTopicConnection();
155                }
156                else {
157                        Connection con = target.createConnection();
158                        if (!(con instanceof TopicConnection)) {
159                                throw new javax.jms.IllegalStateException("'targetConnectionFactory' is not a TopicConnectionFactory");
160                        }
161                        return (TopicConnection) con;
162                }
163        }
164
165        @Override
166        public TopicConnection createTopicConnection(String username, String password) throws JMSException {
167                ConnectionFactory target = obtainTargetConnectionFactory();
168                if (target instanceof TopicConnectionFactory) {
169                        return ((TopicConnectionFactory) target).createTopicConnection(username, password);
170                }
171                else {
172                        Connection con = target.createConnection(username, password);
173                        if (!(con instanceof TopicConnection)) {
174                                throw new javax.jms.IllegalStateException("'targetConnectionFactory' is not a TopicConnectionFactory");
175                        }
176                        return (TopicConnection) con;
177                }
178        }
179
180        @Override
181        public JMSContext createContext() {
182                return obtainTargetConnectionFactory().createContext();
183        }
184
185        @Override
186        public JMSContext createContext(String userName, String password) {
187                return obtainTargetConnectionFactory().createContext(userName, password);
188        }
189
190        @Override
191        public JMSContext createContext(String userName, String password, int sessionMode) {
192                return obtainTargetConnectionFactory().createContext(userName, password, sessionMode);
193        }
194
195        @Override
196        public JMSContext createContext(int sessionMode) {
197                return obtainTargetConnectionFactory().createContext(sessionMode);
198        }
199
200        @Override
201        public boolean shouldStop(Connection con) {
202                return this.shouldStopConnections;
203        }
204
205}