Java tutorial
/* // This software is subject to the terms of the Eclipse Public License v1.0 // Agreement, available at the following URL: // http://www.eclipse.org/legal/epl-v10.html. // You must accept the terms of that agreement to use this software. // // Copyright (C) 2001-2005 Julian Hyde // Copyright (C) 2005-2013 Pentaho and others // All Rights Reserved. */ package mondrian.rolap; import mondrian.calc.*; import mondrian.calc.impl.DelegatingTupleList; import mondrian.olap.*; import mondrian.parser.MdxParserValidator; import mondrian.resource.MondrianResource; import mondrian.server.*; import mondrian.spi.*; import mondrian.spi.impl.JndiDataSourceResolver; import mondrian.util.*; import org.apache.log4j.Logger; import org.eigenbase.util.property.StringProperty; import org.olap4j.Scenario; import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.SQLException; import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicInteger; import javax.sql.DataSource; /** * A <code>RolapConnection</code> is a connection to a Mondrian OLAP Server. * * <p>Typically, you create a connection via * {@link DriverManager#getConnection(String, mondrian.spi.CatalogLocator)}. * {@link RolapConnectionProperties} describes allowable keywords.</p> * * @see RolapSchema * @see DriverManager * @author jhyde * @since 2 October, 2002 */ public class RolapConnection extends ConnectionBase { private static final Logger LOGGER = Logger.getLogger(RolapConnection.class); private static final AtomicInteger ID_GENERATOR = new AtomicInteger(); private final MondrianServer server; private final Util.PropertyList connectInfo; /** * Factory for JDBC connections to talk to the RDBMS. This factory will * usually use a connection pool. */ private final DataSource dataSource; private final String catalogUrl; private final RolapSchema schema; private SchemaReader schemaReader; protected Role role; private Locale locale = Locale.getDefault(); private Scenario scenario; private boolean closed = false; private static DataSourceResolver dataSourceResolver; private final int id; private final Statement internalStatement; /** * Creates a connection. * * @param server Server instance this connection belongs to * @param connectInfo Connection properties; keywords are described in * {@link RolapConnectionProperties}. * @param dataSource JDBC data source */ public RolapConnection(MondrianServer server, Util.PropertyList connectInfo, DataSource dataSource) { this(server, connectInfo, null, dataSource); } /** * Creates a RolapConnection. * * <p>Only {@link RolapSchemaPool#get} calls this with * schema != null (to create a schema's internal connection). * Other uses retrieve a schema from the cache based upon * the <code>Catalog</code> property. * * @param server Server instance this connection belongs to * @param connectInfo Connection properties; keywords are described in * {@link RolapConnectionProperties}. * @param schema Schema for the connection. Must be null unless this is to * be an internal connection. * @param dataSource If not null an external DataSource to be used * by Mondrian */ RolapConnection(MondrianServer server, Util.PropertyList connectInfo, RolapSchema schema, DataSource dataSource) { super(); assert server != null; this.server = server; this.id = ID_GENERATOR.getAndIncrement(); assert connectInfo != null; String provider = connectInfo.get(RolapConnectionProperties.Provider.name(), "mondrian"); Util.assertTrue(provider.equalsIgnoreCase("mondrian")); this.connectInfo = connectInfo; this.catalogUrl = connectInfo.get(RolapConnectionProperties.Catalog.name()); final String jdbcUser = connectInfo.get(RolapConnectionProperties.JdbcUser.name()); final String jdbcConnectString = connectInfo.get(RolapConnectionProperties.Jdbc.name()); final String strDataSource = connectInfo.get(RolapConnectionProperties.DataSource.name()); StringBuilder buf = new StringBuilder(); this.dataSource = createDataSource(dataSource, connectInfo, buf); Role role = null; // Register this connection before we register its internal statement. server.addConnection(this); if (schema == null) { // If RolapSchema.Pool.get were to call this with schema == null, // we would loop. Statement bootstrapStatement = createInternalStatement(false); final Locus locus = new Locus(new Execution(bootstrapStatement, 0), null, "Initializing connection"); Locus.push(locus); try { if (dataSource == null) { // If there is no external data source is passed in, we // expect the properties Jdbc, JdbcUser, DataSource to be // set, as they are used to generate the schema cache key. final String connectionKey = jdbcConnectString + getJdbcProperties(connectInfo).toString(); schema = RolapSchemaPool.instance().get(catalogUrl, connectionKey, jdbcUser, strDataSource, connectInfo); } else { schema = RolapSchemaPool.instance().get(catalogUrl, dataSource, connectInfo); } } finally { Locus.pop(locus); bootstrapStatement.close(); } internalStatement = schema.getInternalConnection().getInternalStatement(); String roleNameList = connectInfo.get(RolapConnectionProperties.Role.name()); if (roleNameList != null) { List<String> roleNames = Util.parseCommaList(roleNameList); List<Role> roleList = new ArrayList<Role>(); for (String roleName : roleNames) { final LockBox.Entry entry = server.getLockBox().get(roleName); Role role1; if (entry != null) { try { role1 = (Role) entry.getValue(); } catch (ClassCastException e) { role1 = null; } } else { role1 = schema.lookupRole(roleName); } if (role1 == null) { throw Util.newError("Role '" + roleName + "' not found"); } roleList.add(role1); } switch (roleList.size()) { case 0: // If they specify 'Role=;', the list of names will be // empty, and the effect will be as if they did specify // Role at all. role = null; break; case 1: role = roleList.get(0); break; default: role = RoleImpl.union(roleList); break; } } } else { this.internalStatement = createInternalStatement(true); // We are creating an internal connection. Now is a great time to // make sure that the JDBC credentials are valid, for this // connection and for external connections built on top of this. Connection conn = null; java.sql.Statement statement = null; try { conn = this.dataSource.getConnection(); Dialect dialect = DialectManager.createDialect(this.dataSource, conn); if (dialect.getDatabaseProduct() == Dialect.DatabaseProduct.DERBY) { // Derby requires a little extra prodding to do the // validation to detect an error. statement = conn.createStatement(); statement.executeQuery("select * from bogustable"); } } catch (SQLException e) { if (e.getMessage().equals("Table/View 'BOGUSTABLE' does not exist.")) { // Ignore. This exception comes from Derby when the // connection is valid. If the connection were invalid, we // would receive an error such as "Schema 'BOGUSUSER' does // not exist" } else { throw Util.newError(e, "Error while creating SQL connection: " + buf); } } finally { try { if (statement != null) { statement.close(); } if (conn != null) { conn.close(); } } catch (SQLException e) { // ignore } } } if (role == null) { role = schema.getDefaultRole(); } // Set the locale. String localeString = connectInfo.get(RolapConnectionProperties.Locale.name()); if (localeString != null) { this.locale = Util.parseLocale(localeString); assert locale != null; } this.schema = schema; setRole(role); } @Override protected void finalize() throws Throwable { try { super.finalize(); close(); } catch (Throwable t) { LOGGER.info(MondrianResource.instance().FinalizerErrorRolapConnection.baseMessage, t); } } /** * Returns the identifier of this connection. Unique within the lifetime of * this JVM. * * @return Identifier of this connection */ public int getId() { return id; } protected Logger getLogger() { return LOGGER; } /** * Creates a JDBC data source from the JDBC credentials contained within a * set of mondrian connection properties. * * <p>This method is package-level so that it can be called from the * RolapConnectionTest unit test. * * @param dataSource Anonymous data source from user, or null * @param connectInfo Mondrian connection properties * @param buf Into which method writes a description of the JDBC credentials * @return Data source */ static DataSource createDataSource(DataSource dataSource, Util.PropertyList connectInfo, StringBuilder buf) { assert buf != null; final String jdbcConnectString = connectInfo.get(RolapConnectionProperties.Jdbc.name()); final String jdbcUser = connectInfo.get(RolapConnectionProperties.JdbcUser.name()); final String jdbcPassword = connectInfo.get(RolapConnectionProperties.JdbcPassword.name()); final String dataSourceName = connectInfo.get(RolapConnectionProperties.DataSource.name()); if (dataSource != null) { appendKeyValue(buf, "Anonymous data source", dataSource); appendKeyValue(buf, RolapConnectionProperties.JdbcUser.name(), jdbcUser); appendKeyValue(buf, RolapConnectionProperties.JdbcPassword.name(), jdbcPassword); if (jdbcUser != null || jdbcPassword != null) { dataSource = new UserPasswordDataSource(dataSource, jdbcUser, jdbcPassword); } return dataSource; } else if (jdbcConnectString != null) { // Get connection through own pooling datasource appendKeyValue(buf, RolapConnectionProperties.Jdbc.name(), jdbcConnectString); appendKeyValue(buf, RolapConnectionProperties.JdbcUser.name(), jdbcUser); appendKeyValue(buf, RolapConnectionProperties.JdbcPassword.name(), jdbcPassword); String jdbcDrivers = connectInfo.get(RolapConnectionProperties.JdbcDrivers.name()); if (jdbcDrivers != null) { RolapUtil.loadDrivers(jdbcDrivers); } final String jdbcDriversProp = MondrianProperties.instance().JdbcDrivers.get(); RolapUtil.loadDrivers(jdbcDriversProp); Properties jdbcProperties = getJdbcProperties(connectInfo); final Map<String, String> map = Util.toMap(jdbcProperties); for (Map.Entry<String, String> entry : map.entrySet()) { // FIXME ordering is non-deterministic appendKeyValue(buf, entry.getKey(), entry.getValue()); } if (jdbcUser != null) { jdbcProperties.put("user", jdbcUser); } if (jdbcPassword != null) { jdbcProperties.put("password", jdbcPassword); } // JDBC connections are dumb beasts, so we assume they're not // pooled. Therefore the default is true. final boolean poolNeeded = connectInfo.get(RolapConnectionProperties.PoolNeeded.name(), "true") .equalsIgnoreCase("true"); if (!poolNeeded) { // Connection is already pooled; don't pool it again. return new DriverManagerDataSource(jdbcConnectString, jdbcProperties); } if (jdbcConnectString.toLowerCase().indexOf("mysql") > -1) { // mysql driver needs this autoReconnect parameter jdbcProperties.setProperty("autoReconnect", "true"); } return RolapConnectionPool.instance().getDriverManagerPoolingDataSource(jdbcConnectString, jdbcProperties); } else if (dataSourceName != null) { appendKeyValue(buf, RolapConnectionProperties.DataSource.name(), dataSourceName); appendKeyValue(buf, RolapConnectionProperties.JdbcUser.name(), jdbcUser); appendKeyValue(buf, RolapConnectionProperties.JdbcPassword.name(), jdbcPassword); // Data sources are fairly smart, so we assume they look after // their own pooling. Therefore the default is false. final boolean poolNeeded = connectInfo.get(RolapConnectionProperties.PoolNeeded.name(), "false") .equalsIgnoreCase("true"); // Get connection from datasource. DataSourceResolver dataSourceResolver = getDataSourceResolver(); try { dataSource = dataSourceResolver.lookup(dataSourceName); } catch (Exception e) { throw Util.newInternal(e, "Error while looking up data source (" + dataSourceName + ")"); } if (poolNeeded) { dataSource = RolapConnectionPool.instance().getDataSourcePoolingDataSource(dataSource, dataSourceName, jdbcUser, jdbcPassword); } else { if (jdbcUser != null || jdbcPassword != null) { dataSource = new UserPasswordDataSource(dataSource, jdbcUser, jdbcPassword); } } return dataSource; } else { throw Util.newInternal("Connect string '" + connectInfo.toString() + "' must contain either '" + RolapConnectionProperties.Jdbc + "' or '" + RolapConnectionProperties.DataSource + "'"); } } /** * Returns the instance of the {@link mondrian.spi.DataSourceResolver} * plugin. * * @return data source resolver */ private static synchronized DataSourceResolver getDataSourceResolver() { if (dataSourceResolver == null) { final StringProperty property = MondrianProperties.instance().DataSourceResolverClass; final String className = property.get(JndiDataSourceResolver.class.getName()); try { dataSourceResolver = ClassResolver.INSTANCE.instantiateSafe(className); } catch (ClassCastException e) { throw Util.newInternal(e, "Plugin class specified by property " + property.getPath() + " must implement " + DataSourceResolver.class.getName()); } } return dataSourceResolver; } /** * Appends "key=value" to a buffer, if value is not null. * * @param buf Buffer * @param key Key * @param value Value */ private static void appendKeyValue(StringBuilder buf, String key, Object value) { if (value != null) { if (buf.length() > 0) { buf.append("; "); } buf.append(key).append('=').append(value); } } /** * Creates a {@link Properties} object containing all of the JDBC * connection properties present in the * {@link mondrian.olap.Util.PropertyList connectInfo}. * * @param connectInfo Connection properties * @return The JDBC connection properties. */ private static Properties getJdbcProperties(Util.PropertyList connectInfo) { Properties jdbcProperties = new Properties(); for (Pair<String, String> entry : connectInfo) { if (entry.left.startsWith(RolapConnectionProperties.JdbcPropertyPrefix)) { jdbcProperties.put(entry.left.substring(RolapConnectionProperties.JdbcPropertyPrefix.length()), entry.right); } } return jdbcProperties; } public Util.PropertyList getConnectInfo() { return connectInfo; } public void close() { if (!closed) { closed = true; server.removeConnection(this); } if (internalStatement != null) { internalStatement.close(); } } public RolapSchema getSchema() { return schema; } public String getConnectString() { return connectInfo.toString(); } public String getCatalogName() { return catalogUrl; } public Locale getLocale() { return locale; } public void setLocale(Locale locale) { if (locale == null) { throw new IllegalArgumentException("locale must not be null"); } this.locale = locale; } public SchemaReader getSchemaReader() { return schemaReader; } public Object getProperty(String name) { // Mask out the values of certain properties. if (name.equals(RolapConnectionProperties.JdbcPassword.name()) || name.equals(RolapConnectionProperties.CatalogContent.name())) { return ""; } return connectInfo.get(name); } public CacheControl getCacheControl(PrintWriter pw) { return getServer().getAggregationManager().getCacheControl(this, pw); } /** * Executes a Query. * * @param query Query parse tree * * @throws ResourceLimitExceededException if some resource limit specified * in the property file was exceeded * @throws QueryCanceledException if query was canceled during execution * @throws QueryTimeoutException if query exceeded timeout specified in * the property file * * @deprecated Use {@link #execute(mondrian.server.Execution)}; this method * will be removed in mondrian-4.0 */ public Result execute(Query query) { final Statement statement = query.getStatement(); Execution execution = new Execution(statement, statement.getQueryTimeoutMillis()); return execute(execution); } /** * Executes a statement. * * @param execution Execution context (includes statement, query) * * @throws ResourceLimitExceededException if some resource limit specified * in the property file was exceeded * @throws QueryCanceledException if query was canceled during execution * @throws QueryTimeoutException if query exceeded timeout specified in * the property file */ public Result execute(final Execution execution) { execution.copyMDC(); return server.getResultShepherd().shepherdExecution(execution, new Callable<Result>() { public Result call() throws Exception { return executeInternal(execution); } }); } private Result executeInternal(final Execution execution) { execution.setContextMap(); final Statement statement = execution.getMondrianStatement(); // Cleanup any previous executions still running synchronized (statement) { final Execution previousExecution = statement.getCurrentExecution(); if (previousExecution != null) { statement.end(previousExecution); } } final Query query = statement.getQuery(); final MemoryMonitor.Listener listener = new MemoryMonitor.Listener() { public void memoryUsageNotification(long used, long max) { execution.setOutOfMemory( "OutOfMemory used=" + used + ", max=" + max + " for connection: " + getConnectString()); } }; MemoryMonitor mm = MemoryMonitorFactory.getMemoryMonitor(); final long currId = execution.getId(); try { mm.addListener(listener); // Check to see if we must punt execution.checkCancelOrTimeout(); if (LOGGER.isDebugEnabled()) { LOGGER.debug(Util.unparse(query)); } if (RolapUtil.MDX_LOGGER.isDebugEnabled()) { RolapUtil.MDX_LOGGER.debug(currId + ": " + Util.unparse(query)); } final Locus locus = new Locus(execution, null, "Loading cells"); Locus.push(locus); Result result; try { statement.start(execution); ((RolapCube) query.getCube()).clearCachedAggregations(true); result = new RolapResult(execution, true); int i = 0; for (QueryAxis axis : query.getAxes()) { if (axis.isNonEmpty()) { result = new NonEmptyResult(result, execution, i); } ++i; } } finally { Locus.pop(locus); ((RolapCube) query.getCube()).clearCachedAggregations(true); } statement.end(execution); return result; } catch (ResultLimitExceededException e) { // query has been punted throw e; } catch (Exception e) { try { if (!execution.isCancelOrTimeout()) { statement.end(execution); } } catch (Exception e1) { // We can safely ignore that cleanup exception. // If an error is encountered here, it means that // one was already encountered at statement.start() // above and the exception we will throw after the // cleanup is the same as the original one. } String queryString; try { queryString = Util.unparse(query); } catch (Exception e1) { queryString = "?"; } throw Util.newError(e, "Error while executing query [" + queryString + "]"); } finally { mm.removeListener(listener); if (RolapUtil.MDX_LOGGER.isDebugEnabled()) { final long elapsed = execution.getElapsedMillis(); RolapUtil.MDX_LOGGER.debug(currId + ": exec: " + elapsed + " ms"); } } } public void setRole(Role role) { assert role != null; this.role = role; this.schemaReader = new RolapSchemaReader(role, schema); } public Role getRole() { Util.assertPostcondition(role != null, "role != null"); return role; } public void setScenario(Scenario scenario) { this.scenario = scenario; } public Scenario getScenario() { return scenario; } /** * Returns the server (mondrian instance) that this connection belongs to. * Usually there is only one server instance in a given JVM. * * @return Server instance; never null */ public MondrianServer getServer() { return server; } public QueryPart parseStatement(String query) { Statement statement = createInternalStatement(false); final Locus locus = new Locus(new Execution(statement, 0), "Parse/validate MDX statement", null); Locus.push(locus); try { QueryPart queryPart = parseStatement(statement, query, null, false); if (queryPart instanceof Query) { ((Query) queryPart).setOwnStatement(true); statement = null; } return queryPart; } finally { Locus.pop(locus); if (statement != null) { statement.close(); } } } public Exp parseExpression(String expr) { boolean debug = false; if (getLogger().isDebugEnabled()) { //debug = true; getLogger().debug(Util.nl + expr); } final Statement statement = getInternalStatement(); try { MdxParserValidator parser = createParser(); final FunTable funTable = getSchema().getFunTable(); return parser.parseExpression(statement, expr, debug, funTable); } catch (Throwable exception) { throw MondrianResource.instance().FailedToParseQuery.ex(expr, exception); } } public Statement getInternalStatement() { if (internalStatement == null) { return schema.getInternalConnection().getInternalStatement(); } else { return internalStatement; } } private Statement createInternalStatement(boolean reentrant) { final Statement statement = reentrant ? new ReentrantInternalStatement() : new InternalStatement(); server.addStatement(statement); return statement; } /** * Implementation of {@link DataSource} which calls the good ol' * {@link java.sql.DriverManager}. * * <p>Overrides {@link #hashCode()} and {@link #equals(Object)} so that * {@link Dialect} objects can be cached more effectively. */ private static class DriverManagerDataSource implements DataSource { private final String jdbcConnectString; private PrintWriter logWriter; private int loginTimeout; private Properties jdbcProperties; public DriverManagerDataSource(String jdbcConnectString, Properties properties) { this.jdbcConnectString = jdbcConnectString; this.jdbcProperties = properties; } @Override public int hashCode() { int h = loginTimeout; h = Util.hash(h, jdbcConnectString); h = Util.hash(h, jdbcProperties); return h; } @Override public boolean equals(Object obj) { if (obj instanceof DriverManagerDataSource) { DriverManagerDataSource that = (DriverManagerDataSource) obj; return this.loginTimeout == that.loginTimeout && this.jdbcConnectString.equals(that.jdbcConnectString) && this.jdbcProperties.equals(that.jdbcProperties); } return false; } public Connection getConnection() throws SQLException { return new org.apache.commons.dbcp.DelegatingConnection( java.sql.DriverManager.getConnection(jdbcConnectString, jdbcProperties)); } public Connection getConnection(String username, String password) throws SQLException { if (jdbcProperties == null) { return java.sql.DriverManager.getConnection(jdbcConnectString, username, password); } else { Properties temp = (Properties) jdbcProperties.clone(); temp.put("user", username); temp.put("password", password); return java.sql.DriverManager.getConnection(jdbcConnectString, temp); } } 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; } public java.util.logging.Logger getParentLogger() { return java.util.logging.Logger.getLogger(""); } public <T> T unwrap(Class<T> iface) throws SQLException { throw new SQLException("not a wrapper"); } public boolean isWrapperFor(Class<?> iface) throws SQLException { return false; } } public DataSource getDataSource() { return dataSource; } /** * Helper method to allow olap4j wrappers to implement * {@link org.olap4j.OlapConnection#createScenario()}. * * @return new Scenario */ public ScenarioImpl createScenario() { final ScenarioImpl scenario = new ScenarioImpl(); scenario.register(schema); return scenario; } /** * A <code>NonEmptyResult</code> filters a result by removing empty rows * on a particular axis. */ static class NonEmptyResult extends ResultBase { final Result underlying; private final int axis; private final Map<Integer, Integer> map; /** workspace. Synchronized access only. */ private final int[] pos; /** * Creates a NonEmptyResult. * * @param result Result set * @param execution Execution context * @param axis Which axis to make non-empty */ NonEmptyResult(Result result, Execution execution, int axis) { super(execution, result.getAxes().clone()); this.underlying = result; this.axis = axis; this.map = new HashMap<Integer, Integer>(); int axisCount = underlying.getAxes().length; this.pos = new int[axisCount]; this.slicerAxis = underlying.getSlicerAxis(); TupleList tupleList = ((RolapAxis) underlying.getAxes()[axis]).getTupleList(); final TupleList filteredTupleList; if (!tupleList.isEmpty() && tupleList.get(0).get(0).getDimension().isHighCardinality()) { filteredTupleList = new DelegatingTupleList(tupleList.getArity(), new FilteredIterableList<List<Member>>(tupleList, new FilteredIterableList.Filter<List<Member>>() { public boolean accept(final List<Member> p) { return p.get(0) != null; } })); } else { filteredTupleList = TupleCollections.createList(tupleList.getArity()); int i = -1; TupleCursor tupleCursor = tupleList.tupleCursor(); while (tupleCursor.forward()) { ++i; if (!isEmpty(i, axis)) { map.put(filteredTupleList.size(), i); filteredTupleList.addCurrent(tupleCursor); } } } this.axes[axis] = new RolapAxis(filteredTupleList); } protected Logger getLogger() { return LOGGER; } /** * Returns true if all cells at a given offset on a given axis are * empty. For example, in a 2x2x2 dataset, <code>isEmpty(1,0)</code> * returns true if cells <code>{(1,0,0), (1,0,1), (1,1,0), * (1,1,1)}</code> are all empty. As you can see, we hold the 0th * coordinate fixed at 1, and vary all other coordinates over all * possible values. */ private boolean isEmpty(int offset, int fixedAxis) { int axisCount = getAxes().length; pos[fixedAxis] = offset; return isEmptyRecurse(fixedAxis, axisCount - 1); } private boolean isEmptyRecurse(int fixedAxis, int axis) { if (axis < 0) { RolapCell cell = (RolapCell) underlying.getCell(pos); return cell.isNull(); } else if (axis == fixedAxis) { return isEmptyRecurse(fixedAxis, axis - 1); } else { List<Position> positions = getAxes()[axis].getPositions(); final int positionCount = positions.size(); for (int i = 0; i < positionCount; i++) { pos[axis] = i; if (!isEmptyRecurse(fixedAxis, axis - 1)) { return false; } } return true; } } // synchronized because we use 'pos' public synchronized Cell getCell(int[] externalPos) { try { System.arraycopy(externalPos, 0, this.pos, 0, externalPos.length); int offset = externalPos[axis]; int mappedOffset = mapOffsetToUnderlying(offset); this.pos[axis] = mappedOffset; return underlying.getCell(this.pos); } catch (NullPointerException npe) { return underlying.getCell(externalPos); } } private int mapOffsetToUnderlying(int offset) { return map.get(offset); } public void close() { underlying.close(); } } /** * Data source that delegates all methods to an underlying data source. */ private static abstract class DelegatingDataSource implements DataSource { protected final DataSource dataSource; public DelegatingDataSource(DataSource dataSource) { this.dataSource = dataSource; } public Connection getConnection() throws SQLException { return dataSource.getConnection(); } public Connection getConnection(String username, String password) throws SQLException { return dataSource.getConnection(username, password); } public PrintWriter getLogWriter() throws SQLException { return dataSource.getLogWriter(); } public void setLogWriter(PrintWriter out) throws SQLException { dataSource.setLogWriter(out); } public void setLoginTimeout(int seconds) throws SQLException { dataSource.setLoginTimeout(seconds); } public int getLoginTimeout() throws SQLException { return dataSource.getLoginTimeout(); } // JDBC 4.0 support (JDK 1.6 and higher) public <T> T unwrap(Class<T> iface) throws SQLException { if (Util.JdbcVersion >= 0x0400) { // Do // return dataSource.unwrap(iface); // via reflection. try { Method method = DataSource.class.getMethod("unwrap", Class.class); return iface.cast(method.invoke(dataSource, iface)); } catch (IllegalAccessException e) { throw Util.newInternal(e, "While invoking unwrap"); } catch (InvocationTargetException e) { throw Util.newInternal(e, "While invoking unwrap"); } catch (NoSuchMethodException e) { throw Util.newInternal(e, "While invoking unwrap"); } } else { if (iface.isInstance(dataSource)) { return iface.cast(dataSource); } else { return null; } } } // JDBC 4.0 support (JDK 1.6 and higher) public boolean isWrapperFor(Class<?> iface) throws SQLException { if (Util.JdbcVersion >= 0x0400) { // Do // return dataSource.isWrapperFor(iface); // via reflection. try { Method method = DataSource.class.getMethod("isWrapperFor", boolean.class); return (Boolean) method.invoke(dataSource, iface); } catch (IllegalAccessException e) { throw Util.newInternal(e, "While invoking isWrapperFor"); } catch (InvocationTargetException e) { throw Util.newInternal(e, "While invoking isWrapperFor"); } catch (NoSuchMethodException e) { throw Util.newInternal(e, "While invoking isWrapperFor"); } } else { return iface.isInstance(dataSource); } } // JDBC 4.1 support (JDK 1.7 and higher) public java.util.logging.Logger getParentLogger() { if (Util.JdbcVersion >= 0x0401) { // Do // return dataSource.getParentLogger(); // via reflection. try { Method method = DataSource.class.getMethod("getParentLogger"); return (java.util.logging.Logger) method.invoke(dataSource); } catch (IllegalAccessException e) { throw Util.newInternal(e, "While invoking getParentLogger"); } catch (InvocationTargetException e) { throw Util.newInternal(e, "While invoking getParentLogger"); } catch (NoSuchMethodException e) { throw Util.newInternal(e, "While invoking getParentLogger"); } } else { // Can't throw SQLFeatureNotSupportedException... it doesn't // exist before JDBC 4.1. throw new UnsupportedOperationException(); } } } /** * Data source that gets connections from an underlying data source but * with different user name and password. */ private static class UserPasswordDataSource extends DelegatingDataSource { private final String jdbcUser; private final String jdbcPassword; /** * Creates a UserPasswordDataSource * * @param dataSource Underlying data source * @param jdbcUser User name * @param jdbcPassword Password */ public UserPasswordDataSource(DataSource dataSource, String jdbcUser, String jdbcPassword) { super(dataSource); this.jdbcUser = jdbcUser; this.jdbcPassword = jdbcPassword; } public Connection getConnection() throws SQLException { return dataSource.getConnection(jdbcUser, jdbcPassword); } } /** * <p>Implementation of {@link Statement} for use when you don't have an * olap4j connection.</p> */ private class InternalStatement extends StatementImpl { private boolean closed = false; public void close() { if (!closed) { closed = true; server.removeStatement(this); } } public RolapConnection getMondrianConnection() { return RolapConnection.this; } } /** * <p>A statement that can be used for all of the various internal * operations, such as resolving MDX identifiers, that require a * {@link Statement} and an {@link Execution}. * * <p>The statement needs to be reentrant because there are many such * operations; several of these operations might be active at one time. We * don't want to create a new statement for each, but just one internal * statement for each connection. The statement shouldn't have a unique * execution. For this reason, we don't use the inherited {@link #execution} * field.</p> * * <p>But there is a drawback. If we can't find the unique execution, the * statement cannot be canceled or time out. If you want that behavior * from an internal statement, use the base class: create a new * {@link InternalStatement} for each operation.</p> */ private class ReentrantInternalStatement extends InternalStatement { @Override public void start(Execution execution) { // Unlike StatementImpl, there is not a unique execution. An // internal statement can execute several at the same time. So, // we don't set this.execution. execution.start(); } @Override public void end(Execution execution) { execution.end(); } @Override public void close() { // do not close } } } // End RolapConnection.java