// Copyright 2008 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.gwtorm.jdbc; import java.io.File; import java.io.PrintWriter; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.logging.Logger; import javax.sql.DataSource; /** A simple non-pooling DataSource representation. */ public class SimpleDataSource implements DataSource { private final Properties connectionInfo; private final String url; private final Driver driver; private PrintWriter logWriter; /** * Create a non-pooling data source. * <p> * The JDBC properties information must define at least <code>url</code> and * <code>driver</code>, but may also include driver specific properties such * as <code>username</code> and <code>password</code>. * * @param dbInfo JDBC connection information. The property table is copied. * @throws SQLException the driver class is not available through the current * class loader. */ public SimpleDataSource(final Properties dbInfo) throws SQLException { connectionInfo = new Properties(); connectionInfo.putAll(dbInfo); url = (String) connectionInfo.remove("url"); if (url == null) { throw new SQLException("Required property 'url' not defined"); } final String driverName = (String) connectionInfo.remove("driver"); final String classpath = (String) connectionInfo.remove("classpath"); if (driverName != null) { ClassLoader cl = threadCL(); if (classpath != null && classpath.length() > 0) { final List<URL> urls = new ArrayList<URL>(); for (final String path : classpath.split(File.pathSeparator)) { try { urls.add(new URL(path)); } catch (MalformedURLException e1) { final File f = new File(path); if (f.exists()) { try { urls.add(f.getAbsoluteFile().toURI().toURL()); } catch (MalformedURLException e2) { throw badClasspath(classpath, e2); } } else { throw badClasspath(classpath, e1); } } } cl = new URLClassLoader(urls.toArray(new URL[urls.size()]), cl); } driver = loadDriver(driverName, cl); } else { driver = null; } logWriter = new PrintWriter(System.out); } private static SQLException badClasspath(final String classpath, final MalformedURLException e1) { final SQLException sqle; sqle = new SQLException("Invalid driver classpath " + classpath); sqle.initCause(e1); return sqle; } public Connection getConnection() throws SQLException { if (driver != null) { return driver.connect(url, connectionInfo); } return DriverManager.getConnection(url, connectionInfo); } public Connection getConnection(String user, String password) throws SQLException { if (driver != null) { final Properties info = new Properties(connectionInfo); if (user != null) { info.put("user", user); } if (password != null) { info.put("password", password); } return driver.connect(url, info); } return DriverManager.getConnection(url, user, password); } public PrintWriter getLogWriter() { return logWriter; } public void setLogWriter(final PrintWriter out) { logWriter = out; } public int getLoginTimeout() { return 0; } public void setLoginTimeout(int seconds) { } public boolean isWrapperFor(Class<?> iface) { return false; } public <T> T unwrap(Class<T> iface) throws SQLException { throw new SQLException(getClass().getName() + " wraps nothing"); } private static synchronized Driver loadDriver(final String driver, final ClassLoader loader) throws SQLException { // I've seen some drivers (*cough* Informix *cough*) which won't load // on multiple threads at the same time. Forcing our code to synchronize // around loading the driver ensures we won't ever ask for the same driver // to initialize from different threads. Of course that could still happen // in other parts of the same JVM, but its quite unlikely. // try { return (Driver) Class.forName(driver, true, loader).newInstance(); } catch (Throwable err) { final SQLException e; e = new SQLException("Driver class " + driver + " not available"); e.initCause(err); throw e; } } private static ClassLoader threadCL() { try { return Thread.currentThread().getContextClassLoader(); } catch (SecurityException e) { return SimpleDataSource.class.getClassLoader(); } } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { throw new SQLFeatureNotSupportedException(); } }