001: /*
002: * The contents of this file are subject to the Sapient Public License
003: * Version 1.0 (the "License"); you may not use this file except in compliance
004: * with the License. You may obtain a copy of the License at
005: * http://carbon.sf.net/License.html.
006: *
007: * Software distributed under the License is distributed on an "AS IS" basis,
008: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
009: * the specific language governing rights and limitations under the License.
010: *
011: * The Original Code is The Carbon Component Framework.
012: *
013: * The Initial Developer of the Original Code is Sapient Corporation
014: *
015: * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
016: */
017:
018: package org.sape.carbon.services.sql.connection;
019:
020: import java.io.PrintWriter;
021: import java.sql.Connection;
022: import java.sql.SQLException;
023: import java.util.Hashtable;
024:
025: import javax.naming.Context;
026: import javax.naming.InitialContext;
027: import javax.naming.NamingException;
028: import javax.sql.DataSource;
029:
030: import org.sape.carbon.core.component.ComponentConfiguration;
031: import org.sape.carbon.core.component.lifecycle.Configurable;
032: import org.sape.carbon.core.component.lifecycle.StateTransitionException;
033:
034: import org.apache.commons.logging.Log;
035: import org.apache.commons.logging.LogFactory;
036:
037: /**
038: * Decorator Component for a JNDI DataSource. This component should be used as
039: * a <code>javax.sql.DataSource<code>.
040: * On configuration, obtains a JDBC DataSource from JNDI and delegates
041: * requests for connections to it. Note that the DataSource instance is
042: * discarded and re-fetched each time this component is configured.
043: *
044: * Copyright 2002 Sapient
045: * @since carbon 1.0
046: * @author Chris Herron, March 2002
047: * @version $Revision: 1.12 $($Author: dvoet $ / $Date: 2003/05/05 21:21:36 $)
048: */
049: public class NamedDataSourceConnectionFactory implements
050: DataSourceConnectionFactory, Configurable {
051: /**
052: * <p>Stores a reference to the data source retrieve from JNDI. Prevents
053: * further calls to JNDI, thus improving performance on acquiring
054: * connections.</p>
055: */
056: protected DataSource dataSource = null;
057: /**
058: * <p>Stores the (optional) userid used to create the connection from the
059: * managed DataSource object.</p>
060: */
061: protected String dataSourceUserId = null;
062:
063: /**
064: * <p>Stores the (optional) password used to create the connection from the
065: * managed DataSource object.</p>
066: */
067: protected String dataSourcePassword = null;
068:
069: /**
070: * Provides a handle to Apache-commons logger
071: */
072: private Log log = LogFactory.getLog(this .getClass());
073:
074: /**
075: * Delegates to the underlying DataSource to obtain a JDBC connection.
076: * If there is a configured dataSourceUserId, uses
077: * <code>this.getConnection(dataSourceUserId, dataSourcePassword)</code> to
078: * to obtain a JDBC connection. If no dataSourceUserId is configured, uses
079: * <code>DataSource.getConnection()</code>.
080: *
081: * @return Connection A JDBC Connection
082: * @throws SQLException When obtaining connection fails
083: */
084: public Connection getConnection() throws SQLException {
085:
086: if (log.isTraceEnabled()) {
087: log.trace("Obtaining JDBC Connection");
088: }
089:
090: Connection conn = null;
091: //TODO: remove empty string check, since this may be intentional
092: //TODO: config currently returns empty string when it should be null
093: if (null == this .dataSourceUserId
094: || this .dataSourceUserId.equals("")) {
095:
096: conn = dataSource.getConnection();
097: } else {
098: conn = this .getConnection(this .dataSourceUserId,
099: this .dataSourcePassword);
100: }
101: return conn;
102: }
103:
104: /**
105: * Delegates to the underlying DataSource to obtain a JDBC connection.
106: * Overrides the dataSourceUserId and dataSourcePassword, if configured.
107: *
108: * @param overridingDataSourceUserId The overriding dataSourceUserId
109: * @param overridingDataSourcePassword The overriding dataSourcePassword
110: * @return Connection A JDBC Connection
111: * @throws SQLException When obtaining connection fails
112: */
113: public Connection getConnection(String overridingDataSourceUserId,
114: String overridingDataSourcePassword) throws SQLException {
115:
116: if (log.isTraceEnabled()) {
117: log.trace("Obtaining JDBC Connection. "
118: + "(Overriding DataSource UserId and Password)");
119: }
120:
121: Connection conn = dataSource.getConnection(
122: overridingDataSourceUserId,
123: overridingDataSourcePassword);
124: return conn;
125: }
126:
127: /**
128: * Delegates to {@link DataSource#getLogWriter}.
129: * @see DataSource#getLogWriter
130: */
131: public PrintWriter getLogWriter() throws SQLException {
132: return dataSource.getLogWriter();
133: }
134:
135: /**
136: * Delegates to {@link DataSource#getLoginTimeout}.
137: * @see DataSource#getLoginTimeout
138: */
139: public int getLoginTimeout() throws SQLException {
140: return dataSource.getLoginTimeout();
141: }
142:
143: /**
144: * Delegates to {@link DataSource#setLogWriter}.
145: * @see DataSource#setLogWriter
146: */
147: public void setLogWriter(PrintWriter printWriter)
148: throws SQLException {
149: dataSource.setLogWriter(printWriter);
150: }
151:
152: /**
153: * Delegates to {@link DataSource#setLoginTimeout}.
154: * @see DataSource#setLoginTimeout
155: */
156: public void setLoginTimeout(int param) throws SQLException {
157: dataSource.setLoginTimeout(param);
158: }
159:
160: /**
161: * Obtains the DataSource and necessary connection properties.
162: * Note that the DataSource is replaced each time the component
163: * is configured.
164: *
165: * @param configuration An instance of
166: * NamedDataSourceConnectionFactoryConfiguration
167: */
168: public void configure(ComponentConfiguration configuration) {
169: NamedDataSourceConnectionFactoryConfiguration config = (NamedDataSourceConnectionFactoryConfiguration) configuration;
170:
171: //JNDI location of the DataSource
172: String jndiName = config.getJndiName();
173: //Class of the InitialContextFactory
174: String initialContextFactory = config
175: .getInitialContextFactory();
176: //(optional) URL of the JNDI provider
177: String providerUrl = config.getProviderUrl();
178: //(optional) userid used to authenticate to the JNDI server
179: String jndiUserId = config.getJndiUserId();
180: //(optional) password used to authenticate to the JNDI server
181: String jndiPassword = config.getJndiPassword();
182: //(optional) DataSource userid
183: this .dataSourceUserId = config.getDataSourceUserId();
184: //(optional) DataSource password
185: this .dataSourcePassword = config.getDataSourcePassword();
186:
187: try {
188: //Obtain an InitialContext to connect to JNDI
189: if (log.isTraceEnabled()) {
190: log.trace("Fetching Initial Context");
191: }
192: Context ic = fetchInitialContext(initialContextFactory,
193: providerUrl, jndiUserId, jndiPassword);
194:
195: //Obtain the DataSource from JNDI
196: if (log.isTraceEnabled()) {
197: log.trace("Obtaining DataSource from:" + "[" + jndiName
198: + "]");
199: }
200:
201: this .dataSource = (DataSource) ic.lookup(jndiName);
202:
203: } catch (NamingException ne) {
204: throw new StateTransitionException(this .getClass(),
205: "Error Obtaining DataSource: [" + jndiName + "]"
206: + "from JNDI service: [" + providerUrl
207: + "]", ne);
208: }
209: }
210:
211: /**
212: * <p>Helper method that creates the InitialContext onto the JNDI tree based
213: * upon the configured properties.</p>
214: *
215: * @param initialContextFactory String representation of the class name to
216: * be used as the <code>InitialContextFactory</code>; this
217: * parameter is analagous to
218: * <code>Context.INITIAL_CONTEXT_FACTORY</code>
219: * @param providerUrl The JNDI provider URL as a Stringl this parameter is
220: * analagous to <code>Context.PROVIDER_URL</code>
221: * @param jndiUserId The security principal as a String; this parameter is
222: * analagous to <code>Context.SECURITY_PRINCIPAL</code>
223: * @param jndiPassword The credentials associated with the security
224: * principal as a String; this parameter is analagous to the
225: * <code>Context.SECURITY_CREDENTIALS</code>
226: * @return a new Context based on the given environment.
227: *
228: * @throws NamingException indicates an error building the Context based
229: * on the given environment. */
230: protected Context fetchInitialContext(String initialContextFactory,
231: String providerUrl, String jndiUserId, String jndiPassword)
232: throws NamingException {
233: // Local variables to hold the Context and parameters for creating it
234: Context context = null;
235: Hashtable environment = new Hashtable();
236:
237: // Go through our four possible parameters for creating the
238: // InitialContext and add all the non-null ones to the environment
239: // Hashtable.
240: if (null != initialContextFactory) {
241: environment.put(Context.INITIAL_CONTEXT_FACTORY,
242: initialContextFactory);
243: }
244: if (null != providerUrl) {
245: environment.put(Context.PROVIDER_URL, providerUrl);
246: }
247: if (null != jndiUserId) {
248: environment.put(Context.SECURITY_PRINCIPAL, jndiUserId);
249: }
250: if (null != jndiPassword) {
251: environment.put(Context.SECURITY_CREDENTIALS, jndiPassword);
252: }
253: // If there were any parameters then we call the factory method with the
254: // parameters, otherwise just call the no-arg factory method.
255: if (environment.size() > 0) {
256: //TODO:
257: //Logger.detail(Logger.PRODUCER_CONNECTION,
258: // "NamedDataSourceManager.getInitialContext() fetching "
259: // + "initial context with environment parameters: "
260: // + parameters.toString());
261: context = new InitialContext(environment);
262: } else {
263: //TODO:
264: //Logger.detail(Logger.PRODUCER_CONNECTION,
265: // "NamedDataSourceManager.getInitialContext() fetching "
266: // + initial context with no environment parameters.");
267: context = new InitialContext();
268: }
269:
270: // Return the context that we made
271: return context;
272: }
273: }
|