blob: abefb669af46abb424c42f76c82a76b92b397bb3 [file] [log] [blame]
// 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<>();
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;
}
@Override
public Connection getConnection() throws SQLException {
if (driver != null) {
return driver.connect(url, connectionInfo);
}
return DriverManager.getConnection(url, connectionInfo);
}
@Override
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);
}
@Override
public PrintWriter getLogWriter() {
return logWriter;
}
@Override
public void setLogWriter(final PrintWriter out) {
logWriter = out;
}
@Override
public int getLoginTimeout() {
return 0;
}
@Override
public void setLoginTimeout(int seconds) {}
@Override
public boolean isWrapperFor(Class<?> iface) {
return false;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
throw new SQLException(getClass().getName() + " wraps nothing");
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException();
}
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();
}
}
}