001/* 002// This software is subject to the terms of the Eclipse Public License v1.0 003// Agreement, available at the following URL: 004// http://www.eclipse.org/legal/epl-v10.html. 005// You must accept the terms of that agreement to use this software. 006// 007// Copyright (C) 2001-2005 Julian Hyde 008// Copyright (C) 2004-2005 TONBELLER AG 009// Copyright (C) 2005-2012 Pentaho and others 010// All Rights Reserved. 011*/ 012package mondrian.rolap; 013 014import mondrian.olap.Level; 015import mondrian.olap.Util; 016import mondrian.rolap.cache.SmartCache; 017import mondrian.rolap.cache.SoftSmartCache; 018import mondrian.rolap.sql.MemberChildrenConstraint; 019import mondrian.rolap.sql.TupleConstraint; 020import mondrian.spi.DataSourceChangeListener; 021import mondrian.util.Pair; 022 023import java.util.*; 024import java.util.Map.Entry; 025 026/** 027 * Encapsulation of member caching. 028 * 029 * @author Will Gorman 030 */ 031public 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 all 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 public RolapMember getMember( 072 Object key, 073 boolean mustCheckCacheStatus) 074 { 075 if (mustCheckCacheStatus) { 076 checkCacheStatus(); 077 } 078 return mapKeyToMember.get(key); 079 } 080 081 082 // implement MemberCache 083 public Object putMember(Object key, RolapMember value) { 084 return mapKeyToMember.put(key, value); 085 } 086 087 // implement MemberCache 088 public Object makeKey(RolapMember parent, Object key) { 089 return new MemberKey(parent, key); 090 } 091 092 // implement MemberCache 093 public RolapMember getMember(Object key) { 094 return getMember(key, true); 095 } 096 097 public synchronized void checkCacheStatus() { 098 if (changeListener != null) { 099 if (changeListener.isHierarchyChanged(rolapHierarchy)) { 100 flushCache(); 101 } 102 } 103 } 104 105 /** 106 * Deprecated in favor of 107 * {@link #putChildren(RolapLevel, TupleConstraint, List)} 108 */ 109 @Deprecated 110 public void putLevelMembersInCache( 111 RolapLevel level, 112 TupleConstraint constraint, 113 List<RolapMember> members) 114 { 115 putChildren(level, constraint, members); 116 } 117 118 public void putChildren( 119 RolapLevel level, 120 TupleConstraint constraint, 121 List<RolapMember> members) 122 { 123 mapLevelToMembers.put(level, constraint, members); 124 } 125 126 public List<RolapMember> getChildrenFromCache( 127 RolapMember member, 128 MemberChildrenConstraint constraint) 129 { 130 if (constraint == null) { 131 constraint = 132 sqlConstraintFactory.getMemberChildrenConstraint(null); 133 } 134 return mapMemberToChildren.get(member, constraint); 135 } 136 137 public void putChildren( 138 RolapMember member, 139 MemberChildrenConstraint constraint, 140 List<RolapMember> children) 141 { 142 if (constraint == null) { 143 constraint = 144 sqlConstraintFactory.getMemberChildrenConstraint(null); 145 } 146 mapMemberToChildren.put(member, constraint, children); 147 } 148 149 public List<RolapMember> getLevelMembersFromCache( 150 RolapLevel level, 151 TupleConstraint constraint) 152 { 153 if (constraint == null) { 154 constraint = sqlConstraintFactory.getLevelMembersConstraint(null); 155 } 156 return mapLevelToMembers.get(level, constraint); 157 } 158 159 // Must sync here because we want the three maps to be modified together. 160 public synchronized void flushCache() { 161 mapMemberToChildren.clear(); 162 mapKeyToMember.clear(); 163 mapLevelToMembers.clear(); 164 // We also need to clear the approxRowCount of each level. 165 for (Level level : rolapHierarchy.getLevels()) { 166 ((RolapLevel)level).setApproxRowCount(Integer.MIN_VALUE); 167 } 168 } 169 170 public DataSourceChangeListener getChangeListener() { 171 return changeListener; 172 } 173 174 public void setChangeListener(DataSourceChangeListener listener) { 175 changeListener = listener; 176 } 177 178 public boolean isMutable() 179 { 180 return true; 181 } 182 183 public synchronized RolapMember removeMember(Object key) 184 { 185 // Flush entries from the level-to-members map 186 // for member's level and all child levels. 187 // Important: Do this even if the member is apparently not in the cache. 188 RolapLevel level = ((MemberKey) key).getLevel(); 189 if (level == null) { 190 level = (RolapLevel) this.rolapHierarchy.getLevels()[0]; 191 } 192 final RolapLevel levelRef = level; 193 mapLevelToMembers.getCache().execute( 194 new SmartCache.SmartCacheTask 195 <Pair<RolapLevel, Object>, List<RolapMember>>() 196 { 197 public void execute( 198 Iterator<Entry<Pair 199 <RolapLevel, Object>, List<RolapMember>>> iterator) 200 { 201 while (iterator.hasNext()) { 202 Map.Entry<Pair 203 <RolapLevel, Object>, List<RolapMember>> entry = 204 iterator.next(); 205 final RolapLevel cacheLevel = entry.getKey().left; 206 if (cacheLevel.equals(levelRef) 207 || (cacheLevel.getHierarchy() 208 .equals(levelRef.getHierarchy()) 209 && cacheLevel.getDepth() 210 >= levelRef.getDepth())) 211 { 212 iterator.remove(); 213 } 214 } 215 } 216 }); 217 218 final RolapMember member = getMember(key); 219 if (member == null) { 220 // not in cache 221 return null; 222 } 223 224 // Drop member from the member-to-children map, wherever it occurs as 225 // a parent or as a child, regardless of the constraint. 226 final RolapMember parent = member.getParentMember(); 227 mapMemberToChildren.cache.execute( 228 new SmartCache.SmartCacheTask 229 <Pair<RolapMember, Object>, List<RolapMember>>() 230 { 231 public void execute( 232 Iterator<Entry 233 <Pair<RolapMember, Object>, List<RolapMember>>> iter) 234 { 235 while (iter.hasNext()) { 236 Map.Entry<Pair 237 <RolapMember, Object>, List<RolapMember>> entry = 238 iter.next(); 239 final RolapMember member1 = entry.getKey().left; 240 final Object constraint = entry.getKey().right; 241 242 // Cache key is (member's parent, constraint); 243 // cache value is a list of member's siblings; 244 // If constraint is trivial remove member from list 245 // of siblings; otherwise it's safer to nuke the cache 246 // entry 247 if (Util.equals(member1, parent)) { 248 if (constraint 249 == DefaultMemberChildrenConstraint.instance()) 250 { 251 List<RolapMember> siblings = entry.getValue(); 252 boolean removedIt = siblings.remove(member); 253 Util.discard(removedIt); 254 } else { 255 iter.remove(); 256 } 257 } 258 259 // cache is (member, some constraint); 260 // cache value is list of member's children; 261 // remove cache entry 262 if (Util.equals(member1, member)) { 263 iter.remove(); 264 } 265 } 266 } 267 }); 268 269 // drop it from the lookup-cache 270 return mapKeyToMember.put(key, null); 271 } 272 273 public RolapMember removeMemberAndDescendants(Object key) { 274 // Can use mapMemberToChildren recursively. No need to update inferior 275 // lists of children. Do need to update inferior lists of level-peers. 276 return null; // STUB 277 } 278} 279 280// End MemberCacheHelper.java 281