Java tutorial
/* // This software is subject to the terms of the Eclipse Public License v1.0 // Agreement, available at the following URL: // http://www.eclipse.org/legal/epl-v10.html. // You must accept the terms of that agreement to use this software. // // Copyright (C) 2001-2005 Julian Hyde // Copyright (C) 2004-2005 TONBELLER AG // Copyright (C) 2005-2015 Pentaho and others // All Rights Reserved. */ package mondrian.rolap; import mondrian.olap.Level; import mondrian.olap.Util; import mondrian.rolap.cache.*; import mondrian.rolap.sql.MemberChildrenConstraint; import mondrian.rolap.sql.TupleConstraint; import mondrian.spi.DataSourceChangeListener; import mondrian.util.*; import org.apache.commons.collections.Predicate; import java.util.*; import java.util.Map.Entry; import static org.apache.commons.collections.CollectionUtils.filter; /** * Encapsulation of member caching. * * @author Will Gorman */ public class MemberCacheHelper implements MemberCache { private final SqlConstraintFactory sqlConstraintFactory = SqlConstraintFactory.instance(); /** maps a parent member and constraint to a list of its children */ final SmartMemberListCache<RolapMember, List<RolapMember>> mapMemberToChildren; /** maps a parent member to the collection of named children that have * been cached. The collection can grow over time as new children are * loaded. */ final SmartIncrementalCache<RolapMember, Collection<RolapMember>> mapParentToNamedChildren; /** a cache for all members to ensure uniqueness */ SmartCache<Object, RolapMember> mapKeyToMember; RolapHierarchy rolapHierarchy; DataSourceChangeListener changeListener; /** maps a level to its members */ final SmartMemberListCache<RolapLevel, List<RolapMember>> mapLevelToMembers; /** * Creates a MemberCacheHelper. * * @param rolapHierarchy Hierarchy */ public MemberCacheHelper(RolapHierarchy rolapHierarchy) { this.rolapHierarchy = rolapHierarchy; this.mapLevelToMembers = new SmartMemberListCache<RolapLevel, List<RolapMember>>(); this.mapKeyToMember = new SoftSmartCache<Object, RolapMember>(); this.mapMemberToChildren = new SmartMemberListCache<RolapMember, List<RolapMember>>(); this.mapParentToNamedChildren = new SmartIncrementalCache<RolapMember, Collection<RolapMember>>(); if (rolapHierarchy != null) { changeListener = rolapHierarchy.getRolapSchema().getDataSourceChangeListener(); } else { changeListener = null; } } public RolapMember getMember(Object key, boolean mustCheckCacheStatus) { if (mustCheckCacheStatus) { checkCacheStatus(); } return mapKeyToMember.get(key); } // implement MemberCache public Object putMember(Object key, RolapMember value) { return mapKeyToMember.put(key, value); } // implement MemberCache public Object makeKey(RolapMember parent, Object key) { return new MemberKey(parent, key); } // implement MemberCache public RolapMember getMember(Object key) { return getMember(key, true); } public synchronized void checkCacheStatus() { if (changeListener != null) { if (changeListener.isHierarchyChanged(rolapHierarchy)) { flushCache(); } } } /** * Deprecated in favor of * {@link #putChildren(RolapLevel, TupleConstraint, List)} */ @Deprecated public void putLevelMembersInCache(RolapLevel level, TupleConstraint constraint, List<RolapMember> members) { putChildren(level, constraint, members); } public void putChildren(RolapLevel level, TupleConstraint constraint, List<RolapMember> members) { mapLevelToMembers.put(level, constraint, members); } public List<RolapMember> getChildrenFromCache(RolapMember member, MemberChildrenConstraint constraint) { if (constraint == null) { constraint = sqlConstraintFactory.getMemberChildrenConstraint(null); } if (constraint instanceof ChildByNameConstraint) { return findNamedChildrenInCache(member, ((ChildByNameConstraint) constraint).getChildNames()); } return mapMemberToChildren.get(member, constraint); } /** * Attempts to find all children requested by the ChildByNameConstraint * in cache. Returns null if the complete list is not found. */ private List<RolapMember> findNamedChildrenInCache(final RolapMember parent, final List<String> childNames) { List<RolapMember> children = checkDefaultAndNamedChildrenCache(parent); if (children == null || childNames == null || childNames.size() > children.size()) { return null; } filter(children, new Predicate() { public boolean evaluate(Object member) { return childNames.contains(((RolapMember) member).getName()); } }); boolean foundAll = children.size() == childNames.size(); return !foundAll ? null : children; } private List<RolapMember> checkDefaultAndNamedChildrenCache(RolapMember parent) { Collection<RolapMember> children = mapMemberToChildren.get(parent, DefaultMemberChildrenConstraint.instance()); if (children == null) { children = mapParentToNamedChildren.get(parent); } return children == null ? Collections.emptyList() : new ArrayList(children); } public void putChildren(RolapMember member, MemberChildrenConstraint constraint, List<RolapMember> children) { if (constraint == null) { constraint = sqlConstraintFactory.getMemberChildrenConstraint(null); } if (constraint instanceof ChildByNameConstraint) { putChildrenInChildNameCache(member, children); } else { mapMemberToChildren.put(member, constraint, children); } } private void putChildrenInChildNameCache(final RolapMember parent, final List<RolapMember> children) { if (children == null || children.isEmpty()) { return; } Collection<RolapMember> cachedChildren = mapParentToNamedChildren.get(parent); if (cachedChildren == null) { // initialize with a sorted set mapParentToNamedChildren.put(parent, new TreeSet<RolapMember>(children)); } else { mapParentToNamedChildren.addToEntry(parent, children); } } public List<RolapMember> getLevelMembersFromCache(RolapLevel level, TupleConstraint constraint) { if (constraint == null) { constraint = sqlConstraintFactory.getLevelMembersConstraint(null); } return mapLevelToMembers.get(level, constraint); } // Must sync here because we want the three maps to be modified together. public synchronized void flushCache() { mapMemberToChildren.clear(); mapKeyToMember.clear(); mapLevelToMembers.clear(); mapParentToNamedChildren.clear(); // We also need to clear the approxRowCount of each level. for (Level level : rolapHierarchy.getLevels()) { ((RolapLevel) level).setApproxRowCount(Integer.MIN_VALUE); } } public DataSourceChangeListener getChangeListener() { return changeListener; } public void setChangeListener(DataSourceChangeListener listener) { changeListener = listener; } public boolean isMutable() { return true; } public synchronized RolapMember removeMember(Object key) { // Flush entries from the level-to-members map // for member's level and all child levels. // Important: Do this even if the member is apparently not in the cache. RolapLevel level = ((MemberKey) key).getLevel(); if (level == null) { level = (RolapLevel) this.rolapHierarchy.getLevels()[0]; } final RolapLevel levelRef = level; mapLevelToMembers.getCache() .execute(new SmartCache.SmartCacheTask<Pair<RolapLevel, Object>, List<RolapMember>>() { public void execute(Iterator<Entry<Pair<RolapLevel, Object>, List<RolapMember>>> iterator) { while (iterator.hasNext()) { Map.Entry<Pair<RolapLevel, Object>, List<RolapMember>> entry = iterator.next(); final RolapLevel cacheLevel = entry.getKey().left; if (cacheLevel.equals(levelRef) || (cacheLevel.getHierarchy().equals(levelRef.getHierarchy()) && cacheLevel.getDepth() >= levelRef.getDepth())) { iterator.remove(); } } } }); final RolapMember member = getMember(key); if (member == null) { // not in cache return null; } // Drop member from the member-to-children map, wherever it occurs as // a parent or as a child, regardless of the constraint. final RolapMember parent = member.getParentMember(); mapMemberToChildren.cache .execute(new SmartCache.SmartCacheTask<Pair<RolapMember, Object>, List<RolapMember>>() { public void execute(Iterator<Entry<Pair<RolapMember, Object>, List<RolapMember>>> iter) { while (iter.hasNext()) { Map.Entry<Pair<RolapMember, Object>, List<RolapMember>> entry = iter.next(); final RolapMember member1 = entry.getKey().left; final Object constraint = entry.getKey().right; // Cache key is (member's parent, constraint); // cache value is a list of member's siblings; // If constraint is trivial remove member from list // of siblings; otherwise it's safer to nuke the cache // entry if (Util.equals(member1, parent)) { if (constraint == DefaultMemberChildrenConstraint.instance()) { List<RolapMember> siblings = entry.getValue(); boolean removedIt = siblings.remove(member); Util.discard(removedIt); } else { iter.remove(); } } // cache is (member, some constraint); // cache value is list of member's children; // remove cache entry if (Util.equals(member1, member)) { iter.remove(); } } } }); mapParentToNamedChildren.getCache() .execute(new SmartCache.SmartCacheTask<RolapMember, Collection<RolapMember>>() { public void execute(Iterator<Entry<RolapMember, Collection<RolapMember>>> iterator) { while (iterator.hasNext()) { Entry<RolapMember, Collection<RolapMember>> entry = iterator.next(); RolapMember currentMember = entry.getKey(); if (member.equals(currentMember)) { iterator.remove(); } else if (parent.equals(currentMember)) { entry.getValue().remove(member); } } } }); // drop it from the lookup-cache return mapKeyToMember.put(key, null); } public RolapMember removeMemberAndDescendants(Object key) { // Can use mapMemberToChildren recursively. No need to update inferior // lists of children. Do need to update inferior lists of level-peers. return null; // STUB } } // End MemberCacheHelper.java