blob: ca01d013be69e2f553e3607fa33c1006c042b830 [file] [log] [blame]
/* $Id$ */
/**
* 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.manifoldcf.jdbc;
import org.apache.manifoldcf.core.interfaces.*;
import org.apache.manifoldcf.core.jdbcpool.*;
import org.apache.manifoldcf.agents.interfaces.*;
import org.apache.manifoldcf.crawler.system.Logging;
import org.apache.manifoldcf.crawler.system.ManifoldCF;
import java.util.*;
import java.sql.*;
import javax.naming.*;
import javax.sql.*;
import java.util.*;
/** This class creates a connection
*/
public class JDBCConnectionFactory
{
public static final String _rcsid = "@(#)$Id$";
private static Map<String,String> driverMap;
private static ConnectionPoolManager _pool = null;
static
{
driverMap = new HashMap<String,String>();
driverMap.put("oracle:thin:@", "oracle.jdbc.OracleDriver");
driverMap.put("postgresql://", "org.postgresql.Driver");
driverMap.put("jtds:sqlserver://", "net.sourceforge.jtds.jdbc.Driver");
driverMap.put("jtds:sybase://", "net.sourceforge.jtds.jdbc.Driver");
driverMap.put("mysql://", "com.mysql.jdbc.Driver");
driverMap.put("mariadb://", "org.mariadb.jdbc.Driver");
driverMap.put("xbib:csv:", "org.xbib.jdbc.csv.CsvDriver");
try
{
_pool = new ConnectionPoolManager(120,false);
}
catch (Exception e)
{
System.err.println("Can't set up pool");
e.printStackTrace(System.err);
}
}
private JDBCConnectionFactory()
{
}
/** Convert various connection parameters to a JDBC connection string, used in conjunction with the
* provider name.
*/
public static String getJDBCDriverString(String providerName, String host, String database, String rawDriverString)
{
if (rawDriverString != null && rawDriverString.length() > 0)
return rawDriverString;
if (database.length() == 0)
database = "_root_";
String instanceName = null;
// Special for MSSQL: Allow database spec to contain an instance name too, in form:
// <instance>/<database>
if (providerName.startsWith("jtds:"))
{
int slashIndex = database.indexOf("/");
if (slashIndex != -1)
{
instanceName = database.substring(0,slashIndex);
database = database.substring(slashIndex+1);
}
}
return host + "/" + database + ((instanceName==null)?"":";instance="+instanceName);
}
public static WrappedConnection getConnection(String providerName, String jdbcDriverString, String userName, String password)
throws ManifoldCFException, ServiceInterruption
{
String driverClassName = driverMap.get(providerName);
if (driverClassName == null)
throw new ManifoldCFException("Unrecognized jdbc provider: '"+providerName+"'");
String dburl = "jdbc:" + providerName + jdbcDriverString;
if (Logging.connectors != null && Logging.connectors.isDebugEnabled())
Logging.connectors.debug("JDBC: The connect string is '"+dburl+"'");
try
{
// Hope for a connection now
if (_pool != null)
{
// Build a unique string to identify the pool. This has to include
// the database and host at a minimum.
// Provider is part of the pool key, so that the pools can distinguish between different databases
String poolKey = providerName + "/" + jdbcDriverString;
// Better include the credentials on the pool key, or we won't be able to change those and have it build new connections
// The password value is SHA-1 hashed, because the pool driver reports the password in many exceptions and we don't want it
// to be displayed.
poolKey += "/" + userName + "/" + ManifoldCF.hash(password);
ConnectionPool cp;
synchronized (_pool)
{
cp = _pool.getPool(poolKey);
if (cp == null)
{
// Register the driver here
Class.forName(driverClassName);
//System.out.println("Class name '"+driverClassName+"'; URL = '"+dburl+"'");
cp =_pool.addAlias(poolKey, driverClassName, dburl,
userName, password, 30, 300000L);
}
}
return cp.getConnection();
}
else
throw new ManifoldCFException("Can't get connection since pool driver did not initialize properly");
}
catch (InterruptedException e)
{
throw new ManifoldCFException(e.getMessage(),ManifoldCFException.INTERRUPTED);
}
catch (java.sql.SQLException e)
{
e.printStackTrace();
// Unfortunately, the connection pool manager manages to eat all actual connection setup errors. This makes it very hard to figure anything out
// when something goes wrong. So, we try again, going directly this time as a means of getting decent error feedback.
try
{
if (userName != null && userName.length() > 0)
{
DriverManager.getConnection(dburl, userName, password).close();
}
else
{
DriverManager.getConnection(dburl).close();
}
}
catch (java.sql.SQLException e2)
{
throw new ManifoldCFException("Error getting connection: "+e2.getMessage(),e2,ManifoldCFException.SETUP_ERROR);
}
// By definition, this must be a service interruption, because the direct route in setting up the connection succeeded.
long currentTime = System.currentTimeMillis();
throw new ServiceInterruption("Error getting connection: "+e.getMessage(),e,currentTime + 300000L,currentTime + 6 * 60 * 60000L,-1,true);
}
catch (java.lang.ClassNotFoundException e)
{
throw new ManifoldCFException("Driver class not found: "+e.getMessage(),e,ManifoldCFException.SETUP_ERROR);
}
catch (java.lang.InstantiationException e)
{
throw new ManifoldCFException("Driver class not instantiable: "+e.getMessage(),e,ManifoldCFException.SETUP_ERROR);
}
catch (java.lang.IllegalAccessException e)
{
throw new ManifoldCFException("Driver class not accessible: "+e.getMessage(),e,ManifoldCFException.SETUP_ERROR);
}
}
public static void releaseConnection(WrappedConnection c)
{
c.release();
}
}