Skip to content

Commit

Permalink
MONDRIAN: Replace JDBC connection with pooled DataSource (RFE 764169).
Browse files Browse the repository at this point in the history
[git-p4: depot-paths = "//open/mondrian/": change = 647]
  • Loading branch information
julianhyde committed Jul 10, 2003
1 parent cf65fcf commit 3886746
Show file tree
Hide file tree
Showing 8 changed files with 772 additions and 480 deletions.
3 changes: 3 additions & 0 deletions build.xml
Expand Up @@ -77,6 +77,9 @@ demo/mysql/FoodMartData.sql"/>
<path id="project.classpath">
<pathelement location="${classes.dir}"/>
<pathelement location="${catalina.home}/common/lib/servlet.jar"/>
<pathelement location="${catalina.home}/common/lib/commons-dbcp.jar"/>
<pathelement location="${catalina.home}/common/lib/commons-collections.jar"/>
<pathelement location="${catalina.home}/common/lib/commons-pool.jar"/>
<pathelement location="${lib.dir}/javacup.jar"/>
<pathelement location="${lib.dir}/mof.jar"/>
<pathelement location="${lib.dir}/jmi.jar"/>
Expand Down
213 changes: 150 additions & 63 deletions src/main/mondrian/rolap/RolapConnection.java
Expand Up @@ -13,11 +13,17 @@

import mondrian.olap.*;

import javax.naming.NamingException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.*;
import java.sql.Connection;
import java.sql.SQLException;
import java.io.PrintWriter;

import org.apache.commons.dbcp.ConnectionFactory;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.DataSourceConnectionFactory;

/**
* A <code>RolapConnection</code> is a connection to a Mondrian OLAP Server.
Expand All @@ -33,7 +39,9 @@
*/
public class RolapConnection extends ConnectionBase {
Util.PropertyList connectInfo;
java.sql.Connection jdbcConnection;
/** Factory for JDBC connections to talk to the RDBMS. This factory will
* usually use a connection pool. */
DataSource dataSource;
String catalogName;
RolapSchema schema;
private SchemaReader schemaReader;
Expand Down Expand Up @@ -67,61 +75,18 @@ public RolapConnection(Util.PropertyList connectInfo) {
* @pre connectInfo != null
*/
RolapConnection(Util.PropertyList connectInfo, RolapSchema schema) {
String provider = connectInfo.get(RolapConnectionProperties.Provider);
Util.assertTrue(provider.equalsIgnoreCase("mondrian"));
this.connectInfo = connectInfo;
final String jdbcConnectString = connectInfo.get(RolapConnectionProperties.Jdbc);
final String jdbcUser = connectInfo.get(RolapConnectionProperties.JdbcUser);
final String dataSource = connectInfo.get(RolapConnectionProperties.DataSource);

if ((jdbcConnectString == null) != (dataSource != null)) {
throw Util.newInternal(
"Connect string '" + connectInfo.toString() +
"' must contain either '" + RolapConnectionProperties.Jdbc +
"' or '" + RolapConnectionProperties.DataSource + "'");
}
this.catalogName = connectInfo.get(RolapConnectionProperties.Catalog);
if (jdbcConnectString != null) {
// Get connection through JDBC DriverManager.
String provider = connectInfo.get(RolapConnectionProperties.Provider);
Util.assertTrue(provider.equalsIgnoreCase("mondrian"));
String jdbcDrivers = connectInfo.get(RolapConnectionProperties.JdbcDrivers);
if (jdbcDrivers != null) {
loadDrivers(jdbcDrivers);
}
loadDrivers(MondrianProperties.instance().getJdbcDrivers());
Properties jdbcProperties = new Properties();
if (jdbcUser != null) {
jdbcProperties.setProperty("user", jdbcUser);
}
String jdbcPassword = connectInfo.get(RolapConnectionProperties.JdbcPassword);
if (jdbcPassword != null) {
jdbcProperties.setProperty("password", jdbcPassword);
}
try {
this.jdbcConnection = java.sql.DriverManager.getConnection(
jdbcConnectString, jdbcProperties);
} catch (SQLException e) {
throw Util.newInternal(e,
"Error while creating RolapSchema (" + connectInfo.toString() + ")");
}
} else {
Util.assertTrue(dataSource != null);
// Get connection from datasource.
try {
final DataSource dataSourceObject = (DataSource)
new InitialContext().lookup(dataSource);
this.jdbcConnection = dataSourceObject.getConnection();
} catch (SQLException e) {
throw Util.newInternal(e,
"Error while creating connection from data source (" + dataSource + ")");
} catch (NamingException e) {
throw Util.newInternal(e,
"Error while looking up data source (" + dataSource + ")");
}
}
this.catalogName = connectInfo.get(RolapConnectionProperties.Catalog);
this.dataSource = createDataSource(connectInfo);
Role role = null;
if (schema == null) {
// If RolapSchema.Pool.get were to call this with schema == null,
// we would loop.
final String jdbcConnectString = connectInfo.get(RolapConnectionProperties.Jdbc);
final String jdbcUser = connectInfo.get(RolapConnectionProperties.JdbcUser);
final String dataSource = connectInfo.get(RolapConnectionProperties.DataSource);
schema = RolapSchema.Pool.instance().get(
catalogName, jdbcConnectString, jdbcUser, dataSource, connectInfo);
String roleName = connectInfo.get(RolapConnectionProperties.Role);
Expand All @@ -139,7 +104,91 @@ public RolapConnection(Util.PropertyList connectInfo) {
this.schema = schema;
}

public static synchronized void loadDrivers(String jdbcDrivers) {
private DataSource createDataSource(Util.PropertyList connectInfo) {
final String jdbcConnectString = connectInfo.get(RolapConnectionProperties.Jdbc);
final String poolNeededString = connectInfo.get(RolapConnectionProperties.PoolNeeded);
final boolean poolNeeded;
if (jdbcConnectString != null) {
// Get connection through own pooling datasource
String jdbcDrivers = connectInfo.get(RolapConnectionProperties.JdbcDrivers);
if (jdbcDrivers != null) {
loadDrivers(jdbcDrivers);
}
loadDrivers(MondrianProperties.instance().getJdbcDrivers());
if (poolNeededString == null) {
// JDBC connections are dumb beasts, so we assume they're not
// pooled.
poolNeeded = true;
} else {
poolNeeded = poolNeededString.equalsIgnoreCase("true");
}
final String jdbcUser = connectInfo.get(RolapConnectionProperties.JdbcUser);
final String jdbcPassword = connectInfo.get(RolapConnectionProperties.JdbcPassword);
if (!poolNeeded) {
// Connection is already pooled; don't pool it again.
return new DriverManagerDataSource(jdbcConnectString, jdbcUser,
jdbcPassword);
}
Properties jdbcProperties = new Properties();
if (jdbcUser != null) {
jdbcProperties.setProperty("user", jdbcUser);
}
if (jdbcPassword != null) {
jdbcProperties.setProperty("password", jdbcPassword);
}
if (jdbcConnectString.toLowerCase().indexOf("mysql") > -1) {
// mysql driver needs this autoReconnect parameter
jdbcProperties.setProperty("autoReconnect", "true");
}
// use the DriverManagerConnectionFactory to create connections
ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(jdbcConnectString, jdbcProperties);
try {
return RolapUtil.getPoolingDataSource(connectionFactory);
} catch (Throwable e) {
throw Util.newInternal(e,
"Error while creating connection pool (with URI " +
jdbcConnectString + ")");
}
}

final String dataSourceName = connectInfo.get(RolapConnectionProperties.DataSource);
if (dataSourceName == null) {
throw Util.newInternal(
"Connect string '" + connectInfo.toString() +
"' must contain either '" + RolapConnectionProperties.Jdbc +
"' or '" + RolapConnectionProperties.DataSource + "'");
}

if (poolNeededString == null) {
// Data sources are fairly smart, so we assume they look after their
// own pooling.
poolNeeded = false;
} else {
poolNeeded = poolNeededString.equalsIgnoreCase("true");
}
// Get connection from datasource.
final DataSource dataSource;
try {
dataSource = (DataSource) new InitialContext().lookup(dataSourceName);
} catch (NamingException e) {
throw Util.newInternal(e, "Error while looking up data source (" +
dataSourceName + ")");
}
if (!poolNeeded) {
return dataSource;
}
ConnectionFactory connectionFactory =
new DataSourceConnectionFactory(dataSource);
try {
return RolapUtil.getPoolingDataSource(connectionFactory);
} catch (Exception e) {
throw Util.newInternal(e,
"Error while creating connection pool (with URI " +
dataSourceName + ")");
}
}

public static synchronized void loadDrivers(String jdbcDrivers) {
StringTokenizer tok = new StringTokenizer(jdbcDrivers, ",");
while (tok.hasMoreTokens()) {
String jdbcDriver = tok.nextToken();
Expand All @@ -154,15 +203,7 @@ public static synchronized void loadDrivers(String jdbcDrivers) {
}
}

public void close()
{
try {
if (jdbcConnection != null) {
jdbcConnection.close();
}
} catch (SQLException e) {
// ignore
}
public void close() {
}

public Schema getSchema() {
Expand Down Expand Up @@ -231,6 +272,52 @@ public Role getRole() {
Util.assertPostcondition(!role.isMutable(), "!role.isMutable()");
return role;
}

/**
* Implementation of {@link DataSource} which calls the good ol'
* {@link java.sql.DriverManager}.
*/
private static class DriverManagerDataSource implements DataSource {
private final String jdbcConnectString;
private final String jdbcUser;
private final String jdbcPassword;
private PrintWriter logWriter;
private int loginTimeout;

public DriverManagerDataSource(String jdbcConnectString,
String jdbcUser, String jdbcPassword) {
this.jdbcConnectString = jdbcConnectString;
this.jdbcUser = jdbcUser;
this.jdbcPassword = jdbcPassword;
}

public Connection getConnection() throws SQLException {
return java.sql.DriverManager.getConnection(jdbcConnectString,
jdbcUser, jdbcPassword);
}

public Connection getConnection(String username, String password)
throws SQLException {
return java.sql.DriverManager.getConnection(jdbcConnectString,
username, password);
}

public PrintWriter getLogWriter() throws SQLException {
return logWriter;
}

public void setLogWriter(PrintWriter out) throws SQLException {
logWriter = out;
}

public void setLoginTimeout(int seconds) throws SQLException {
loginTimeout = seconds;
}

public int getLoginTimeout() throws SQLException {
return loginTimeout;
}
}
}

/**
Expand Down
12 changes: 11 additions & 1 deletion src/main/mondrian/rolap/RolapConnectionProperties.java
Expand Up @@ -23,7 +23,7 @@ public class RolapConnectionProperties extends EnumeratedValues {
private RolapConnectionProperties() {
super(new String[] {
Provider, Jdbc, JdbcDrivers, JdbcUser, JdbcPassword, Catalog,
CatalogContent, DataSource, Role});
CatalogContent, DataSource, PoolNeeded, Role});
}
/**
* @{value} must equal <code>"Mondrian"</code>.
Expand Down Expand Up @@ -71,6 +71,16 @@ private RolapConnectionProperties() {
* You must specify either {@link #DataSource} or {@link #Jdbc}.
*/
public static final String DataSource = "DataSource";
/**
* @{value} tells Mondrian whether to add a layer of connection pooling.
*
* <p>If no value is specified, we assume that:<ul>
* <li>connections created via the {@link #Jdbc} property are not pooled,
* and therefore need to be pooled,
* <li>connections created via the {@link #DataSource} are already pooled.
* </ul>
*/
public static final String PoolNeeded = "PoolNeeded";
/**
* {@value} is the name of the {@link mondrian.olap.Role role} to adopt. If
* not specified, the connection uses a role which has access to every
Expand Down

0 comments on commit 3886746

Please sign in to comment.