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) 2012-2012 Pentaho and others
008// All Rights Reserved.
009*/
010package mondrian.rolap;
011
012import mondrian.olap.*;
013import mondrian.rolap.RolapHierarchy.LimitedRollupMember;
014import mondrian.rolap.sql.*;
015
016import java.util.*;
017import java.util.concurrent.locks.*;
018
019/**
020 * A {@link SmartRestrictedMemberReader} is a subclass of
021 * {@link RestrictedMemberReader} which caches the access rights
022 * per children's list. We place them in this throw-away object
023 * to speed up partial rollup calculations.
024 *
025 * <p>The speed improvement is noticeable when dealing with very
026 * big dimensions with a lot of branches (like a parent-child
027 * hierarchy) because the 'partial' rollup policy forces us to
028 * navigate the tree and find the lowest level to rollup to and
029 * then figure out all of the children on which to constraint
030 * the SQL query.
031 */
032class SmartRestrictedMemberReader extends RestrictedMemberReader {
033
034    SmartRestrictedMemberReader(
035        final MemberReader memberReader,
036        final Role role)
037    {
038        // We want to extend a RestrictedMemberReader with access details
039        // that we cache.
040        super(memberReader, role);
041    }
042
043    // Our little ad-hoc cache.
044    final Map<RolapMember, AccessAwareMemberList>
045        memberToChildren =
046            new WeakHashMap<RolapMember, AccessAwareMemberList>();
047
048    // The lock for cache access.
049    final ReadWriteLock lock = new ReentrantReadWriteLock();
050
051    @Override
052    public Map<? extends Member, Access> getMemberChildren(
053        RolapMember member,
054        List<RolapMember> children,
055        MemberChildrenConstraint constraint)
056    {
057        // Strip off the rollup wrapper.
058        if (member instanceof LimitedRollupMember) {
059            member = ((LimitedRollupMember)member).member;
060        }
061        try {
062            // Get the read lock.
063            lock.readLock().lock();
064
065            AccessAwareMemberList memberList =
066                memberToChildren.get(member);
067
068            if (memberList != null) {
069                // Sadly, we need to do a hard cast here,
070                // but since we know what it is, it's fine.
071                children.addAll(
072                    memberList.children);
073
074                return memberList.accessMap;
075            }
076        } finally {
077            lock.readLock().unlock();
078        }
079
080        // No cache data.
081        try {
082            // Get a write lock.
083            lock.writeLock().lock();
084
085            Map<? extends Member, Access> membersWithAccessDetails =
086                super.getMemberChildren(
087                    member,
088                    children,
089                    constraint);
090
091            memberToChildren.put(
092                member,
093                new AccessAwareMemberList(
094                    membersWithAccessDetails,
095                    new ArrayList(membersWithAccessDetails.keySet())));
096
097            return membersWithAccessDetails;
098        } finally {
099            lock.writeLock().unlock();
100        }
101    }
102
103    private static class AccessAwareMemberList {
104        private final Map<? extends Member, Access> accessMap;
105        private final Collection<RolapMember> children;
106        public AccessAwareMemberList(
107            Map<? extends Member, Access> accessMap,
108            Collection<RolapMember> children)
109        {
110            this.accessMap = accessMap;
111            this.children = children;
112        }
113    }
114}
115
116// End SmartRestrictedMemberReader.java