001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.resource.adapter.jdbc.local;
023:
024: import java.io.ByteArrayInputStream;
025: import java.io.IOException;
026: import java.io.InputStream;
027: import java.sql.Connection;
028: import java.sql.Driver;
029: import java.sql.DriverManager;
030: import java.util.Iterator;
031: import java.util.Properties;
032: import java.util.Set;
033:
034: import javax.resource.ResourceException;
035: import javax.resource.spi.ConnectionRequestInfo;
036: import javax.resource.spi.ManagedConnection;
037: import javax.security.auth.Subject;
038:
039: import org.jboss.resource.JBossResourceException;
040: import org.jboss.resource.adapter.jdbc.BaseWrapperManagedConnectionFactory;
041: import org.jboss.util.NestedRuntimeException;
042:
043: /**
044: * LocalManagedConnectionFactory
045: *
046: * @author <a href="mailto:d_jencks@users.sourceforge.net">David Jencks</a>
047: * @author <a href="mailto:adrian@jboss.com">Adrian Brock</a>
048: * @author <a href="weston.price@jboss.com">Weston Price</a>
049: *
050: * @version $Revision: 57189 $
051: */
052: public class LocalManagedConnectionFactory extends
053: BaseWrapperManagedConnectionFactory {
054: static final long serialVersionUID = 4698955390505160469L;
055:
056: private String driverClass;
057:
058: private transient Driver driver;
059:
060: private String connectionURL;
061:
062: protected String connectionProperties;
063:
064: public LocalManagedConnectionFactory() {
065:
066: }
067:
068: /**
069: * Get the value of ConnectionURL.
070: *
071: * @return value of ConnectionURL.
072: */
073: public String getConnectionURL() {
074: return connectionURL;
075: }
076:
077: /**
078: * Set the value of ConnectionURL.
079: *
080: * @param connectionURL Value to assign to ConnectionURL.
081: */
082: public void setConnectionURL(final String connectionURL) {
083: this .connectionURL = connectionURL;
084: }
085:
086: /**
087: * Get the DriverClass value.
088: *
089: * @return the DriverClass value.
090: */
091: public String getDriverClass() {
092: return driverClass;
093: }
094:
095: /**
096: * Set the DriverClass value.
097: *
098: * @param driverClass The new DriverClass value.
099: */
100: public synchronized void setDriverClass(final String driverClass) {
101: this .driverClass = driverClass;
102: driver = null;
103: }
104:
105: /**
106: * Get the value of connectionProperties.
107: *
108: * @return value of connectionProperties.
109: */
110: public String getConnectionProperties() {
111: return connectionProperties;
112: }
113:
114: /**
115: * Set the value of connectionProperties.
116: *
117: * @param connectionProperties Value to assign to connectionProperties.
118: */
119: public void setConnectionProperties(String connectionProperties) {
120: this .connectionProperties = connectionProperties;
121: connectionProps.clear();
122: if (connectionProperties != null) {
123: // Map any \ to \\
124: connectionProperties = connectionProperties.replaceAll(
125: "\\\\", "\\\\\\\\");
126:
127: InputStream is = new ByteArrayInputStream(
128: connectionProperties.getBytes());
129: try {
130: connectionProps.load(is);
131: } catch (IOException ioe) {
132: throw new NestedRuntimeException(
133: "Could not load connection properties", ioe);
134: }
135: }
136: }
137:
138: public ManagedConnection createManagedConnection(Subject subject,
139: ConnectionRequestInfo cri)
140: throws javax.resource.ResourceException {
141: Properties props = getConnectionProperties(subject, cri);
142: // Some friendly drivers (Oracle, you guessed right) modify the props you supply.
143: // Since we use our copy to identify compatibility in matchManagedConnection, we need
144: // a pristine copy for our own use. So give the friendly driver a copy.
145: Properties copy = (Properties) props.clone();
146: boolean trace = log.isTraceEnabled();
147: if (trace) {
148: // Make yet another copy to mask the password
149: Properties logCopy = copy;
150: if (copy.getProperty("password") != null) {
151: logCopy = (Properties) props.clone();
152: logCopy.setProperty("password", "--hidden--");
153: }
154: log.trace("Using properties: " + logCopy);
155: }
156:
157: try {
158: String url = getConnectionURL();
159: Driver d = getDriver(url);
160: Connection con = d.connect(url, copy);
161: if (con == null)
162: throw new JBossResourceException(
163: "Wrong driver class for this connection URL");
164:
165: return new LocalManagedConnection(this , con, props,
166: transactionIsolation, preparedStatementCacheSize);
167: } catch (Exception e) {
168: throw new JBossResourceException(
169: "Could not create connection", e);
170: }
171: }
172:
173: public ManagedConnection matchManagedConnections(final Set mcs,
174: final Subject subject, final ConnectionRequestInfo cri)
175: throws ResourceException {
176: Properties newProps = getConnectionProperties(subject, cri);
177:
178: for (Iterator i = mcs.iterator(); i.hasNext();) {
179: Object o = i.next();
180:
181: if (o instanceof LocalManagedConnection) {
182: LocalManagedConnection mc = (LocalManagedConnection) o;
183:
184: //First check the properties
185: if (mc.getProps().equals(newProps)) {
186: //Next check to see if we are validating on matchManagedConnections
187: if ((getValidateOnMatch() && mc.checkValid())
188: || !getValidateOnMatch()) {
189:
190: return mc;
191:
192: }
193:
194: }
195: }
196: }
197:
198: return null;
199: }
200:
201: public int hashCode() {
202: int result = 17;
203: result = result
204: * 37
205: + ((connectionURL == null) ? 0 : connectionURL
206: .hashCode());
207: result = result * 37
208: + ((driverClass == null) ? 0 : driverClass.hashCode());
209: result = result * 37
210: + ((userName == null) ? 0 : userName.hashCode());
211: result = result * 37
212: + ((password == null) ? 0 : password.hashCode());
213: result = result * 37 + transactionIsolation;
214: return result;
215: }
216:
217: public boolean equals(Object other) {
218: if (this == other)
219: return true;
220: if (getClass() != other.getClass())
221: return false;
222: LocalManagedConnectionFactory otherMcf = (LocalManagedConnectionFactory) other;
223: return this .connectionURL.equals(otherMcf.connectionURL)
224: && this .driverClass.equals(otherMcf.driverClass)
225: && ((this .userName == null) ? otherMcf.userName == null
226: : this .userName.equals(otherMcf.userName))
227: && ((this .password == null) ? otherMcf.password == null
228: : this .password.equals(otherMcf.password))
229: && this .transactionIsolation == otherMcf.transactionIsolation;
230:
231: }
232:
233: /**
234: * Check the driver for the given URL. If it is not registered already
235: * then register it.
236: *
237: * @param url The JDBC URL which we need a driver for.
238: */
239: protected synchronized Driver getDriver(final String url)
240: throws ResourceException {
241: boolean trace = log.isTraceEnabled();
242:
243: // don't bother if it is loaded already
244: if (driver != null) {
245: return driver;
246: }
247: if (trace)
248: log.trace("Checking driver for URL: " + url);
249:
250: if (driverClass == null) {
251: throw new JBossResourceException(
252: "No Driver class specified!");
253: }
254:
255: // Check if the driver is already loaded, if not then try to load it
256:
257: if (isDriverLoadedForURL(url)) {
258: return driver;
259: } // end of if ()
260:
261: try {
262: //try to load the class... this should register with DriverManager.
263: Class clazz = Class.forName(driverClass, true, Thread
264: .currentThread().getContextClassLoader());
265: if (isDriverLoadedForURL(url))
266: //return immediately, some drivers (Cloudscape) do not let you create an instance.
267: return driver;
268:
269: //We loaded the class, but either it didn't register
270: //and is not spec compliant, or is the wrong class.
271: driver = (Driver) clazz.newInstance();
272: DriverManager.registerDriver(driver);
273: if (isDriverLoadedForURL(url))
274: return driver;
275: //We can even instantiate one, it must be the wrong class for the URL.
276: } catch (Exception e) {
277: throw new JBossResourceException(
278: "Failed to register driver for: " + driverClass, e);
279: }
280:
281: throw new JBossResourceException(
282: "Apparently wrong driver class specified for URL: class: "
283: + driverClass + ", url: " + url);
284: }
285:
286: private boolean isDriverLoadedForURL(String url) {
287: boolean trace = log.isTraceEnabled();
288:
289: try {
290: driver = DriverManager.getDriver(url);
291: if (trace)
292: log.trace("Driver already registered for url: " + url);
293: return true;
294: } catch (Exception e) {
295: if (trace)
296: log.trace("Driver not yet registered for url: " + url);
297: return false;
298: }
299: }
300:
301: protected String internalGetConnectionURL() {
302: return connectionURL;
303: }
304: }
|