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.jdbc.datasource;
018:
019: import java.sql.Connection;
020: import java.sql.SQLException;
021:
022: import org.springframework.util.Assert;
023: import org.springframework.util.StringUtils;
024:
025: /**
026: * An adapter for a target JDBC {@link javax.sql.DataSource}, applying the specified
027: * user credentials to every standard <code>getConnection()</code> call, implicitly
028: * invoking <code>getConnection(username, password)</code> on the target.
029: * All other methods simply delegate to the corresponding methods of the
030: * target DataSource.
031: *
032: * <p>Can be used to proxy a target JNDI DataSource that does not have user
033: * credentials configured. Client code can work with this DataSource as usual,
034: * using the standard <code>getConnection()</code> call.
035: *
036: * <p>In the following example, client code can simply transparently work with
037: * the preconfigured "myDataSource", implicitly accessing "myTargetDataSource"
038: * with the specified user credentials.
039: *
040: * <pre class="code">
041: * <bean id="myTargetDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
042: * <property name="jndiName" value="java:comp/env/jdbc/myds"/>
043: * </bean>
044: *
045: * <bean id="myDataSource" class="org.springframework.jdbc.datasource.UserCredentialsDataSourceAdapter">
046: * <property name="targetDataSource" ref="myTargetDataSource"/>
047: * <property name="username" value="myusername"/>
048: * <property name="password" value="mypassword"/>
049: * </bean></pre>
050: *
051: * <p>If the "username" is empty, this proxy will simply delegate to the
052: * standard <code>getConnection()</code> method of the target DataSource.
053: * This can be used to keep a UserCredentialsDataSourceAdapter bean definition
054: * just for the <i>option</i> of implicitly passing in user credentials if
055: * the particular target DataSource requires it.
056: *
057: * @author Juergen Hoeller
058: * @since 1.0.2
059: * @see #getConnection
060: */
061: public class UserCredentialsDataSourceAdapter extends
062: DelegatingDataSource {
063:
064: private String username;
065:
066: private String password;
067:
068: private final ThreadLocal threadBoundCredentials = new ThreadLocal();
069:
070: /**
071: * Set the default username that this adapter should use for retrieving Connections.
072: * <p>Default is no specific user. Note that an explicitly specified username
073: * will always override any username/password specified at the DataSource level.
074: * @see #setPassword
075: * @see #setCredentialsForCurrentThread(String, String)
076: * @see #getConnection(String, String)
077: */
078: public void setUsername(String username) {
079: this .username = username;
080: }
081:
082: /**
083: * Set the default user's password that this adapter should use for retrieving Connections.
084: * <p>Default is no specific password. Note that an explicitly specified username
085: * will always override any username/password specified at the DataSource level.
086: * @see #setUsername
087: * @see #setCredentialsForCurrentThread(String, String)
088: * @see #getConnection(String, String)
089: */
090: public void setPassword(String password) {
091: this .password = password;
092: }
093:
094: /**
095: * Set user credententials for this proxy and the current thread.
096: * The given username and password will be applied to all subsequent
097: * <code>getConnection()</code> calls on this DataSource proxy.
098: * <p>This will override any statically specified user credentials,
099: * that is, values of the "username" and "password" bean properties.
100: * @param username the username to apply
101: * @param password the password to apply
102: * @see #removeCredentialsFromCurrentThread
103: */
104: public void setCredentialsForCurrentThread(String username,
105: String password) {
106: this .threadBoundCredentials.set(new String[] { username,
107: password });
108: }
109:
110: /**
111: * Remove any user credentials for this proxy from the current thread.
112: * Statically specified user credentials apply again afterwards.
113: * @see #setCredentialsForCurrentThread
114: */
115: public void removeCredentialsFromCurrentThread() {
116: this .threadBoundCredentials.set(null);
117: }
118:
119: /**
120: * Determine whether there are currently thread-bound credentials,
121: * using them if available, falling back to the statically specified
122: * username and password (i.e. values of the bean properties) else.
123: * <p>Delegates to {@link #doGetConnection(String, String)} with the
124: * determined credentials as parameters.
125: */
126: public Connection getConnection() throws SQLException {
127: String[] threadCredentials = (String[]) this .threadBoundCredentials
128: .get();
129: if (threadCredentials != null) {
130: return doGetConnection(threadCredentials[0],
131: threadCredentials[1]);
132: } else {
133: return doGetConnection(this .username, this .password);
134: }
135: }
136:
137: /**
138: * Simply delegates to {@link #doGetConnection(String, String)},
139: * keeping the given user credentials as-is.
140: */
141: public Connection getConnection(String username, String password)
142: throws SQLException {
143: return doGetConnection(username, password);
144: }
145:
146: /**
147: * This implementation delegates to the <code>getConnection(username, password)</code>
148: * method of the target DataSource, passing in the specified user credentials.
149: * If the specified username is empty, it will simply delegate to the standard
150: * <code>getConnection()</code> method of the target DataSource.
151: * @param username the username to use
152: * @param password the password to use
153: * @return the Connection
154: * @see javax.sql.DataSource#getConnection(String, String)
155: * @see javax.sql.DataSource#getConnection()
156: */
157: protected Connection doGetConnection(String username,
158: String password) throws SQLException {
159: Assert.state(getTargetDataSource() != null,
160: "'targetDataSource' is required");
161: if (StringUtils.hasLength(username)) {
162: return getTargetDataSource().getConnection(username,
163: password);
164: } else {
165: return getTargetDataSource().getConnection();
166: }
167: }
168:
169: }
|