mondrian.rolap.MemberCacheHelper.java Source code

Java tutorial

Introduction

Here is the source code for mondrian.rolap.MemberCacheHelper.java

Source

/*
// 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