001 /* 002 // $Id: //open/mondrian/src/main/mondrian/rolap/MemberCacheHelper.java#3 $ 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 // Copyright (C) 2004-2005 TONBELLER AG 009 // All Rights Reserved. 010 // You must accept the terms of that agreement to use this software. 011 // 012 // jhyde, 21 December, 2001 013 */ 014 package mondrian.rolap; 015 016 import mondrian.rolap.cache.SmartCache; 017 import mondrian.rolap.cache.SoftSmartCache; 018 import mondrian.rolap.sql.MemberChildrenConstraint; 019 import mondrian.rolap.sql.TupleConstraint; 020 import mondrian.spi.DataSourceChangeListener; 021 import mondrian.olap.Util; 022 023 import java.util.*; 024 025 /** 026 * Encapsulation of member caching. 027 * 028 * @author Will Gorman (wgorman@pentaho.org) 029 * @version $Id: //open/mondrian/src/main/mondrian/rolap/MemberCacheHelper.java#3 $ 030 */ 031 public class MemberCacheHelper implements MemberCache { 032 033 private final SqlConstraintFactory sqlConstraintFactory = 034 SqlConstraintFactory.instance(); 035 036 /** maps a parent member to a list of its children */ 037 final SmartMemberListCache<RolapMember, List<RolapMember>> 038 mapMemberToChildren; 039 040 /** a cache for alle members to ensure uniqueness */ 041 SmartCache<Object, RolapMember> mapKeyToMember; 042 RolapHierarchy rolapHierarchy; 043 DataSourceChangeListener changeListener; 044 045 /** maps a level to its members */ 046 final SmartMemberListCache<RolapLevel, List<RolapMember>> 047 mapLevelToMembers; 048 049 /** 050 * Creates a MemberCacheHelper. 051 * 052 * @param rolapHierarchy Hierarchy 053 */ 054 public MemberCacheHelper(RolapHierarchy rolapHierarchy) { 055 this.rolapHierarchy = rolapHierarchy; 056 this.mapLevelToMembers = 057 new SmartMemberListCache<RolapLevel, List<RolapMember>>(); 058 this.mapKeyToMember = 059 new SoftSmartCache<Object, RolapMember>(); 060 this.mapMemberToChildren = 061 new SmartMemberListCache<RolapMember, List<RolapMember>>(); 062 063 if (rolapHierarchy != null) { 064 changeListener = 065 rolapHierarchy.getRolapSchema().getDataSourceChangeListener(); 066 } else { 067 changeListener = null; 068 } 069 } 070 071 // implement MemberCache 072 // synchronization: Must synchronize, because uses mapKeyToMember 073 public synchronized RolapMember getMember( 074 Object key, 075 boolean mustCheckCacheStatus) 076 { 077 if (mustCheckCacheStatus) { 078 checkCacheStatus(); 079 } 080 return mapKeyToMember.get(key); 081 } 082 083 084 // implement MemberCache 085 // synchronization: Must synchronize, because modifies mapKeyToMember 086 public synchronized Object putMember(Object key, RolapMember value) { 087 return mapKeyToMember.put(key, value); 088 } 089 090 // implement MemberCache 091 public Object makeKey(RolapMember parent, Object key) { 092 return new MemberKey(parent, key); 093 } 094 095 // implement MemberCache 096 // synchronization: Must synchronize, because modifies mapKeyToMember 097 public synchronized RolapMember getMember(Object key) { 098 return getMember(key, true); 099 } 100 101 public synchronized void checkCacheStatus() { 102 if (changeListener != null) { 103 if (changeListener.isHierarchyChanged(rolapHierarchy)) { 104 flushCache(); 105 } 106 } 107 } 108 109 /** 110 * ??? 111 * 112 * @param level 113 * @param constraint 114 * @param members 115 */ 116 // synchronization: Must synchronize, because modifies mapKeyToMember 117 public synchronized void putLevelMembersInCache( 118 RolapLevel level, 119 TupleConstraint constraint, 120 List<RolapMember> members) 121 { 122 mapLevelToMembers.put(level, constraint, members); 123 } 124 125 public synchronized List<RolapMember> getChildrenFromCache( 126 RolapMember member, 127 MemberChildrenConstraint constraint) 128 { 129 if (constraint == null) { 130 constraint = 131 sqlConstraintFactory.getMemberChildrenConstraint(null); 132 } 133 return mapMemberToChildren.get(member, constraint); 134 } 135 136 public synchronized void putChildren( 137 RolapMember member, 138 MemberChildrenConstraint constraint, 139 List<RolapMember> children) 140 { 141 if (constraint == null) { 142 constraint = 143 sqlConstraintFactory.getMemberChildrenConstraint(null); 144 } 145 mapMemberToChildren.put(member, constraint, children); 146 } 147 148 public synchronized List<RolapMember> getLevelMembersFromCache( 149 RolapLevel level, 150 TupleConstraint constraint) 151 { 152 if (constraint == null) { 153 constraint = sqlConstraintFactory.getLevelMembersConstraint(null); 154 } 155 return mapLevelToMembers.get(level, constraint); 156 } 157 158 public synchronized void flushCache() { 159 mapMemberToChildren.clear(); 160 mapKeyToMember.clear(); 161 mapLevelToMembers.clear(); 162 } 163 164 public DataSourceChangeListener getChangeListener() { 165 return changeListener; 166 } 167 168 public void setChangeListener(DataSourceChangeListener listener) { 169 changeListener = listener; 170 } 171 172 public boolean isMutable() 173 { 174 return true; 175 } 176 177 public synchronized RolapMember removeMember(Object key) 178 { 179 RolapMember member = getMember(key); 180 if (member == null) { 181 // not in cache 182 return null; 183 } 184 185 // Drop member from the level-to-members map. 186 // Cache key is a (level, constraint) pair, 187 // cache value is a list of RolapMember. 188 // For each cache key whose level matches, remove from the list, 189 // regardless of the constraint. 190 RolapLevel level = member.getLevel(); 191 for (Map.Entry< 192 SmartMemberListCache.Key2<RolapLevel, Object>, 193 List<RolapMember>> entry : mapLevelToMembers.getCache()) 194 { 195 if (entry.getKey().o1.equals(level)) { 196 List<RolapMember> peers = entry.getValue(); 197 boolean removedIt = peers.remove(member); 198 Util.discard(removedIt); 199 } 200 } 201 202 // Drop member from the member-to-children map, wherever it occurs as 203 // a parent or as a child, regardless of the constraint. 204 RolapMember parent = member.getParentMember(); 205 final Iterator< 206 Map.Entry< 207 SmartMemberListCache.Key2<RolapMember, Object>, 208 List<RolapMember>>> iter = 209 mapMemberToChildren.getCache().iterator(); 210 while (iter.hasNext()) { 211 Map.Entry< 212 SmartMemberListCache.Key2<RolapMember, Object>, 213 List<RolapMember>> entry = iter.next(); 214 final RolapMember member1 = entry.getKey().o1; 215 final Object constraint = entry.getKey().o2; 216 217 // Cache key is (member's parent, constraint); 218 // cache value is a list of member's siblings; 219 // If constraint is trivial remove member from list of siblings; 220 // otherwise it's safer to nuke the cache entry 221 if (member1.equals(parent)) { 222 if (constraint == DefaultMemberChildrenConstraint.instance()) { 223 List<RolapMember> siblings = entry.getValue(); 224 boolean removedIt = siblings.remove(member); 225 Util.discard(removedIt); 226 } else { 227 iter.remove(); 228 } 229 } 230 231 // cache is (member, some constraint); 232 // cache value is list of member's children; 233 // remove cache entry 234 if (member1.equals(member)) { 235 iter.remove(); 236 } 237 } 238 239 // drop it from the lookup-cache 240 return mapKeyToMember.put(key, null); 241 } 242 243 public synchronized RolapMember removeMemberAndDescendants(Object key) { 244 // Can use mapMemberToChildren recursively. No need to update inferior 245 // lists of children. Do need to update inferior lists of level-peers. 246 return null; // STUB 247 } 248 249 } 250 251 // End MemberCacheHelper.java