001 /* 002 // $Id: //open/mondrian/src/main/mondrian/rolap/RolapCubeHierarchy.java#13 $ 003 // This software is subject to the terms of the Common Public License 004 // Agreement, available at the following URL: 005 // http://www.opensource.org/licenses/cpl.html. 006 // Copyright (C) 2001-2002 Kana Software, Inc. 007 // Copyright (C) 2001-2008 Julian Hyde and others 008 // All Rights Reserved. 009 // You must accept the terms of that agreement to use this software. 010 // 011 // wgorman, 19 October 2007 012 */ 013 package mondrian.rolap; 014 015 import java.sql.ResultSet; 016 import java.sql.SQLException; 017 import java.util.ArrayList; 018 import java.util.Collections; 019 import java.util.HashMap; 020 import java.util.Iterator; 021 import java.util.List; 022 import java.util.Map; 023 024 import mondrian.olap.Formula; 025 import mondrian.olap.Level; 026 import mondrian.olap.Member; 027 import mondrian.olap.MondrianDef; 028 import mondrian.olap.MondrianProperties; 029 import mondrian.olap.Property; 030 import mondrian.olap.Util; 031 import mondrian.rolap.TupleReader.MemberBuilder; 032 import mondrian.rolap.sql.MemberChildrenConstraint; 033 import mondrian.rolap.sql.TupleConstraint; 034 import mondrian.util.UnsupportedList; 035 036 /** 037 * Hierarchy that is associated with a specific Cube. 038 * 039 * @author Will Gorman (wgorman@pentaho.org) 040 * @version $Id: //open/mondrian/src/main/mondrian/rolap/RolapCubeHierarchy.java#13 $ 041 */ 042 public class RolapCubeHierarchy extends RolapHierarchy { 043 044 private final RolapCubeDimension parentDimension; 045 private final RolapHierarchy rolapHierarchy; 046 private final RolapCubeLevel currentNullLevel; 047 private RolapNullMember currentNullMember; 048 private RolapCubeMember currentAllMember; 049 private final MondrianDef.RelationOrJoin currentRelation; 050 private final RolapCubeHierarchyMemberReader reader; 051 private HierarchyUsage usage; 052 private final Map<String, String> aliases = new HashMap<String, String>(); 053 private RolapCubeMember currentDefaultMember; 054 055 /** 056 * True if the hierarchy is degenerate - has no dimension table of its own, 057 * just drives from the cube's fact table. 058 */ 059 protected final boolean usingCubeFact; 060 061 public RolapCubeHierarchy( 062 RolapCubeDimension dimension, 063 MondrianDef.CubeDimension cubeDim, 064 RolapHierarchy rolapHierarchy, 065 String subName) 066 { 067 super(dimension, subName, rolapHierarchy.hasAll()); 068 069 if (!dimension.getCube().isVirtual()) { 070 this.usage = 071 new HierarchyUsage(dimension.getCube(), rolapHierarchy, cubeDim); 072 } 073 074 this.rolapHierarchy = rolapHierarchy; 075 this.parentDimension = dimension; 076 this.xmlHierarchy = rolapHierarchy.getXmlHierarchy(); 077 // this relation should equal the name of the new dimension table 078 // The null member belongs to a level with very similar properties to 079 // the 'all' level. 080 this.currentNullLevel = new RolapCubeLevel(nullLevel, this); 081 082 usingCubeFact = 083 (dimension.getCube().getFact() == null 084 || dimension.getCube().getFact().equals( 085 rolapHierarchy.getRelation())); 086 087 // re-alias names if necessary 088 if (!usingCubeFact) { 089 // join expressions are columns only 090 assert usage.getJoinExp() instanceof MondrianDef.Column; 091 currentRelation = 092 parentDimension.getCube().getStar().getUniqueRelation( 093 rolapHierarchy.getRelation(), 094 usage.getForeignKey(), 095 ((MondrianDef.Column)usage.getJoinExp()).getColumnName(), 096 usage.getJoinTable().getAlias()); 097 } else { 098 currentRelation = rolapHierarchy.getRelation(); 099 } 100 extractNewAliases(rolapHierarchy.getRelation(), currentRelation); 101 this.relation = currentRelation; 102 this.levels = new RolapCubeLevel[rolapHierarchy.getLevels().length]; 103 for (int i = 0; i < rolapHierarchy.getLevels().length; i++) { 104 this.levels[i] = 105 new RolapCubeLevel( 106 (RolapLevel)rolapHierarchy.getLevels()[i], this); 107 if (i == 0) { 108 if (rolapHierarchy.getAllMember() != null) { 109 RolapCubeLevel allLevel; 110 if (hasAll()) { 111 allLevel = (RolapCubeLevel)this.levels[0]; 112 } else { 113 // create an all level if one doesn't normally 114 // exist in the hierarchy 115 allLevel = 116 new RolapCubeLevel( 117 rolapHierarchy.getAllMember().getLevel(), 118 this); 119 allLevel.init(dimension.xmlDimension); 120 } 121 122 this.currentAllMember = 123 new RolapCubeMember( 124 null, 125 rolapHierarchy.getAllMember(), 126 allLevel, 127 dimension.getCube()); 128 } 129 } 130 } 131 132 if (dimension.isHighCardinality()) { 133 this.reader = new NoCacheRolapCubeHierarchyMemberReader(); 134 } else { 135 this.reader = new CacheRolapCubeHierarchyMemberReader(); 136 } 137 } 138 139 public String getAllMemberName() { 140 return rolapHierarchy.getAllMemberName(); 141 } 142 143 public String getSharedHierarchyName() { 144 return rolapHierarchy.getSharedHierarchyName(); 145 } 146 147 public String getAllLevelName() { 148 return rolapHierarchy.getAllLevelName(); 149 } 150 151 public boolean isUsingCubeFact() { 152 return usingCubeFact; 153 } 154 155 public String lookupAlias(String origTable) { 156 return aliases.get(origTable); 157 } 158 159 // override with stricter return type 160 public RolapCubeDimension getDimension() { 161 return (RolapCubeDimension) super.getDimension(); 162 } 163 164 public RolapHierarchy getRolapHierarchy() { 165 return rolapHierarchy; 166 } 167 168 /** 169 * Populates the alias map for the old and new relations. 170 * 171 * <p>This method may be simplified when we obsolete 172 * {@link mondrian.rolap.HierarchyUsage}. 173 * 174 * @param oldrel Original relation, as defined in the schema 175 * @param newrel New star relation, generated by RolapStar, canonical, and 176 * shared between all cubes with similar structure 177 */ 178 protected void extractNewAliases( 179 MondrianDef.RelationOrJoin oldrel, 180 MondrianDef.RelationOrJoin newrel) 181 { 182 if (oldrel == null && newrel == null) { 183 return; 184 } else if (oldrel instanceof MondrianDef.Relation 185 && newrel instanceof MondrianDef.Relation) 186 { 187 aliases.put( 188 ((MondrianDef.Relation) oldrel).getAlias(), 189 ((MondrianDef.Relation) newrel).getAlias()); 190 } else if (oldrel instanceof MondrianDef.Join 191 && newrel instanceof MondrianDef.Join) { 192 MondrianDef.Join oldjoin = (MondrianDef.Join)oldrel; 193 MondrianDef.Join newjoin = (MondrianDef.Join)newrel; 194 extractNewAliases(oldjoin.left, newjoin.left); 195 extractNewAliases(oldjoin.right, newjoin.right); 196 } else { 197 throw new UnsupportedOperationException(); 198 } 199 } 200 201 public boolean equals(Object o) { 202 if (this == o) { 203 return true; 204 } 205 if (!(o instanceof RolapCubeHierarchy)) { 206 return false; 207 } 208 209 RolapCubeHierarchy that = (RolapCubeHierarchy)o; 210 return parentDimension.equals(that.parentDimension) 211 && getUniqueName().equals(that.getUniqueName()); 212 } 213 214 protected int computeHashCode() { 215 return Util.hash(super.computeHashCode(), this.parentDimension.parent); 216 } 217 218 public Member createMember( 219 Member parent, 220 Level level, 221 String name, 222 Formula formula) 223 { 224 RolapLevel rolapLevel = ((RolapCubeLevel)level).getRolapLevel(); 225 if (formula == null) { 226 RolapMember member = new RolapMember( 227 (RolapMember) parent, rolapLevel, name); 228 return new RolapCubeMember((RolapCubeMember)parent, member, 229 (RolapCubeLevel)level, parentDimension.getCube()); 230 } else if (level.getDimension().isMeasures()) { 231 RolapCalculatedMeasure member = new RolapCalculatedMeasure( 232 (RolapMember) parent, rolapLevel, name, formula); 233 return new RolapCubeMember((RolapCubeMember)parent, member, 234 (RolapCubeLevel)level, parentDimension.getCube()); 235 } else { 236 RolapCalculatedMember member = new RolapCalculatedMember( 237 (RolapMember) parent, rolapLevel, name, formula); 238 return new RolapCubeMember((RolapCubeMember)parent, member, 239 (RolapCubeLevel)level, parentDimension.getCube()); 240 } 241 } 242 243 244 boolean tableExists(String tableName) { 245 return rolapHierarchy.tableExists(tableName); 246 } 247 248 /** 249 * The currentRelation object is derived from the shared relation object 250 * it is generated via the RolapStar object, and contains unique aliases 251 * for it's particular join path 252 * 253 * @return rolap cube hierarchy relation 254 */ 255 public MondrianDef.RelationOrJoin getRelation() { 256 return currentRelation; 257 } 258 259 public Member getDefaultMember() { 260 if (currentDefaultMember == null) { 261 reader.getRootMembers(); 262 RolapCubeLevel level = 263 (RolapCubeLevel) 264 levels[rolapHierarchy.getDefaultMember().getDepth()]; 265 RolapMember rolapDefaultMember = 266 (RolapMember) rolapHierarchy.getDefaultMember(); 267 268 currentDefaultMember = reader.lookupCubeMember( 269 hasAll() ? currentAllMember : null, 270 rolapDefaultMember, level); 271 } 272 return currentDefaultMember; 273 } 274 275 public Member getNullMember() { 276 // use lazy initialization to get around bootstrap issues 277 if (currentNullMember == null) { 278 currentNullMember = new RolapNullMember(currentNullLevel); 279 } 280 return currentNullMember; 281 } 282 283 /** 284 * Returns the 'all' member. 285 */ 286 public RolapCubeMember getAllMember() { 287 return currentAllMember; 288 } 289 290 /** 291 * Returns the display name of this catalog element. 292 * If no caption is defined, the name is returned. 293 */ 294 public String getCaption() { 295 return rolapHierarchy.getCaption(); 296 } 297 298 /** 299 * Sets the display name of this catalog element. 300 */ 301 public void setCaption(String caption) { 302 rolapHierarchy.setCaption(caption); 303 } 304 305 void setMemberReader(MemberReader memberReader) { 306 rolapHierarchy.setMemberReader(memberReader); 307 } 308 309 MemberReader getMemberReader() { 310 return reader; 311 } 312 313 public void setDefaultMember(Member defaultMeasure) { 314 // refactor this! 315 rolapHierarchy.setDefaultMember(defaultMeasure); 316 317 RolapCubeLevel level = 318 new RolapCubeLevel( 319 (RolapLevel)rolapHierarchy.getDefaultMember().getLevel(), 320 this); 321 currentDefaultMember = 322 new RolapCubeMember( 323 null, 324 (RolapMember)rolapHierarchy.getDefaultMember(), 325 level, 326 parentDimension.getCube()); 327 } 328 329 void init(MondrianDef.CubeDimension xmlDimension) { 330 // first init shared hierarchy 331 rolapHierarchy.init(xmlDimension); 332 // second init cube hierarchy 333 super.init(xmlDimension); 334 } 335 336 /** 337 * TODO: Since this is part of a caching strategy, should be implemented 338 * as a Strategy Pattern, avoiding hirarchy. 339 */ 340 public static interface RolapCubeHierarchyMemberReader 341 extends MemberReader 342 { 343 public RolapCubeMember lookupCubeMember( 344 final RolapCubeMember parent, 345 final RolapMember member, 346 final RolapCubeLevel level); 347 public MemberCacheHelper getRolapCubeMemberCacheHelper(); 348 } 349 350 /****** 351 352 RolapCubeMember Caching Approach: 353 354 - RolapHierarchy.SmartMemberReader.SmartCacheHelper -> 355 This is the shared cache across shared hierarchies. This member cache only 356 contains members loaded by non-cube specific member lookups. This cache 357 should only contain RolapMembers, not RolapCubeMembers 358 359 - RolapCubeHierarchy.RolapCubeHierarchyMemberReader.rolapCubeCacheHelper -> 360 This cache contains the RolapCubeMember objects, which are cube specific 361 wrappers of shared members. 362 363 - RolapCubeHierarchy.RolapCubeHierarchyMemberReader.SmartCacheHelper -> 364 This is the inherited shared cache from SmartMemberReader, and is used when 365 a join with the fact table is necessary, SqlContextConstraint.isJoinRequired(). 366 This cache may be redundant with rolapCubeCacheHelper. 367 368 - A Special note regarding RolapCubeHierarchyMemberReader.cubeSource - 369 This class was required for the special situation getMemberBuilder() method 370 call from RolapNativeSet. This class utilizes both the rolapCubeCacheHelper 371 class for storing RolapCubeMembers, and also the 372 RolapCubeHierarchyMemberReader's inherited SmartCacheHelper 373 374 375 ******/ 376 377 378 /** 379 * member reader wrapper - uses existing member reader, 380 * but wraps and caches all intermediate members 381 * <p>Synchronization. Most synchronization takes place within SmartMemberReader. 382 * All synchronization is done on the cacheHelper object. 383 */ 384 public class CacheRolapCubeHierarchyMemberReader 385 extends SmartMemberReader 386 implements RolapCubeHierarchyMemberReader 387 { 388 /** 389 * cubeSource is passed as our member builder 390 */ 391 protected final RolapCubeSqlMemberSource cubeSource; 392 393 /** 394 * this cache caches RolapCubeMembers that are light wrappers around 395 * shared and non-shared Hierarchy RolapMembers. The inherited 396 * cacheHelper object contains non-shared hierarchy RolapMembers. 397 * non-shared hierarchy RolapMembers are created when a member lookup 398 * involves the Cube's fact table. 399 */ 400 protected MemberCacheHelper rolapCubeCacheHelper; 401 private final boolean enableCache = 402 MondrianProperties.instance().EnableRolapCubeMemberCache.get(); 403 404 public CacheRolapCubeHierarchyMemberReader() { 405 super(new SqlMemberSource(RolapCubeHierarchy.this)); 406 rolapCubeCacheHelper = 407 new MemberCacheHelper(RolapCubeHierarchy.this); 408 409 cubeSource = 410 new RolapCubeSqlMemberSource( 411 this, 412 RolapCubeHierarchy.this, 413 rolapCubeCacheHelper, 414 cacheHelper); 415 416 cubeSource.setCache(getMemberCache()); 417 } 418 419 public MemberBuilder getMemberBuilder() { 420 return this.cubeSource; 421 } 422 423 public MemberCacheHelper getRolapCubeMemberCacheHelper() { 424 return rolapCubeCacheHelper; 425 } 426 427 public List<RolapMember> getRootMembers() { 428 if (rootMembers == null) { 429 rootMembers = 430 getMembersInLevel( 431 (RolapCubeLevel) getLevels()[0], 432 0, 433 Integer.MAX_VALUE); 434 } 435 return rootMembers; 436 } 437 438 private String getUniqueNameForMemberWithoutHierarchy( 439 RolapMember member) 440 { 441 String name = 442 (String) member.getPropertyValue( 443 Property.UNIQUE_NAME_WITHOUT_HIERARCHY.getName()); 444 RolapMember parent = member; 445 if (name == null) { 446 StringBuilder fullName = new StringBuilder(); 447 while (parent != null) { 448 fullName.append("[").append(parent.getName()).append("]"); 449 parent = parent.getParentMember(); 450 } 451 name = fullName.toString(); 452 member.setProperty( 453 Property.UNIQUE_NAME_WITHOUT_HIERARCHY.getName(), 454 name); 455 } 456 return name; 457 } 458 459 protected void readMemberChildren( 460 List<RolapMember> parentMembers, 461 List<RolapMember> children, 462 MemberChildrenConstraint constraint) 463 { 464 List<RolapMember> rolapChildren = new ArrayList<RolapMember>(); 465 List<RolapMember> rolapParents = new ArrayList<RolapMember>(); 466 Map<String, RolapCubeMember> lookup = 467 new HashMap<String, RolapCubeMember>(); 468 469 // extract RolapMembers from their RolapCubeMember objects 470 // populate lookup for reconnecting parents and children 471 final List<RolapCubeMember> parentRolapCubeMemberList = 472 Util.cast(parentMembers); 473 for (RolapCubeMember member : parentRolapCubeMemberList) { 474 lookup.put( 475 getUniqueNameForMemberWithoutHierarchy( 476 member.getRolapMember()), member); 477 rolapParents.add(member.getRolapMember()); 478 } 479 480 // get member children from shared member reader if possible, 481 // if not get them from our own source 482 boolean joinReq = 483 (constraint instanceof SqlContextConstraint) 484 && (((SqlContextConstraint)constraint).isJoinRequired() || 485 ((SqlContextConstraint)constraint).getEvaluator().isNonEmpty()); 486 if (joinReq) { 487 super.readMemberChildren(parentMembers, rolapChildren, constraint); 488 } else { 489 rolapHierarchy.getMemberReader().getMemberChildren( 490 rolapParents, rolapChildren, constraint); 491 } 492 493 // now lookup or create RolapCubeMember 494 for (RolapMember currMember : rolapChildren) { 495 RolapCubeMember parent = 496 lookup.get( 497 getUniqueNameForMemberWithoutHierarchy( 498 currMember.getParentMember())); 499 RolapCubeLevel level = 500 parent.getLevel().getChildLevel(); 501 if (level == null) { 502 // most likely a parent child hierarchy 503 level = parent.getLevel(); 504 } 505 RolapCubeMember newmember = 506 lookupCubeMember( 507 parent, currMember, level); 508 children.add(newmember); 509 } 510 511 // Put them in a temporary hash table first. Register them later, 512 // when we know their size (hence their 'cost' to the cache pool). 513 Map<RolapMember, List<RolapMember>> tempMap = 514 new HashMap<RolapMember, List<RolapMember>>(); 515 for (RolapMember member1 : parentMembers) { 516 tempMap.put(member1, Collections.<RolapMember>emptyList()); 517 } 518 519 // note that this stores RolapCubeMembers in our cache, 520 // which also stores RolapMembers. 521 522 for (RolapMember child : children) { 523 // todo: We could optimize here. If members.length is small, it's 524 // more efficient to drive from members, rather than hashing 525 // children.length times. We could also exploit the fact that the 526 // result is sorted by ordinal and therefore, unless the "members" 527 // contains members from different levels, children of the same 528 // member will be contiguous. 529 assert child != null : "child"; 530 final RolapMember parentMember = child.getParentMember(); 531 List<RolapMember> cacheList = tempMap.get(parentMember); 532 if (cacheList == null) { 533 // The list is null if, due to dropped constraints, we now 534 // have a children list of a member we didn't explicitly 535 // ask for it. Adding it to the cache would be viable, but 536 // let's ignore it. 537 continue; 538 } else if (cacheList == Collections.EMPTY_LIST) { 539 cacheList = new ArrayList<RolapMember>(); 540 tempMap.put(parentMember, cacheList); 541 } 542 cacheList.add(child); 543 } 544 545 synchronized (cacheHelper) { 546 for (Map.Entry<RolapMember, List<RolapMember>> entry : 547 tempMap.entrySet()) 548 { 549 final RolapMember member = entry.getKey(); 550 if (rolapCubeCacheHelper.getChildrenFromCache( 551 member, constraint) == null) 552 { 553 final List<RolapMember> cacheList = entry.getValue(); 554 if (enableCache) { 555 rolapCubeCacheHelper.putChildren( 556 member, constraint, cacheList); 557 } 558 } 559 } 560 } 561 } 562 563 public void getMemberChildren( 564 List<RolapMember> parentMembers, 565 List<RolapMember> children, 566 MemberChildrenConstraint constraint) { 567 568 synchronized (cacheHelper) { 569 checkCacheStatus(); 570 571 List<RolapMember> missed = new ArrayList<RolapMember>(); 572 for (RolapMember parentMember : parentMembers) { 573 List<RolapMember> list = 574 rolapCubeCacheHelper.getChildrenFromCache(parentMember, constraint); 575 if (list == null) { 576 // the null member has no children 577 if (!parentMember.isNull()) { 578 missed.add(parentMember); 579 } 580 } else { 581 children.addAll(list); 582 } 583 } 584 if (missed.size() > 0) { 585 readMemberChildren(missed, children, constraint); 586 } 587 } 588 } 589 590 591 public List<RolapMember> getMembersInLevel( 592 RolapLevel level, 593 int startOrdinal, 594 int endOrdinal, 595 TupleConstraint constraint) 596 { 597 synchronized (cacheHelper) { 598 checkCacheStatus(); 599 600 List<RolapMember> members = 601 rolapCubeCacheHelper.getLevelMembersFromCache( 602 level, constraint); 603 if (members != null) { 604 return members; 605 } 606 607 // if a join is required, we need to pass in the RolapCubeLevel 608 // vs. the regular level 609 boolean joinReq = 610 (constraint instanceof SqlContextConstraint) 611 && (((SqlContextConstraint)constraint).isJoinRequired() || 612 constraint.getEvaluator().isNonEmpty()); 613 614 List<RolapMember> list; 615 if (!joinReq) { 616 list = 617 rolapHierarchy.getMemberReader().getMembersInLevel( 618 ((RolapCubeLevel) level).getRolapLevel(), 619 startOrdinal, endOrdinal, constraint); 620 } else { 621 list = 622 super.getMembersInLevel( 623 level, startOrdinal, endOrdinal, constraint); 624 } 625 List<RolapMember> newlist = new ArrayList<RolapMember>(); 626 for (RolapMember member : list) { 627 // note that there is a special case for the all member 628 629 // REVIEW: disabled, to see what happens. if this code is for 630 // performance, we should check level.isAll at the top of the 631 // method; if it is for correctness, leave the code in 632 if (false && member == rolapHierarchy.getAllMember()) { 633 newlist.add(getAllMember()); 634 } else { 635 // i'm not quite sure about this one yet 636 RolapCubeMember parent = null; 637 if (member.getParentMember() != null) { 638 parent = 639 createAncestorMembers( 640 (RolapCubeLevel) level.getParentLevel(), 641 member.getParentMember()); 642 } 643 RolapCubeMember newmember = 644 lookupCubeMember( 645 parent, member, (RolapCubeLevel) level); 646 newlist.add(newmember); 647 } 648 } 649 rolapCubeCacheHelper.putLevelMembersInCache( 650 level, constraint, newlist); 651 652 return newlist; 653 } 654 } 655 656 private RolapCubeMember createAncestorMembers( 657 RolapCubeLevel level, 658 RolapMember member) 659 { 660 RolapCubeMember parent = null; 661 if (member.getParentMember() != null) { 662 parent = 663 createAncestorMembers( 664 level.getParentLevel(), member.getParentMember()); 665 } 666 return lookupCubeMember(parent, member, level); 667 } 668 669 public RolapCubeMember lookupCubeMember( 670 RolapCubeMember parent, 671 RolapMember member, 672 RolapCubeLevel level) 673 { 674 synchronized (cacheHelper) { 675 if (member.getKey() == null) { 676 if (member.isAll()) { 677 return getAllMember(); 678 } 679 680 throw new NullPointerException(); 681 } 682 683 RolapCubeMember cubeMember; 684 if (enableCache) { 685 Object key = 686 rolapCubeCacheHelper.makeKey(parent, member.getKey()); 687 cubeMember = (RolapCubeMember) 688 rolapCubeCacheHelper.getMember(key, false); 689 if (cubeMember == null) { 690 cubeMember = 691 new RolapCubeMember( 692 parent, member, level, parentDimension.getCube()); 693 rolapCubeCacheHelper.putMember(key, cubeMember); 694 } 695 } else { 696 cubeMember = 697 new RolapCubeMember( 698 parent, member, level, parentDimension.getCube()); 699 } 700 return cubeMember; 701 } 702 } 703 704 public int getMemberCount() { 705 return rolapHierarchy.getMemberReader().getMemberCount(); 706 } 707 708 protected void checkCacheStatus() { 709 synchronized (cacheHelper) { 710 // if necessary, flush all caches: 711 // - shared SmartMemberReader RolapMember cache 712 // - local key to cube member RolapCubeMember cache 713 // - cube source RolapCubeMember cache 714 // - local regular RolapMember cache, used when cube 715 // specific joins occur 716 717 if (cacheHelper.getChangeListener() != null) { 718 if (cacheHelper.getChangeListener() 719 .isHierarchyChanged(getHierarchy())) { 720 cacheHelper.flushCache(); 721 rolapCubeCacheHelper.flushCache(); 722 723 if (rolapHierarchy.getMemberReader() 724 instanceof SmartMemberReader) { 725 SmartMemberReader smartMemberReader = 726 (SmartMemberReader) 727 rolapHierarchy.getMemberReader(); 728 if (smartMemberReader.getMemberCache() 729 instanceof MemberCacheHelper) { 730 MemberCacheHelper helper = 731 (MemberCacheHelper) 732 smartMemberReader.getMemberCache(); 733 helper.flushCache(); 734 } 735 } 736 } 737 } 738 } 739 } 740 } 741 742 /** 743 * Same as RolapCubeHierarchyMemberReader but without caching anything. 744 */ 745 public class NoCacheRolapCubeHierarchyMemberReader 746 extends NoCacheMemberReader 747 implements RolapCubeHierarchyMemberReader 748 { 749 public NoCacheRolapCubeHierarchyMemberReader() { 750 super(new SqlMemberSource(RolapCubeHierarchy.this)); 751 } 752 753 public List<RolapMember> getMembersInLevel( 754 final RolapLevel level, 755 int startOrdinal, 756 int endOrdinal, 757 TupleConstraint constraint) 758 { 759 final RolapCubeLevel cubeLevel = (RolapCubeLevel) level; 760 // if a join is required, we need to pass in the RolapCubeLevel 761 // vs. the regular level 762 boolean joinReq = (constraint instanceof SqlContextConstraint) 763 && ((SqlContextConstraint)constraint).isJoinRequired(); 764 final List<RolapMember> list; 765 if (!joinReq) { 766 list = 767 rolapHierarchy.getMemberReader().getMembersInLevel( 768 cubeLevel.getRolapLevel(), 769 startOrdinal, endOrdinal, constraint); 770 } else { 771 list = 772 super.getMembersInLevel( 773 cubeLevel, startOrdinal, endOrdinal, constraint); 774 } 775 return new UnsupportedList<RolapMember>() { 776 public RolapMember get(final int index) { 777 return mutate(list.get(index)); 778 } 779 780 public int size() { 781 return list.size(); 782 } 783 784 public Iterator<RolapMember> iterator() { 785 final Iterator<RolapMember> it = list.iterator(); 786 return new Iterator<RolapMember>() { 787 public boolean hasNext() { 788 return it.hasNext(); 789 } 790 public RolapMember next() { 791 return mutate(it.next()); 792 } 793 794 public void remove() { 795 throw new UnsupportedOperationException(); 796 } 797 }; 798 } 799 800 private RolapMember mutate(final RolapMember member) { 801 RolapCubeMember parent = null; 802 if (member.getParentMember() != null) { 803 parent = 804 createAncestorMembers( 805 cubeLevel.getParentLevel(), 806 member.getParentMember()); 807 } 808 return lookupCubeMember( 809 parent, 810 member, 811 cubeLevel); 812 } 813 }; 814 } 815 816 public MemberCacheHelper getRolapCubeMemberCacheHelper() { 817 return null; 818 } 819 820 public RolapCubeMember lookupCubeMember( 821 final RolapCubeMember parent, 822 final RolapMember member, 823 final RolapCubeLevel level) 824 { 825 if (member.getKey() == null) { 826 if (member.isAll()) { 827 return getAllMember(); 828 } 829 throw new NullPointerException(); 830 } 831 return new RolapCubeMember( 832 parent, member, level, parentDimension.getCube()); 833 } 834 835 private RolapCubeMember createAncestorMembers( 836 final RolapCubeLevel level, 837 final RolapMember member) 838 { 839 RolapCubeMember parent = null; 840 if (member.getParentMember() != null) { 841 parent = 842 createAncestorMembers( 843 level.getParentLevel(), 844 member.getParentMember()); 845 } 846 return lookupCubeMember(parent, member, level); 847 } 848 } 849 850 851 public static class RolapCubeSqlMemberSource extends SqlMemberSource { 852 853 private final RolapCubeHierarchyMemberReader memberReader; 854 private final MemberCacheHelper memberSourceCacheHelper; 855 private final Object memberCacheLock; 856 857 public RolapCubeSqlMemberSource( 858 RolapCubeHierarchyMemberReader memberReader, 859 RolapCubeHierarchy hierarchy, 860 MemberCacheHelper memberSourceCacheHelper, 861 Object memberCacheLock) 862 { 863 super(hierarchy); 864 this.memberReader = memberReader; 865 this.memberSourceCacheHelper = memberSourceCacheHelper; 866 this.memberCacheLock = memberCacheLock; 867 } 868 869 public RolapMember makeMember( 870 RolapMember parentMember, 871 RolapLevel childLevel, 872 Object value, 873 Object captionValue, 874 boolean parentChild, 875 ResultSet resultSet, 876 Object key, 877 int columnOffset) 878 throws SQLException { 879 880 RolapMember member = 881 super.makeMember( 882 parentMember, 883 ((RolapCubeLevel)childLevel).getRolapLevel(), 884 value, captionValue, parentChild, resultSet, key, 885 columnOffset); 886 return 887 memberReader.lookupCubeMember( 888 (RolapCubeMember)parentMember, 889 member, 890 (RolapCubeLevel)childLevel); 891 } 892 893 public MemberCache getMemberCache() { 894 // this is a special cache used solely for rolapcubemembers 895 return memberSourceCacheHelper; 896 } 897 898 /** 899 * use the same lock in the RolapCubeMemberSource as the 900 * RolapCubeHiearchyMemberReader to avoid deadlocks 901 */ 902 public Object getMemberCacheLock() { 903 return memberCacheLock; 904 } 905 } 906 } 907 908 // End RolapCubeHierarchy.java 909