Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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 org.apache.jackrabbit.core.util.db;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.jcr.RepositoryException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.DelegatingConnection;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.jackrabbit.core.config.DataSourceConfig;
import org.apache.jackrabbit.core.config.DataSourceConfig.DataSourceDefinition;
import org.apache.jackrabbit.core.util.db.DataSourceWrapper;
import org.apache.jackrabbit.util.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A factory for new database connections.
* Supported are regular JDBC drivers, as well as
* JNDI resources.
*
* FIXME: the registry currently is ClassLoader wide. I.e., if you start two repositories
* then you share the registered datasources...
*/
public final class ConnectionFactory {
private static final Logger log = LoggerFactory.getLogger(ConnectionFactory.class);
/**
* The lock to protect the fields of this class.
*/
private final Object lock = new Object();
/**
* The data sources without logical name. The keys in the map are based on driver-url-user combination.
*/
private final Map keyToDataSource = new HashMap();
/**
* The configured data sources with logical name. The keys in the map are the logical name.
*/
private final Map nameToDataSource = new HashMap();
/**
* The configured data source defs. The keys in the map are the logical name.
*/
private final Map nameToDataSourceDef = new HashMap();
/**
* The list of data sources created by this factory.
*/
private final List created = new ArrayList();
private boolean closed = false;
/**
* Registers a number of data sources.
*
* @param dsc the {@link DataSourceConfig} which contains the configuration
*/
public void registerDataSources(DataSourceConfig dsc) throws RepositoryException {
synchronized (lock) {
sanityCheck();
for (DataSourceDefinition def : dsc.getDefinitions()) {
Class driverClass = getDriverClass(def.getDriver());
if (driverClass != null
&& Context.class.isAssignableFrom(driverClass)) {
DataSource ds = getJndiDataSource((Class) driverClass, def.getUrl());
nameToDataSource.put(def.getLogicalName(), ds);
nameToDataSourceDef.put(def.getLogicalName(), def);
} else {
BasicDataSource bds =
getDriverDataSource(driverClass, def.getUrl(), def.getUser(), def.getPassword());
if (def.getMaxPoolSize() > 0) {
bds.setMaxActive(def.getMaxPoolSize());
}
if (def.getValidationQuery() != null && !"".equals(def.getValidationQuery().trim())) {
bds.setValidationQuery(def.getValidationQuery());
}
nameToDataSource.put(def.getLogicalName(), bds);
nameToDataSourceDef.put(def.getLogicalName(), def);
}
}
}
}
/**
* Retrieves a configured data source by logical name.
*
* @param logicalName the name of the {@code DataSource}
* @return a {@code DataSource}
* @throws RepositoryException if there is no {@code DataSource} with the given name
*/
public DataSource getDataSource(String logicalName) throws RepositoryException {
synchronized (lock) {
sanityCheck();
DataSource ds = nameToDataSource.get(logicalName);
if (ds == null) {
throw new RepositoryException("DataSource with logicalName " + logicalName
+ " has not been configured");
}
return ds;
}
}
/**
* @param logicalName the name of the {@code DataSource}
* @return the configured database type
* @throws RepositoryException if there is no {@code DataSource} with the given name
*/
public String getDataBaseType(String logicalName) throws RepositoryException {
synchronized (lock) {
sanityCheck();
DataSourceDefinition def = nameToDataSourceDef.get(logicalName);
if (def == null) {
throw new RepositoryException("DataSource with logicalName " + logicalName
+ " has not been configured");
}
return def.getDbType();
}
}
/**
* Retrieve a {@code DataSource} for the specified properties.
* This can be a JNDI Data Source as well. To do that,
* the driver class name must reference a {@code javax.naming.Context} class
* (for example {@code javax.naming.InitialContext}), and the URL must be the JNDI URL
* (for example {@code java:comp/env/jdbc/Test}).
*
* @param driver the JDBC driver or the Context class
* @param url the database URL
* @param user the user name
* @param password the password
* @return the {@code DataSource}
* @throws RepositoryException if the driver could not be loaded
* @throws SQLException if the connection could not be established
*/
public DataSource getDataSource(String driver, String url, String user, String password)
throws RepositoryException, SQLException {
final String key = driver + url + user;
synchronized(lock) {
sanityCheck();
DataSource ds = keyToDataSource.get(key);
if (ds == null) {
ds = createDataSource(
driver, url, user, Base64.decodeIfEncoded(password));
keyToDataSource.put(key, ds);
}
return ds;
}
}
/**
*
*/
public void close() {
synchronized(lock) {
sanityCheck();
for (BasicDataSource ds : created) {
try {
ds.close();
} catch (SQLException e) {
log.error("failed to close " + ds, e);
}
}
keyToDataSource.clear();
nameToDataSource.clear();
nameToDataSourceDef.clear();
created.clear();
closed = true;
}
}
/**
* Needed for pre-10R2 Oracle blob support....:(
*
* This method actually assumes that we are using commons DBCP 1.2.2.
*
* @param con the commons-DBCP {@code DelegatingConnection} to unwrap
* @return the unwrapped connection
*/
public static Connection unwrap(Connection con) throws SQLException {
if (con instanceof DelegatingConnection) {
return ((DelegatingConnection)con).getInnermostDelegate();
} else {
throw new SQLException("failed to unwrap connection of class " + con.getClass().getName() +
", expected it to be a " + DelegatingConnection.class.getName());
}
}
private void sanityCheck() {
if (closed) {
throw new IllegalStateException("this factory has already been closed");
}
}
/**
* Create a new pooling data source or finds an existing JNDI data source (depends on driver).
*
* @param driver
* @param url
* @param user
* @param password
* @return
* @throws RepositoryException
*/
private DataSource createDataSource(String driver, String url, String user, String password)
throws RepositoryException {
Class driverClass = getDriverClass(driver);
if (driverClass != null
&& Context.class.isAssignableFrom(driverClass)) {
@SuppressWarnings("unchecked")
DataSource database = getJndiDataSource((Class) driverClass, url);
if (user == null && password == null) {
return database;
} else {
return new DataSourceWrapper(database, user, password);
}
} else {
return getDriverDataSource(driverClass, url, user, password);
}
}
/**
* Loads and returns the given JDBC driver (or JNDI context) class.
* Returns null if a class name is not given.
*
* @param driver driver class name
* @return driver class, or null
* @throws RepositoryException if the class can not be loaded
*/
private Class getDriverClass(String driver)
throws RepositoryException {
try {
if (driver != null && driver.length() > 0) {
return Class.forName(driver);
} else {
return null;
}
} catch (ClassNotFoundException e) {
throw new RepositoryException(
"Could not load JDBC driver class " + driver, e);
}
}
/**
* Returns the JDBC {@link DataSource} bound to the given name in
* the JNDI {@link Context} identified by the given class.
*
* @param contextClass class that is instantiated to get the JNDI context
* @param name name of the DataSource within the JNDI context
* @return the DataSource bound in JNDI
* @throws RepositoryException if the JNDI context can not be accessed,
* or if the named DataSource is not found
*/
private DataSource getJndiDataSource(
Class contextClass, String name)
throws RepositoryException {
try {
Object object = contextClass.newInstance().lookup(name);
if (object instanceof DataSource) {
return (DataSource) object;
} else {
throw new RepositoryException(
"Object " + object + " with JNDI name "
+ name + " is not a JDBC DataSource");
}
} catch (InstantiationException e) {
throw new RepositoryException(
"Invalid JNDI context: " + contextClass.getName(), e);
} catch (IllegalAccessException e) {
throw new RepositoryException(
"Invalid JNDI context: " + contextClass.getName(), e);
} catch (NamingException e) {
throw new RepositoryException(
"JNDI name not found: " + name, e);
}
}
/**
* Creates and returns a pooling JDBC {@link DataSource} for accessing
* the database identified by the given driver class and JDBC
* connection URL. The driver class can be null if
* a specific driver has not been configured.
*
* @param driverClass the JDBC driver class, or null
* @param url the JDBC connection URL
* @return pooling DataSource for accessing the specified database
*/
private BasicDataSource getDriverDataSource(
Class driverClass, String url, String user, String password) {
BasicDataSource ds = new BasicDataSource();
created.add(ds);
if (driverClass != null) {
Driver instance = null;
try {
// Workaround for Apache Derby:
// The JDBC specification recommends the Class.forName
// method without the .newInstance() method call,
// but it is required after a Derby 'shutdown'
instance = (Driver) driverClass.newInstance();
} catch (Throwable e) {
// Ignore exceptions as there's no requirement for
// a JDBC driver class to have a public default constructor
}
if (instance != null) {
if (instance.jdbcCompliant()) {
// JCR-3445 At the moment the PostgreSQL isn't compliant because it doesn't implement this method...
ds.setValidationQueryTimeout(3);
}
}
ds.setDriverClassName(driverClass.getName());
}
ds.setUrl(url);
ds.setUsername(user);
ds.setPassword(password);
ds.setDefaultAutoCommit(true);
ds.setTestOnBorrow(false);
ds.setTestWhileIdle(true);
ds.setTimeBetweenEvictionRunsMillis(600000); // 10 Minutes
ds.setMinEvictableIdleTimeMillis(60000); // 1 Minute
ds.setMaxActive(-1); // unlimited
ds.setMaxIdle(GenericObjectPool.DEFAULT_MAX_IDLE + 10);
ds.setValidationQuery(guessValidationQuery(url));
ds.setAccessToUnderlyingConnectionAllowed(true);
ds.setPoolPreparedStatements(true);
ds.setMaxOpenPreparedStatements(-1); // unlimited
return ds;
}
private String guessValidationQuery(String url) {
if (url.contains("derby")) {
return "values(1)";
} else if (url.contains("mysql")) {
return "select 1";
} else if (url.contains("sqlserver") || url.contains("jtds")) {
return "select 1";
} else if (url.contains("oracle")) {
return "select 'validationQuery' from dual";
} else if (url.contains("postgresql")) {
return "select 1";
} else if (url.contains("h2")) {
return "select 1";
} else if (url.contains("db2")) {
return "values(1)";
}
log.warn("Failed to guess validation query for URL " + url);
return null;
}
}