1 13 package mondrian.rolap; 14 15 import mondrian.olap.*; 16 import mondrian.util.MemoryMonitor; 17 import mondrian.util.MemoryMonitorFactory; 18 import mondrian.rolap.agg.AggregationManager; 19 import org.apache.commons.dbcp.ConnectionFactory; 20 import org.apache.commons.dbcp.DataSourceConnectionFactory; 21 import org.apache.commons.dbcp.DriverManagerConnectionFactory; 22 23 import org.apache.log4j.Logger; 24 import javax.naming.InitialContext ; 25 import javax.naming.NamingException ; 26 import javax.sql.DataSource ; 27 import java.io.PrintWriter ; 28 import java.io.StringWriter ; 29 import java.sql.Connection ; 30 import java.sql.SQLException ; 31 import java.util.*; 32 33 46 public class RolapConnection extends ConnectionBase { 47 private static final Logger LOGGER = Logger.getLogger(RolapConnection.class); 48 49 private final Util.PropertyList connectInfo; 50 51 55 private final DataSource dataSource; 56 private final String catalogUrl; 57 private final RolapSchema schema; 58 private SchemaReader schemaReader; 59 protected Role role; 60 private Locale locale = Locale.US; 61 62 68 public RolapConnection(Util.PropertyList connectInfo) { 69 this(connectInfo, null, null); 70 } 71 72 78 public RolapConnection(Util.PropertyList connectInfo, DataSource datasource) { 79 this(connectInfo, null, datasource); 80 } 81 82 95 RolapConnection(Util.PropertyList connectInfo, RolapSchema schema) { 96 this(connectInfo, schema, null); 97 } 98 99 115 RolapConnection( 116 Util.PropertyList connectInfo, 117 RolapSchema schema, 118 DataSource dataSource) 119 { 120 super(); 121 122 String provider = connectInfo.get( 123 RolapConnectionProperties.Provider.name(), "mondrian"); 124 Util.assertTrue(provider.equalsIgnoreCase("mondrian")); 125 this.connectInfo = connectInfo; 126 this.catalogUrl = 127 connectInfo.get(RolapConnectionProperties.Catalog.name()); 128 this.dataSource = (dataSource != null) 129 ? dataSource 130 : createDataSource(connectInfo); 131 Role role = null; 132 if (schema == null) { 133 if (dataSource == null) { 136 final String jdbcConnectString = 140 connectInfo.get(RolapConnectionProperties.Jdbc.name()); 141 final String jdbcUser = 142 connectInfo.get(RolapConnectionProperties.JdbcUser.name()); 143 final String strDataSource = 144 connectInfo.get(RolapConnectionProperties.DataSource.name()); 145 final String connectionKey = jdbcConnectString + 146 getJDBCProperties(connectInfo).toString(); 147 148 schema = RolapSchema.Pool.instance().get( 149 catalogUrl, 150 connectionKey, 151 jdbcUser, 152 strDataSource, 153 connectInfo); 154 } else { 155 schema = RolapSchema.Pool.instance().get( 156 catalogUrl, 157 dataSource, 158 connectInfo); 159 } 160 String roleName = 161 connectInfo.get(RolapConnectionProperties.Role.name()); 162 if (roleName != null) { 163 role = schema.lookupRole(roleName); 164 if (role == null) { 165 throw Util.newError("Role '" + roleName + "' not found"); 166 } 167 } 168 } 169 if (role == null) { 170 role = schema.getDefaultRole(); 171 } 172 173 String localeString = 175 connectInfo.get(RolapConnectionProperties.Locale.name()); 176 if (localeString != null) { 177 String [] strings = localeString.split("_"); 178 switch (strings.length) { 179 case 1: 180 this.locale = new Locale(strings[0]); 181 break; 182 case 2: 183 this.locale = new Locale(strings[0], strings[1]); 184 break; 185 case 3: 186 this.locale = new Locale(strings[0], strings[1], strings[2]); 187 break; 188 default: 189 throw Util.newInternal("bad locale string '" + localeString + "'"); 190 } 191 } 192 193 this.schema = schema; 194 setRole(role); 195 } 196 197 protected Logger getLogger() { 198 return LOGGER; 199 } 200 201 202 static DataSource createDataSource(Util.PropertyList connectInfo) { 205 final String jdbcConnectString = 206 connectInfo.get(RolapConnectionProperties.Jdbc.name()); 207 final String poolNeededString = 208 connectInfo.get(RolapConnectionProperties.PoolNeeded.name()); 209 210 Properties jdbcProperties = getJDBCProperties(connectInfo); 211 String propertyString = jdbcProperties.toString(); 212 if (jdbcConnectString != null) { 213 String jdbcDrivers = 215 connectInfo.get(RolapConnectionProperties.JdbcDrivers.name()); 216 if (jdbcDrivers != null) { 217 RolapUtil.loadDrivers(jdbcDrivers); 218 } 219 final String jdbcDriversProp = 220 MondrianProperties.instance().JdbcDrivers.get(); 221 RolapUtil.loadDrivers(jdbcDriversProp); 222 223 final boolean poolNeeded = (poolNeededString == null) 224 ? true 227 : poolNeededString.equalsIgnoreCase("true"); 228 229 final String jdbcUser = 230 connectInfo.get(RolapConnectionProperties.JdbcUser.name()); 231 final String jdbcPassword = 232 connectInfo.get(RolapConnectionProperties.JdbcPassword.name()); 233 234 if (jdbcUser != null) { 235 jdbcProperties.put("user", jdbcUser); 236 } 237 if (jdbcPassword != null) { 238 jdbcProperties.put("password", jdbcPassword); 239 } 240 241 if (!poolNeeded) { 242 return new DriverManagerDataSource(jdbcConnectString, 244 jdbcProperties); 245 } 246 247 if (jdbcConnectString.toLowerCase().indexOf("mysql") > -1) { 248 jdbcProperties.setProperty("autoReconnect", "true"); 250 } 251 ConnectionFactory connectionFactory = 253 new DriverManagerConnectionFactory(jdbcConnectString , 254 jdbcProperties); 255 try { 256 return RolapConnectionPool.instance().getPoolingDataSource( 257 jdbcConnectString + propertyString, connectionFactory); 258 } catch (Throwable e) { 259 throw Util.newInternal(e, 260 "Error while creating connection pool (with URI " + 261 jdbcConnectString + ")"); 262 } 263 264 } else { 265 266 final String dataSourceName = 267 connectInfo.get(RolapConnectionProperties.DataSource.name()); 268 if (dataSourceName == null) { 269 throw Util.newInternal( 270 "Connect string '" + connectInfo.toString() + 271 "' must contain either '" + RolapConnectionProperties.Jdbc + 272 "' or '" + RolapConnectionProperties.DataSource + "'"); 273 } 274 275 final boolean poolNeeded = (poolNeededString == null) 276 ? false 279 : poolNeededString.equalsIgnoreCase("true"); 280 281 final DataSource dataSource; 283 try { 284 dataSource = 285 (DataSource ) new InitialContext ().lookup(dataSourceName); 286 } catch (NamingException e) { 287 throw Util.newInternal(e, 288 "Error while looking up data source (" + 289 dataSourceName + ")"); 290 } 291 if (!poolNeeded) { 292 return dataSource; 293 } 294 ConnectionFactory connectionFactory = 295 new DataSourceConnectionFactory(dataSource); 296 try { 297 return RolapConnectionPool.instance().getPoolingDataSource( 298 dataSourceName, connectionFactory); 299 } catch (Exception e) { 300 throw Util.newInternal(e, 301 "Error while creating connection pool (with URI " + 302 dataSourceName + ")"); 303 } 304 } 305 } 306 307 315 private static Properties getJDBCProperties(Util.PropertyList connectInfo) { 316 Properties jdbcProperties = new Properties(); 317 Iterator<String []> iterator = connectInfo.iterator(); 318 while (iterator.hasNext()) { 319 String [] entry = iterator.next(); 320 if (entry[0].startsWith(RolapConnectionProperties.JdbcPropertyPrefix)) { 321 jdbcProperties.put(entry[0].substring(RolapConnectionProperties.JdbcPropertyPrefix.length()), entry[1]); 322 } 323 } 324 return jdbcProperties; 325 } 326 327 public Util.PropertyList getConnectInfo() { 328 return connectInfo; 329 } 330 331 public void close() { 332 } 333 334 public Schema getSchema() { 335 return schema; 336 } 337 338 public String getConnectString() { 339 return connectInfo.toString(); 340 } 341 342 public String getCatalogName() { 343 return catalogUrl; 344 } 345 346 public Locale getLocale() { 347 return locale; 348 } 349 350 public void setLocale(Locale locale) { 351 this.locale = locale; 352 } 353 354 public SchemaReader getSchemaReader() { 355 return schemaReader; 356 } 357 358 public Object getProperty(String name) { 359 if (name.equals(RolapConnectionProperties.JdbcPassword.name()) || 361 name.equals(RolapConnectionProperties.CatalogContent.name())) { 362 return ""; 363 } 364 return connectInfo.get(name); 365 } 366 367 public CacheControl getCacheControl(PrintWriter pw) { 368 return AggregationManager.instance().getCacheControl(pw); 369 } 370 371 380 public Result execute(Query query) { 381 class Listener implements MemoryMonitor.Listener { 382 private final Query query; 383 Listener(final Query query) { 384 this.query = query; 385 } 386 public void memoryUsageNotification(long used, long max) { 387 StringBuilder buf = new StringBuilder (200); 388 buf.append("OutOfMemory used="); 389 buf.append(used); 390 buf.append(", max="); 391 buf.append(max); 392 buf.append(" for connection: "); 393 buf.append(getConnectString()); 394 RolapConnection.memoryUsageNotification(query, buf.toString()); 397 } 398 } 399 Listener listener = new Listener(query); 400 MemoryMonitor mm = MemoryMonitorFactory.getMemoryMonitor(); 401 try { 402 mm.addListener(listener); 403 query.checkCancelOrTimeout(); 405 406 if (LOGGER.isDebugEnabled()) { 407 LOGGER.debug(Util.unparse(query)); 408 } 409 query.setQueryStartTime(); 410 Result result = new RolapResult(query, true); 411 for (int i = 0; i < query.axes.length; i++) { 412 QueryAxis axis = query.axes[i]; 413 if (axis.isNonEmpty()) { 414 result = new NonEmptyResult(result, query, i); 415 } 416 } 417 if (LOGGER.isDebugEnabled()) { 418 StringWriter sw = new StringWriter (); 419 PrintWriter pw = new PrintWriter (sw); 420 result.print(pw); 421 pw.flush(); 422 LOGGER.debug(sw.toString()); 423 } 424 query.setQueryEndExecution(); 425 return result; 426 427 } catch (ResultLimitExceededException e) { 428 throw e; 430 } catch (Exception e) { 431 String queryString; 432 query.setQueryEndExecution(); 433 try { 434 queryString = Util.unparse(query); 435 } catch (Exception e1) { 436 queryString = "?"; 437 } 438 throw Util.newError(e, "Error while executing query [" + 439 queryString + "]"); 440 } finally { 441 mm.removeListener(listener); 442 } 443 } 444 445 public void setRole(Role role) { 446 assert role != null; 447 assert !role.isMutable(); 448 449 this.role = role; 450 this.schemaReader = new RolapSchemaReader(role, schema) { 451 public Cube getCube() { 452 throw new UnsupportedOperationException (); 453 } 454 }; 455 } 456 457 public Role getRole() { 458 Util.assertPostcondition(role != null, "role != null"); 459 Util.assertPostcondition(!role.isMutable(), "!role.isMutable()"); 460 461 return role; 462 } 463 464 468 private static class DriverManagerDataSource implements DataSource { 469 private final String jdbcConnectString; 470 private PrintWriter logWriter; 471 private int loginTimeout; 472 private Properties jdbcProperties; 473 474 public DriverManagerDataSource(String jdbcConnectString, 475 Properties properties) { 476 this.jdbcConnectString = jdbcConnectString; 477 this.jdbcProperties = properties; 478 } 479 480 public Connection getConnection() throws SQLException { 481 return new org.apache.commons.dbcp.DelegatingConnection( 482 java.sql.DriverManager.getConnection( 483 jdbcConnectString, jdbcProperties)); 484 } 485 486 public Connection getConnection(String username, String password) 487 throws SQLException { 488 if (jdbcProperties == null) { 489 return java.sql.DriverManager.getConnection(jdbcConnectString, 490 username, password); 491 } else { 492 Properties temp = (Properties)jdbcProperties.clone(); 493 temp.put("user", username); 494 temp.put("password", password); 495 return java.sql.DriverManager.getConnection(jdbcConnectString, temp); 496 } 497 } 498 499 public PrintWriter getLogWriter() throws SQLException { 500 return logWriter; 501 } 502 503 public void setLogWriter(PrintWriter out) throws SQLException { 504 logWriter = out; 505 } 506 507 public void setLoginTimeout(int seconds) throws SQLException { 508 loginTimeout = seconds; 509 } 510 511 public int getLoginTimeout() throws SQLException { 512 return loginTimeout; 513 } 514 515 public <T> T unwrap(Class <T> iface) throws SQLException { 516 throw new SQLException ("not a wrapper"); 517 } 518 519 public boolean isWrapperFor(Class <?> iface) throws SQLException { 520 return false; 521 } 522 } 523 524 public DataSource getDataSource() { 525 return dataSource; 526 } 527 528 532 class NonEmptyResult extends ResultBase { 533 534 final Result underlying; 535 private final int axis; 536 private final Map<Integer , Integer > map; 537 538 private final int[] pos; 539 540 NonEmptyResult(Result result, Query query, int axis) { 541 super(query, result.getAxes().clone()); 542 543 this.underlying = result; 544 this.axis = axis; 545 this.map = new HashMap<Integer , Integer >(); 546 int axisCount = underlying.getAxes().length; 547 this.pos = new int[axisCount]; 548 this.slicerAxis = underlying.getSlicerAxis(); 549 List<Position> positions = underlying.getAxes()[axis].getPositions(); 550 551 List<Position> positionsList = new ArrayList<Position>(); 552 int i = 0; 553 for (Position position: positions) { 554 if (! isEmpty(i, axis)) { 555 map.put(positionsList.size(), i); 556 positionsList.add(position); 557 } 558 i++; 559 } 560 this.axes[axis] = new RolapAxis.PositionList(positionsList); 561 } 562 563 protected Logger getLogger() { 564 return LOGGER; 565 } 566 567 575 private boolean isEmpty(int offset, int fixedAxis) { 576 int axisCount = getAxes().length; 577 pos[fixedAxis] = offset; 578 return isEmptyRecurse(fixedAxis, axisCount - 1); 579 } 580 581 private boolean isEmptyRecurse(int fixedAxis, int axis) { 582 if (axis < 0) { 583 RolapCell cell = (RolapCell) underlying.getCell(pos); 584 return cell.isNull(); 585 } else if (axis == fixedAxis) { 586 return isEmptyRecurse(fixedAxis, axis - 1); 587 } else { 588 List<Position> positions = getAxes()[axis].getPositions(); 589 int i = 0; 590 for (Position position: positions) { 591 pos[axis] = i; 592 if (!isEmptyRecurse(fixedAxis, axis - 1)) { 593 return false; 594 } 595 i++; 596 } 597 return true; 598 } 599 } 600 601 public synchronized Cell getCell(int[] externalPos) { 603 System.arraycopy(externalPos, 0, this.pos, 0, externalPos.length); 604 int offset = externalPos[axis]; 605 int mappedOffset = mapOffsetToUnderlying(offset); 606 this.pos[axis] = mappedOffset; 607 return underlying.getCell(this.pos); 608 } 609 610 private int mapOffsetToUnderlying(int offset) { 611 return map.get(offset); 612 } 613 614 public void close() { 615 underlying.close(); 616 } 617 } 618 } 619 620 | Popular Tags |