001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.jms.connection;
018:
019: import javax.jms.Connection;
020: import javax.jms.ConnectionFactory;
021: import javax.jms.JMSException;
022: import javax.jms.QueueConnection;
023: import javax.jms.QueueConnectionFactory;
024: import javax.jms.TopicConnection;
025: import javax.jms.TopicConnectionFactory;
026:
027: import org.springframework.beans.factory.InitializingBean;
028: import org.springframework.util.Assert;
029: import org.springframework.util.StringUtils;
030:
031: /**
032: * An adapter for a target JMS {@link javax.jms.ConnectionFactory}, applying the
033: * given user credentials to every standard <code>createConnection()</code> call,
034: * that is, implicitly invoking <code>createConnection(username, password)</code>
035: * on the target. All other methods simply delegate to the corresponding methods
036: * of the target ConnectionFactory.
037: *
038: * <p>Can be used to proxy a target JNDI ConnectionFactory that does not have user
039: * credentials configured. Client code can work with the ConnectionFactory without
040: * passing in username and password on every <code>createConnection()</code> call.
041: *
042: * <p>In the following example, client code can simply transparently work
043: * with the preconfigured "myConnectionFactory", implicitly accessing
044: * "myTargetConnectionFactory" with the specified user credentials.
045: *
046: * <pre class="code">
047: * <bean id="myTargetConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
048: * <property name="jndiName" value="java:comp/env/jms/mycf"/>
049: * </bean>
050: *
051: * <bean id="myConnectionFactory" class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
052: * <property name="targetConnectionFactory" ref="myTargetConnectionFactory"/>
053: * <property name="username" value="myusername"/>
054: * <property name="password" value="mypassword"/>
055: * </bean></pre>
056: *
057: * <p>If the "username" is empty, this proxy will simply delegate to the standard
058: * <code>createConnection()</code> method of the target ConnectionFactory.
059: * This can be used to keep a UserCredentialsConnectionFactoryAdapter bean
060: * definition just for the <i>option</i> of implicitly passing in user credentials
061: * if the particular target ConnectionFactory requires it.
062: *
063: * @author Juergen Hoeller
064: * @since 1.2
065: * @see #createConnection
066: * @see #createQueueConnection
067: * @see #createTopicConnection
068: */
069: public class UserCredentialsConnectionFactoryAdapter implements
070: ConnectionFactory, QueueConnectionFactory,
071: TopicConnectionFactory, InitializingBean {
072:
073: private ConnectionFactory targetConnectionFactory;
074:
075: private String username;
076:
077: private String password;
078:
079: private final ThreadLocal threadBoundCredentials = new ThreadLocal();
080:
081: /**
082: * Set the target ConnectionFactory that this ConnectionFactory should delegate to.
083: */
084: public void setTargetConnectionFactory(
085: ConnectionFactory targetConnectionFactory) {
086: Assert.notNull(targetConnectionFactory,
087: "'targetConnectionFactory' must not be null");
088: this .targetConnectionFactory = targetConnectionFactory;
089: }
090:
091: /**
092: * Set the username that this adapter should use for retrieving Connections.
093: * Default is no specific user.
094: */
095: public void setUsername(String username) {
096: this .username = username;
097: }
098:
099: /**
100: * Set the password that this adapter should use for retrieving Connections.
101: * Default is no specific password.
102: */
103: public void setPassword(String password) {
104: this .password = password;
105: }
106:
107: public void afterPropertiesSet() {
108: if (this .targetConnectionFactory == null) {
109: throw new IllegalArgumentException(
110: "Property 'targetConnectionFactory' is required");
111: }
112: }
113:
114: /**
115: * Set user credententials for this proxy and the current thread.
116: * The given username and password will be applied to all subsequent
117: * <code>createConnection()</code> calls on this ConnectionFactory proxy.
118: * <p>This will override any statically specified user credentials,
119: * that is, values of the "username" and "password" bean properties.
120: * @param username the username to apply
121: * @param password the password to apply
122: * @see #removeCredentialsFromCurrentThread
123: */
124: public void setCredentialsForCurrentThread(String username,
125: String password) {
126: this .threadBoundCredentials.set(new String[] { username,
127: password });
128: }
129:
130: /**
131: * Remove any user credentials for this proxy from the current thread.
132: * Statically specified user credentials apply again afterwards.
133: * @see #setCredentialsForCurrentThread
134: */
135: public void removeCredentialsFromCurrentThread() {
136: this .threadBoundCredentials.set(null);
137: }
138:
139: /**
140: * Determine whether there are currently thread-bound credentials,
141: * using them if available, falling back to the statically specified
142: * username and password (i.e. values of the bean properties) else.
143: * @see #doCreateConnection
144: */
145: public final Connection createConnection() throws JMSException {
146: String[] threadCredentials = (String[]) this .threadBoundCredentials
147: .get();
148: if (threadCredentials != null) {
149: return doCreateConnection(threadCredentials[0],
150: threadCredentials[1]);
151: } else {
152: return doCreateConnection(this .username, this .password);
153: }
154: }
155:
156: /**
157: * Delegate the call straight to the target ConnectionFactory.
158: */
159: public Connection createConnection(String username, String password)
160: throws JMSException {
161: return doCreateConnection(username, password);
162: }
163:
164: /**
165: * This implementation delegates to the <code>createConnection(username, password)</code>
166: * method of the target ConnectionFactory, passing in the specified user credentials.
167: * If the specified username is empty, it will simply delegate to the standard
168: * <code>createConnection()</code> method of the target ConnectionFactory.
169: * @param username the username to use
170: * @param password the password to use
171: * @return the Connection
172: * @see javax.jms.ConnectionFactory#createConnection(String, String)
173: * @see javax.jms.ConnectionFactory#createConnection()
174: */
175: protected Connection doCreateConnection(String username,
176: String password) throws JMSException {
177: Assert.state(this .targetConnectionFactory != null,
178: "'targetConnectionFactory' is required");
179: if (StringUtils.hasLength(username)) {
180: return this .targetConnectionFactory.createConnection(
181: username, password);
182: } else {
183: return this .targetConnectionFactory.createConnection();
184: }
185: }
186:
187: /**
188: * Determine whether there are currently thread-bound credentials,
189: * using them if available, falling back to the statically specified
190: * username and password (i.e. values of the bean properties) else.
191: * @see #doCreateQueueConnection
192: */
193: public final QueueConnection createQueueConnection()
194: throws JMSException {
195: String[] threadCredentials = (String[]) this .threadBoundCredentials
196: .get();
197: if (threadCredentials != null) {
198: return doCreateQueueConnection(threadCredentials[0],
199: threadCredentials[1]);
200: } else {
201: return doCreateQueueConnection(this .username, this .password);
202: }
203: }
204:
205: /**
206: * Delegate the call straight to the target QueueConnectionFactory.
207: */
208: public QueueConnection createQueueConnection(String username,
209: String password) throws JMSException {
210: return doCreateQueueConnection(username, password);
211: }
212:
213: /**
214: * This implementation delegates to the <code>createQueueConnection(username, password)</code>
215: * method of the target QueueConnectionFactory, passing in the specified user credentials.
216: * If the specified username is empty, it will simply delegate to the standard
217: * <code>createQueueConnection()</code> method of the target ConnectionFactory.
218: * @param username the username to use
219: * @param password the password to use
220: * @return the Connection
221: * @see javax.jms.QueueConnectionFactory#createQueueConnection(String, String)
222: * @see javax.jms.QueueConnectionFactory#createQueueConnection()
223: */
224: protected QueueConnection doCreateQueueConnection(String username,
225: String password) throws JMSException {
226: Assert.state(this .targetConnectionFactory != null,
227: "'targetConnectionFactory' is required");
228: if (!(this .targetConnectionFactory instanceof QueueConnectionFactory)) {
229: throw new javax.jms.IllegalStateException(
230: "'targetConnectionFactory' is not a QueueConnectionFactory");
231: }
232: QueueConnectionFactory queueFactory = (QueueConnectionFactory) this .targetConnectionFactory;
233: if (StringUtils.hasLength(username)) {
234: return queueFactory.createQueueConnection(username,
235: password);
236: } else {
237: return queueFactory.createQueueConnection();
238: }
239: }
240:
241: /**
242: * Determine whether there are currently thread-bound credentials,
243: * using them if available, falling back to the statically specified
244: * username and password (i.e. values of the bean properties) else.
245: * @see #doCreateTopicConnection
246: */
247: public final TopicConnection createTopicConnection()
248: throws JMSException {
249: String[] threadCredentials = (String[]) this .threadBoundCredentials
250: .get();
251: if (threadCredentials != null) {
252: return doCreateTopicConnection(threadCredentials[0],
253: threadCredentials[1]);
254: } else {
255: return doCreateTopicConnection(this .username, this .password);
256: }
257: }
258:
259: /**
260: * Delegate the call straight to the target TopicConnectionFactory.
261: */
262: public TopicConnection createTopicConnection(String username,
263: String password) throws JMSException {
264: return doCreateTopicConnection(username, password);
265: }
266:
267: /**
268: * This implementation delegates to the <code>createTopicConnection(username, password)</code>
269: * method of the target TopicConnectionFactory, passing in the specified user credentials.
270: * If the specified username is empty, it will simply delegate to the standard
271: * <code>createTopicConnection()</code> method of the target ConnectionFactory.
272: * @param username the username to use
273: * @param password the password to use
274: * @return the Connection
275: * @see javax.jms.TopicConnectionFactory#createTopicConnection(String, String)
276: * @see javax.jms.TopicConnectionFactory#createTopicConnection()
277: */
278: protected TopicConnection doCreateTopicConnection(String username,
279: String password) throws JMSException {
280: Assert.state(this .targetConnectionFactory != null,
281: "'targetConnectionFactory' is required");
282: if (!(this .targetConnectionFactory instanceof TopicConnectionFactory)) {
283: throw new javax.jms.IllegalStateException(
284: "'targetConnectionFactory' is not a TopicConnectionFactory");
285: }
286: TopicConnectionFactory queueFactory = (TopicConnectionFactory) this.targetConnectionFactory;
287: if (StringUtils.hasLength(username)) {
288: return queueFactory.createTopicConnection(username,
289: password);
290: } else {
291: return queueFactory.createTopicConnection();
292: }
293: }
294:
295: }
|