/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.alibaba.wasp.jdbcx;
import com.alibaba.wasp.jdbc.Driver;
import com.alibaba.wasp.jdbc.JdbcConnection;
import com.alibaba.wasp.jdbc.JdbcException;
import com.alibaba.wasp.util.StringUtils;
import com.alibaba.wasp.util.Utils;
import org.apache.commons.lang.NotImplementedException;
import org.apache.hadoop.conf.Configuration;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.DataSource;
import javax.sql.PooledConnection;
import javax.sql.XAConnection;
import javax.sql.XADataSource;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Properties;
import java.util.logging.Logger;
/*## Java 1.7 ##
import java.util.logging.Logger;
//*/
/**
* A data source for H2 database connections. It is a factory for XAConnection
* and Connection objects. This class is usually registered in a JNDI naming
* service. To create a data source object and register it with a JNDI service,
* use the following code:
*
* <pre>
* import com.alibaba.wasp.jdbcx.JdbcDataSource;
* import javax.naming.Context;
* import javax.naming.InitialContext;
* JdbcDataSource ds = new JdbcDataSource();
* ds.setURL("jdbc:wasp:˜/test");
* ds.setUser("sa");
* ds.setPassword("sa");
* Context ctx = new InitialContext();
* ctx.bind("jdbc/dsName", ds);
* </pre>
*
* To use a data source that is already registered, use the following code:
*
* <pre>
* import java.sql.Connection;
* import javax.sql.DataSource;
* import javax.naming.Context;
* import javax.naming.InitialContext;
* Context ctx = new InitialContext();
* DataSource ds = (DataSource) ctx.lookup("jdbc/dsName");
* Connection conn = ds.getConnection();
* </pre>
*
* In this example the user name and password are serialized as well; this may
* be a security problem in some cases.
*/
public class JdbcDataSource implements XADataSource, DataSource,
ConnectionPoolDataSource, Serializable, Referenceable {
private static final long serialVersionUID = 1288136338451857771L;
private transient JdbcDataSourceFactory factory;
private transient PrintWriter logWriter;
private int loginTimeout;
private String userName = "";
private char[] passwordChars = {};
private String url = "";
private String description;
private Configuration conf;
static {
com.alibaba.wasp.jdbc.Driver.load();
}
/**
* The public constructor.
*/
public JdbcDataSource(Configuration conf) {
this.conf = conf;
initFactory();
}
/**
* Called when de-serializing the object.
*
* @param in
* the input stream
*/
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
initFactory();
in.defaultReadObject();
}
private void initFactory() {
factory = new JdbcDataSourceFactory(conf);
}
/**
* Get the login timeout in seconds, 0 meaning no timeout.
*
* @return the timeout in seconds
*/
public int getLoginTimeout() {
return loginTimeout;
}
/**
* Set the login timeout in seconds, 0 meaning no timeout. The default value
* is 0. This value is ignored by this database.
*
* @param timeout
* the timeout in seconds
*/
public void setLoginTimeout(int timeout) {
this.loginTimeout = timeout;
}
/**
* Get the current log writer for this object.
*
* @return the log writer
*/
public PrintWriter getLogWriter() {
return logWriter;
}
/**
* Set the current log writer for this object. This value is ignored by this
* database.
*
* @param out
* the log writer
*/
public void setLogWriter(PrintWriter out) {
logWriter = out;
}
/**
* Open a new connection using the current URL, user name and password.
*
* @return the connection
*/
public Connection getConnection() throws SQLException {
return getJdbcConnection(userName,
StringUtils.cloneCharArray(passwordChars));
}
/**
* Open a new connection using the current URL and the specified user name and
* password.
*
* @param user
* the user name
* @param password
* the password
* @return the connection
*/
public Connection getConnection(String user, String password)
throws SQLException {
return getJdbcConnection(user, convertToCharArray(password));
}
private JdbcConnection getJdbcConnection(String user, char[] password)
throws SQLException {
Properties info = Utils.convertConfigurationToProperties(conf);
info.setProperty("user", user);
info.put("password", password);
Connection conn = getConnectionInternal(getURL(url, true), info);
if (conn == null) {
throw new SQLException("No suitable driver found for " + url, "08001",
8001);
} else if (!(conn instanceof JdbcConnection)) {
throw new SQLException("Connecting with old version is not supported: "
+ url, "08001", 8001);
}
return (JdbcConnection) conn;
}
private static Connection getConnectionInternal(String url, Properties properties) throws SQLException {
//com.alibaba.wasp.jdbc.Driver.load();
Enumeration<java.sql.Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
java.sql.Driver driver = (java.sql.Driver) drivers.nextElement();
if(!(driver instanceof Driver)) {
DriverManager.deregisterDriver(driver);
}
}
return DriverManager.getConnection(url, properties);
}
/**
* Get the database URL for the given database name using the current
* configuration options.
*
* @param name
* the database name
* @param admin
* true if the current user is an admin
* @return the database URL
*/
protected static String getURL(String name, boolean admin) {
String url;
if (name.startsWith("jdbc:")) {
return name;
}
url = name;
return "jdbc:wasp:" + url;
}
/**
* Get the current URL.
*
* @return the URL
*/
public String getURL() {
return url;
}
/**
* Set the current URL.
*
* @param url
* the new URL
*/
public void setURL(String url) {
this.url = url;
}
/**
* Set the current password.
*
* @param password
* the new password.
*/
public void setPassword(String password) {
this.passwordChars = convertToCharArray(password);
}
/**
* Set the current password in the form of a char array.
*
* @param password
* the new password in the form of a char array.
*/
public void setPasswordChars(char[] password) {
this.passwordChars = password;
}
private static char[] convertToCharArray(String s) {
return s == null ? null : s.toCharArray();
}
private static String convertToString(char[] a) {
return a == null ? null : new String(a);
}
/**
* Get the current password.
*
* @return the password
*/
public String getPassword() {
return convertToString(passwordChars);
}
/**
* Get the current user name.
*
* @return the user name
*/
public String getUser() {
return userName;
}
/**
* Set the current user name.
*
* @param user
* the new user name
*/
public void setUser(String user) {
this.userName = user;
}
/**
* Get the current description.
*
* @return the description
*/
public String getDescription() {
return description;
}
/**
* Set the description.
*
* @param description
* the new description
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Get a new reference for this object, using the current settings.
*
* @return the new reference
*/
public Reference getReference() {
String factoryClassName = JdbcDataSourceFactory.class.getName();
Reference ref = new Reference(getClass().getName(), factoryClassName, null);
ref.add(new StringRefAddr("url", url));
ref.add(new StringRefAddr("user", userName));
ref.add(new StringRefAddr("password", convertToString(passwordChars)));
ref.add(new StringRefAddr("loginTimeout", String.valueOf(loginTimeout)));
ref.add(new StringRefAddr("description", description));
return ref;
}
/**
* Open a new XA connection using the current URL, user name and password.
*
* @return the connection
*/
public XAConnection getXAConnection() throws SQLException {
return new JdbcXAConnection(getJdbcConnection(userName,
StringUtils.cloneCharArray(passwordChars)));
}
/**
* Open a new XA connection using the current URL and the specified user name
* and password.
*
* @param user
* the user name
* @param password
* the password
* @return the connection
*/
public XAConnection getXAConnection(String user, String password)
throws SQLException {
return new JdbcXAConnection(getJdbcConnection(user,
convertToCharArray(password)));
}
/**
* Open a new pooled connection using the current URL, user name and password.
*
* @return the connection
*/
public PooledConnection getPooledConnection() throws SQLException {
return getXAConnection();
}
/**
* Open a new pooled connection using the current URL and the specified user
* name and password.
*
* @param user
* the user name
* @param password
* the password
* @return the connection
*/
public PooledConnection getPooledConnection(String user, String password)
throws SQLException {
return getXAConnection(user, password);
}
/**
* [Not supported] Return an object of this class if possible.
*
* @param iface
* the class
*/
// ## Java 1.6 ##
public <T> T unwrap(Class<T> iface) throws SQLException {
throw JdbcException.getUnsupportedException("isWrapperFor unsupported");
}
// */
/**
* [Not supported] Checks if unwrap can return an object of this class.
*
* @param iface
* the class
*/
// ## Java 1.6 ##
public boolean isWrapperFor(Class<?> iface) throws SQLException {
throw JdbcException.getUnsupportedException("isWrapperFor unsupported");
}
// */
/**
* [Not supported]
*/
public Logger getParentLogger() {
throw new NotImplementedException();
}
/**
* INTERNAL
*/
public String toString() {
return "JdbcDataSource : url=" + url + " user=" + userName;
}
}