Example #1
0
 /**
  * Computes the number of atomic cells in a cell identified by a list of members.
  *
  * <p>The method may be expensive. If the value is not in the cache, computes it immediately using
  * a database access. It uses an aggregate table if applicable, and puts the value into the cache.
  *
  * <p>Compare with {@link #evaluateAtomicCellCount(RolapEvaluator)}, which gets the value from the
  * cache but may lie (and generate a cache miss) if the value is not present.
  *
  * @param cube Cube
  * @param memberList Coordinate members of cell
  * @return Number of atomic cells in cell
  */
 private static double computeAtomicCellCount(RolapCube cube, List<RolapMember> memberList) {
   // Implementation generates and executes a recursive MDX query. This
   // may not be the most efficient implementation, but achieves the
   // design goals of (a) immediacy, (b) cache use, (c) aggregate table
   // use.
   final StringBuilder buf = new StringBuilder();
   buf.append("select from ");
   buf.append(cube.getUniqueName());
   int k = 0;
   for (Member member : memberList) {
     if (member.isMeasure()) {
       member = cube.factCountMeasure;
       assert member != null : "fact count measure is required for writeback cubes";
     }
     if (!member.equals(member.getHierarchy().getDefaultMember())) {
       if (k++ > 0) {
         buf.append(", ");
       } else {
         buf.append(" where (");
       }
       buf.append(member.getUniqueName());
     }
   }
   if (k > 0) {
     buf.append(")");
   }
   final String mdx = buf.toString();
   final RolapConnection connection = cube.getSchema().getInternalConnection();
   final Query query = connection.parseQuery(mdx);
   final Result result = connection.execute(query);
   final Object o = result.getCell(new int[0]).getValue();
   return o instanceof Number ? ((Number) o).doubleValue() : 0d;
 }
  public void testJndiConnection() throws NamingException {
    // Cannot guarantee that this test will work if they have chosen to
    // resolve data sources other than by JNDI.
    if (MondrianProperties.instance().DataSourceResolverClass.isSet()) {
      return;
    }
    // get a regular connection
    Util.PropertyList properties = TestContext.instance().getConnectionProperties().clone();
    final StringBuilder buf = new StringBuilder();
    final DataSource dataSource = RolapConnection.createDataSource(null, properties, buf);
    // Don't know what the connect string is - it differs with database
    // and with the user's set up - but we know that it contains a JDBC
    // connect string. Best we can do is check that createDataSource is
    // setting it to something.
    final String desc = buf.toString();
    assertTrue(desc, desc.startsWith("Jdbc="));

    final List<String> lookupCalls = new ArrayList<String>();
    // mock the JNDI naming manager to provide that datasource
    THREAD_INITIAL_CONTEXT.set(
        // Use lazy initialization. Otherwise during initialization of this
        // initial context JNDI tries to create a default initial context
        // and bumps into itself coming the other way.
        new InitialContext(true) {
          public Object lookup(String str) {
            lookupCalls.add("Called");
            return dataSource;
          }
        });

    // Use the datasource property to connect to the database.
    // Remove user and password, because some data sources (those using
    // pools) don't allow you to override user.
    Util.PropertyList properties2 = TestContext.instance().getConnectionProperties().clone();
    properties2.remove(RolapConnectionProperties.Jdbc.name());
    properties2.remove(RolapConnectionProperties.JdbcUser.name());
    properties2.remove(RolapConnectionProperties.JdbcPassword.name());
    properties2.put(RolapConnectionProperties.DataSource.name(), "jnditest");
    DriverManager.getConnection(properties2, null);

    // if we've made it here with lookupCalls,
    // we've successfully used JNDI
    assertTrue(lookupCalls.size() > 0);
  }
  public void testPooledConnectionWithProperties() throws SQLException {
    Util.PropertyList properties = TestContext.instance().getConnectionProperties().clone();

    // Only the JDBC-ODBC bridge gives the error necessary for this
    // test to succeed. So trivially succeed for all other JDBC
    // drivers.
    final String jdbc = properties.get("Jdbc");
    if (jdbc != null && !jdbc.startsWith("jdbc:odbc:")) {
      return;
    }

    // JDBC-ODBC driver does not support UTF-16, so this test succeeds
    // because creating the connection from the DataSource will fail.
    properties.put("jdbc.charSet", "UTF-16");

    final StringBuilder buf = new StringBuilder();
    DataSource dataSource = RolapConnection.createDataSource(null, properties, buf);
    final String desc = buf.toString();
    assertTrue(desc.startsWith("Jdbc="));

    Connection connection;
    try {
      connection = dataSource.getConnection();
      connection.close();
      fail("Expected exception");
    } catch (SQLException e) {
      if (e.getClass().getName().equals("org.apache.commons.dbcp.DbcpException")) {
        // This is expected. (We use string-comparison so that the
        // compiler doesn't warn about using a deprecated class.)
      } else if (e.getClass() == SQLException.class
          && e.getCause() == null
          && e.getMessage() != null
          && e.getMessage().equals("")) {
        // This is expected, from a later version of Dbcp.
      } else {
        fail("Expected exception, but got a different one: " + e);
      }
    } catch (IllegalArgumentException e) {
      handleIllegalArgumentException(properties, e);
    } finally {
      RolapConnectionPool.instance().clearPool();
    }
  }
  public void testNonPooledConnectionWithProperties() {
    Util.PropertyList properties = TestContext.instance().getConnectionProperties().clone();

    // Only the JDBC-ODBC bridge gives the error necessary for this
    // test to succeed. So trivially succeed for all other JDBC
    // drivers.
    final String jdbc = properties.get("Jdbc");
    if (jdbc != null && !jdbc.startsWith("jdbc:odbc:")) {
      return;
    }

    // This test is just like the test testPooledConnectionWithProperties
    // except with non-pooled connections.
    properties.put("jdbc.charSet", "UTF-16");
    properties.put(RolapConnectionProperties.PoolNeeded.name(), "false");

    final StringBuilder buf = new StringBuilder();
    DataSource dataSource = RolapConnection.createDataSource(null, properties, buf);
    final String desc = buf.toString();
    assertTrue(desc.startsWith("Jdbc="));

    Connection connection = null;
    try {
      connection = dataSource.getConnection();
      fail("Expected exception");
    } catch (SQLException se) {
      // this is expected
    } catch (IllegalArgumentException e) {
      handleIllegalArgumentException(properties, e);
    } finally {
      if (connection != null) {
        try {
          connection.close();
        } catch (SQLException e) {
          // ignore
        }
      }
    }
  }
  /**
   * Handle an {@link IllegalArgumentException} which occurs when have tried to create a connection
   * with an illegal charset.
   */
  private void handleIllegalArgumentException(
      Util.PropertyList properties, IllegalArgumentException e) {
    // Workaround Java bug #6504538 (see http://bugs.sun.com) with synopsis
    // "DriverManager.getConnection throws IllegalArgumentException".
    if (System.getProperties().getProperty("java.version").startsWith("1.6.")) {
      properties.remove("jdbc.charSet");

      final StringBuilder buf = new StringBuilder();
      DataSource dataSource = RolapConnection.createDataSource(null, properties, buf);
      final String desc = buf.toString();
      assertTrue(desc.startsWith("Jdbc="));

      try {
        Connection connection1 = dataSource.getConnection();
        connection1.close();
      } catch (SQLException e1) {
        // ignore
      }
    } else {
      fail("Expect IllegalArgumentException only in JDK 1.6, got " + e);
    }
  }
  public void testDataSourceOverrideUserPass() throws SQLException, NamingException {
    // use the datasource property to connect to the database
    Util.PropertyList properties = TestContext.instance().getConnectionProperties().clone();
    final Dialect dialect = TestContext.instance().getDialect();
    if (dialect.getDatabaseProduct() == Dialect.DatabaseProduct.ACCESS) {
      // Access doesn't accept user/password, so this test is pointless.
      return;
    }

    final String jdbcUser = properties.get(RolapConnectionProperties.JdbcUser.name());
    final String jdbcPassword = properties.get(RolapConnectionProperties.JdbcPassword.name());
    if (jdbcUser == null || jdbcPassword == null) {
      // Can only run this test if username and password are explicit.
      return;
    }

    // Define a data source with bogus user and password.
    properties.put(RolapConnectionProperties.JdbcUser.name(), "bogususer");
    properties.put(RolapConnectionProperties.JdbcPassword.name(), "boguspassword");
    properties.put(RolapConnectionProperties.PoolNeeded.name(), "false");
    final StringBuilder buf = new StringBuilder();
    final DataSource dataSource = RolapConnection.createDataSource(null, properties, buf);
    final String desc = buf.toString();
    assertTrue(desc, desc.startsWith("Jdbc="));
    assertTrue(desc, desc.indexOf("JdbcUser=bogususer; JdbcPassword=boguspassword") >= 0);
    final String jndiName = "jndiDataSource";
    THREAD_INITIAL_CONTEXT.set(
        new InitialContext() {
          public Object lookup(String str) {
            return str.equals(jndiName) ? dataSource : null;
          }
        });

    // Create a property list that we will use for the actual mondrian
    // connection. Replace the original JDBC info with the data source we
    // just created.
    final Util.PropertyList properties2 = new Util.PropertyList();
    for (Pair<String, String> entry : properties) {
      properties2.put(entry.getKey(), entry.getValue());
    }
    properties2.remove(RolapConnectionProperties.Jdbc.name());
    properties2.put(RolapConnectionProperties.DataSource.name(), jndiName);

    // With JdbcUser and JdbcPassword credentials in the mondrian connect
    // string, the data source's "user" and "password" properties are
    // overridden and the connection succeeds.
    properties2.put(RolapConnectionProperties.JdbcUser.name(), jdbcUser);
    properties2.put(RolapConnectionProperties.JdbcPassword.name(), jdbcPassword);
    mondrian.olap.Connection connection = null;
    try {
      connection = DriverManager.getConnection(properties2, null);
      Query query = connection.parseQuery("select from [Sales]");
      final Result result = connection.execute(query);
      assertNotNull(result);
    } finally {
      if (connection != null) {
        connection.close();
        connection = null;
      }
    }

    // If we don't specify JdbcUser and JdbcPassword in the mondrian
    // connection properties, mondrian uses the data source's
    // bogus credentials, and the connection fails.
    properties2.remove(RolapConnectionProperties.JdbcUser.name());
    properties2.remove(RolapConnectionProperties.JdbcPassword.name());
    for (String poolNeeded : Arrays.asList("false", "true")) {
      // Important to test with & without pooling. Connection pools
      // typically do not let you change user, so it's important that
      // mondrian handles these right.
      properties2.put(RolapConnectionProperties.PoolNeeded.name(), poolNeeded);
      try {
        connection = DriverManager.getConnection(properties2, null);
        fail("Expected exception");
      } catch (MondrianException e) {
        final String s = TestContext.getStackTrace(e);
        assertTrue(
            s,
            s.indexOf("Error while creating SQL connection: " + "DataSource=jndiDataSource") >= 0);
        switch (dialect.getDatabaseProduct()) {
          case DERBY:
            assertTrue(
                s,
                s.indexOf(
                        "Caused by: java.sql.SQLException: " + "Schema 'BOGUSUSER' does not exist")
                    >= 0);
            break;
          case ORACLE:
            assertTrue(
                s,
                s.indexOf(
                        "Caused by: java.sql.SQLException: ORA-01017: "
                            + "invalid username/password; logon denied")
                    >= 0);
            break;
          case MYSQL:
            assertTrue(
                s,
                s.indexOf(
                        "Caused by: java.sql.SQLException: Access denied " + "for user 'bogususer'")
                    >= 0);
            break;
          case POSTGRESQL:
            assertTrue(
                s,
                s.indexOf(
                        "Caused by: org.postgresql.util.PSQLException: "
                            + "FATAL: password authentication failed for "
                            + "user \"bogususer\"")
                    >= 0);
            break;
        }
      } finally {
        if (connection != null) {
          connection.close();
          connection = null;
        }
      }
    }
  }