org.jahia.services.usermanager.GroupCacheHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.jahia.services.usermanager.GroupCacheHelper.java

Source

/**
 * ==========================================================================================
 * =                   JAHIA'S DUAL LICENSING - IMPORTANT INFORMATION                       =
 * ==========================================================================================
 *
 *                                 http://www.jahia.com
 *
 *     Copyright (C) 2002-2017 Jahia Solutions Group SA. All rights reserved.
 *
 *     THIS FILE IS AVAILABLE UNDER TWO DIFFERENT LICENSES:
 *     1/GPL OR 2/JSEL
 *
 *     1/ GPL
 *     ==================================================================================
 *
 *     IF YOU DECIDE TO CHOOSE THE GPL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS:
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 *
 *     2/ JSEL - Commercial and Supported Versions of the program
 *     ===================================================================================
 *
 *     IF YOU DECIDE TO CHOOSE THE JSEL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS:
 *
 *     Alternatively, commercial and supported versions of the program - also known as
 *     Enterprise Distributions - must be used in accordance with the terms and conditions
 *     contained in a separate written agreement between you and Jahia Solutions Group SA.
 *
 *     If you are unsure which license is appropriate for your use,
 *     please contact the sales department at sales@jahia.com.
 */
package org.jahia.services.usermanager;

import java.io.Serializable;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import javax.jcr.*;
import javax.jcr.query.Query;
import javax.jcr.query.RowIterator;

import net.sf.ehcache.Element;
import net.sf.ehcache.config.Searchable;
import net.sf.ehcache.constructs.blocking.CacheEntryFactory;
import net.sf.ehcache.constructs.blocking.SelfPopulatingCache;

import net.sf.ehcache.search.Result;
import net.sf.ehcache.search.Results;
import net.sf.ehcache.search.expression.EqualTo;
import org.apache.commons.lang.StringUtils;
import org.jahia.api.Constants;
import org.jahia.exceptions.JahiaException;
import org.jahia.services.cache.ehcache.EhCacheProvider;
import org.jahia.services.content.JCRContentUtils;
import org.jahia.services.content.JCRNodeWrapper;
import org.jahia.services.content.JCRSessionFactory;
import org.jahia.services.content.decorator.JCRUserNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Group manager cache helper.
 *
 * @author Sergiy Shyrkov
 */
public class GroupCacheHelper {

    private static Logger logger = LoggerFactory.getLogger(GroupCacheHelper.class);

    private EhCacheProvider ehCacheProvider;
    private SelfPopulatingCache groupPathByGroupNameCache;
    private SelfPopulatingCache membershipCache;
    private int timeToLiveForNonExistingGroups = 600;

    private class GroupPathByGroupNameCacheEntryFactory implements CacheEntryFactory {

        @Override
        public Object createEntry(final Object key) throws Exception {
            final GroupPathByGroupNameCacheKey k = (GroupPathByGroupNameCacheKey) key;
            String path = internalGetGroupPath(k.siteKey, k.groupName);
            if (path != null) {
                return new Element(key, path);
            } else {
                return new Element(key, StringUtils.EMPTY, 0, timeToLiveForNonExistingGroups);
            }
        }
    }

    private static class GroupPathByGroupNameCacheKey implements Serializable {

        private static final long serialVersionUID = 6343178106505586294L;

        private String siteKey;
        private String groupName;
        private final int hash;

        private GroupPathByGroupNameCacheKey(String siteKey, String groupName) {
            this.siteKey = siteKey;
            this.groupName = groupName;
            this.hash = getHashCode();
        }

        @Override
        public boolean equals(Object o) {

            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            GroupPathByGroupNameCacheKey that = (GroupPathByGroupNameCacheKey) o;

            if (!groupName.equals(that.groupName)) {
                return false;
            }
            if (siteKey != null ? !siteKey.equals(that.siteKey) : that.siteKey != null) {
                return false;
            }

            return true;
        }

        private int getHashCode() {
            int result = siteKey != null ? siteKey.hashCode() : 0;
            result = 31 * result + groupName.hashCode();
            return result;
        }

        @Override
        public int hashCode() {
            return hash;
        }
    }

    private class MembershipCacheEntryFactory implements CacheEntryFactory {

        @Override
        public Object createEntry(final Object key) throws Exception {
            return internalGetMembershipByPath((String) key);
        }
    }

    public String getGroupPath(String siteKey, String name) {
        final String value = (String) getGroupPathByGroupNameCache()
                .get(new GroupPathByGroupNameCacheKey(siteKey, name)).getObjectValue();
        if (value.equals(StringUtils.EMPTY)) {
            return null;
        }
        return value;
    }

    private SelfPopulatingCache getGroupPathByGroupNameCache() {
        // First do non-synchronized check to avoid locking any threads that invoke the method simultaneously.
        if (groupPathByGroupNameCache == null) {
            // Then check-again-and-initialize-if-needed within the synchronized block to ensure check-and-initialization consistency.
            synchronized (this) {
                if (groupPathByGroupNameCache == null) {
                    groupPathByGroupNameCache = ehCacheProvider.registerSelfPopulatingCache(
                            "org.jahia.services.usermanager.JahiaGroupManagerService.groupPathByGroupNameCache",
                            new Searchable(), new GroupPathByGroupNameCacheEntryFactory());
                }
            }
        }
        return groupPathByGroupNameCache;
    }

    SelfPopulatingCache getMembershipCache() {
        // First do non-synchronized check to avoid locking any threads that invoke the method simultaneously.
        if (membershipCache == null) {
            // Then check-again-and-initialize-if-needed within the synchronized block to ensure check-and-initialization consistency.
            synchronized (this) {
                if (membershipCache == null) {
                    membershipCache = ehCacheProvider.registerSelfPopulatingCache(
                            "org.jahia.services.usermanager.JahiaGroupManagerService.membershipCache",
                            new MembershipCacheEntryFactory());
                }
            }
        }
        return membershipCache;
    }

    private static GroupPathByGroupNameCacheKey getPathCacheKey(String groupPath) {
        return new GroupPathByGroupNameCacheKey(
                groupPath.startsWith("/sites/") ? StringUtils.substringBetween(groupPath, "/sites/", "/") : null,
                StringUtils.substringAfterLast(groupPath, "/"));
    }

    private String internalGetGroupPath(String siteKey, String name) throws RepositoryException {
        StringBuilder buff = new StringBuilder();
        buff.append("SELECT [j:nodename] FROM [" + Constants.JAHIANT_GROUP + "] as group where localname()='")
                .append(JCRContentUtils.sqlEncode(name)).append("' ");
        if (siteKey != null) {
            buff.append("and isdescendantnode(group,'/sites/").append(JCRContentUtils.sqlEncode(siteKey))
                    .append("/groups')");
        } else {
            buff.append("and isdescendantnode(group,'/groups')");
        }

        Query q = JCRSessionFactory.getInstance().getCurrentSystemSession(Constants.LIVE_WORKSPACE, null, null)
                .getWorkspace().getQueryManager().createQuery(buff.toString(), Query.JCR_SQL2);
        q.setLimit(1);
        RowIterator it = q.execute().getRows();
        if (!it.hasNext()) {
            return null;
        }
        return it.nextRow().getPath();
    }

    private List<String> internalGetMembershipByPath(String principalPath) {
        try {
            JCRNodeWrapper principalNode = JCRSessionFactory.getInstance()
                    .getCurrentSystemSession(Constants.LIVE_WORKSPACE, null, null).getNode(principalPath);
            Set<String> groups = new LinkedHashSet<String>();
            try {
                populateGroups(groups, principalNode);
            } catch (JahiaException e) {
                logger.warn("Error retrieving membership for user " + principalPath, e);
            }
            if (principalNode instanceof JCRUserNode) {
                JCRUserNode userNode = (JCRUserNode) principalNode;
                if (!JahiaUserManagerService.isGuest(userNode)) {
                    populateSpecialGroups(groups, JahiaGroupManagerService.USERS_GROUPPATH);
                    if (userNode.getRealm() != null) {
                        String siteUsers = "/sites/" + userNode.getRealm() + "/groups/"
                                + JahiaGroupManagerService.SITE_USERS_GROUPNAME;
                        populateSpecialGroups(groups, siteUsers);
                    }
                }
                populateSpecialGroups(groups, JahiaGroupManagerService.GUEST_GROUPPATH);
            }
            return new LinkedList<String>(groups);
        } catch (PathNotFoundException e) {
            // Non existing user/group
        } catch (RepositoryException e) {
            logger.error("Error retrieving membership for user " + principalPath + ", will return empty list", e);
        }
        return Collections.emptyList();
    }

    @SuppressWarnings("unchecked")
    private void populateSpecialGroups(Set<String> groups, String groupPath) {
        groups.add(groupPath);
        groups.addAll((List<String>) getMembershipCache().get(groupPath).getObjectValue());
    }

    private void populateGroups(Set<String> groups, JCRNodeWrapper principal)
            throws RepositoryException, JahiaException {
        try {
            PropertyIterator weakReferences = principal.getWeakReferences("j:member");
            while (weakReferences.hasNext()) {
                try {
                    Property property = weakReferences.nextProperty();
                    if (property != null && property.getPath() != null
                            && property.getPath().contains("/j:members/")) {
                        JCRNodeWrapper group = (JCRNodeWrapper) property.getSession()
                                .getNode(StringUtils.substringBefore(property.getPath(), "/j:members/"));
                        if (group.isNodeType(Constants.JAHIANT_GROUP)) {
                            if (groups.add(group.getPath())) {
                                // recurse on the found group only we have not done it yet
                                populateGroups(groups, group);
                            }
                        }
                    }
                } catch (ItemNotFoundException e) {
                    logger.warn("Cannot find group for " + principal.getPath(), e);
                }
            }
        } catch (InvalidItemStateException e) {
            logger.warn("Cannot find membership for " + principal.getPath(), e);
        }
    }

    public void setEhCacheProvider(EhCacheProvider ehCacheProvider) {
        this.ehCacheProvider = ehCacheProvider;
    }

    public void setTimeToLiveForNonExistingGroups(int timeToLiveForNonExistingGroups) {
        this.timeToLiveForNonExistingGroups = timeToLiveForNonExistingGroups;
    }

    public void updatePathCacheAdded(String groupPath) {
        getGroupPathByGroupNameCache().put(new Element(getPathCacheKey(groupPath), groupPath));
    }

    public void updatePathCacheRemoved(String groupPath) {
        getGroupPathByGroupNameCache().remove(getPathCacheKey(groupPath));
    }

    public void clearNonExistingGroupsCache() {
        SelfPopulatingCache cache = getGroupPathByGroupNameCache();
        Results results = cache.createQuery().addCriteria(new EqualTo("value", "")).includeKeys().execute();
        for (Result result : results.all()) {
            cache.remove(result.getKey());
        }
    }
}