Package org.jahia.services.render.filter.cache

Source Code of org.jahia.services.render.filter.cache.DefaultCacheKeyGenerator

/**
* This file is part of Jahia, next-generation open source CMS:
* Jahia's next-generation, open source CMS stems from a widely acknowledged vision
* of enterprise application convergence - web, search, document, social and portal -
* unified by the simplicity of web content management.
*
* For more information, please visit http://www.jahia.com.
*
* Copyright (C) 2002-2011 Jahia Solutions Group SA. All rights reserved.
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* As a special exception to the terms and conditions of version 2.0 of
* the GPL (or any later version), you may redistribute this Program in connection
* with Free/Libre and Open Source Software ("FLOSS") applications as described
* in Jahia's FLOSS exception. You should have received a copy of the text
* describing the FLOSS exception, and it is also available here:
* http://www.jahia.com/license
*
* Commercial and Supported Versions of the program (dual licensing):
* alternatively, commercial and supported versions of the program may 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.render.filter.cache;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import org.apache.commons.collections.ListUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.StringUtils;
import org.apache.jackrabbit.core.security.JahiaAccessManager;
import org.jahia.api.Constants;
import org.jahia.services.cache.CacheImplementation;
import org.jahia.services.cache.ehcache.EhCacheProvider;
import org.jahia.services.content.JCRCallback;
import org.jahia.services.content.JCRNodeWrapper;
import org.jahia.services.content.JCRSessionWrapper;
import org.jahia.services.content.JCRTemplate;
import org.jahia.services.render.RenderContext;
import org.jahia.services.render.Resource;
import org.jahia.services.render.Template;
import org.jahia.services.usermanager.JahiaGroup;
import org.jahia.services.usermanager.JahiaGroupManagerService;
import org.jahia.services.usermanager.JahiaUser;
import org.jahia.services.usermanager.JahiaUserManagerService;
import org.slf4j.Logger;
import org.springframework.beans.factory.InitializingBean;

import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.query.Query;
import javax.jcr.query.QueryResult;
import java.text.FieldPosition;
import java.text.MessageFormat;
import java.text.ParseException;
import java.util.*;

/**
* Default implementation of the module output cache key generator.
*
* @author rincevent
* @author Sergiy Shyrkov
*/
public class DefaultCacheKeyGenerator implements CacheKeyGenerator, InitializingBean {

    private static transient Logger logger = org.slf4j.LoggerFactory.getLogger(DefaultCacheKeyGenerator.class);

    private static final Set<String> KNOWN_FIELDS = new LinkedHashSet<String>(Arrays.asList("workspace", "language",
            "path", "template", "templateType", "acls", "context", "wrapped", "custom", "queryString",
            "templateNodes","resourceID","inArea"));
    private static final String CACHE_NAME = "HTMLNodeUsersACLs";
    private static final String PROPERTY_CACHE_NAME = "HTMLRequiredPermissionsCache";
    public static final String PER_USER = "_perUser_";
    private List<String> fields = new LinkedList<String>(KNOWN_FIELDS);

    private MessageFormat format = new MessageFormat("#{0}#{1}#{2}#{3}#{4}#{5}#{6}#{7}#{8}#{9}#{10}#{11}#{12}");

    private JahiaGroupManagerService groupManagerService;
    private JahiaUserManagerService userManagerService;
    private final Map<String, Set<JahiaGroup>> aclGroups = new LinkedHashMap<String, Set<JahiaGroup>>();
    private EhCacheProvider cacheProvider;
    private Cache cache;
    private JCRTemplate template;
    private Cache permissionCache;

    public void setGroupManagerService(JahiaGroupManagerService groupManagerService) {
        this.groupManagerService = groupManagerService;
    }

    public void setUserManagerService(JahiaUserManagerService userManagerService) {
        this.userManagerService = userManagerService;
    }

    public String generate(Resource resource, RenderContext renderContext) {
        return format.format(getArguments(resource, renderContext), new StringBuffer(32), new FieldPosition(
                0)).toString();
    }

    private Object[] getArguments(Resource resource, RenderContext renderContext) {
        List<String> args = new LinkedList<String>();
        for (String field : fields) {
            if ("workspace".equals(field)) {
                args.add(resource.getWorkspace());
            } else if ("language".equals(field)) {
                args.add(resource.getLocale().toString());
            } else if ("path".equals(field)) {
                StringBuilder s = new StringBuilder(resource.getNode().getPath());
                if (Boolean.TRUE.equals(renderContext.getRequest().getAttribute("cache.mainResource"))) {
                    s.append("_mr_");
                }
                args.add(s.toString());
            } else if ("template".equals(field)) {
                if (resource.getContextConfiguration().equals("page") && resource.getNode().getPath().equals(
                        renderContext.getMainResource().getNode().getPath())) {
                    args.add(renderContext.getMainResource().getResolvedTemplate());
                } else {
                    args.add(resource.getResolvedTemplate());
                }
            } else if ("templateType".equals(field)) {
                String templateType = resource.getTemplateType();
                if (renderContext.isAjaxRequest()) {
                    templateType += ".ajax";
                }
                args.add(templateType);
            } else if ("queryString".equals(field)) {
                final String queryString = renderContext.getRequest().getQueryString();
                if (renderContext.getRequest().getParameter("ec") != null) {
                    try {
                        if (renderContext.getRequest().getParameter("ec").equals(resource.getNode().getIdentifier())) {
                            args.add(queryString != null ? queryString : "");
                        } else {
                            args.add("");
                        }
                    } catch (RepositoryException e) {
                        logger.debug(e.getMessage(),e);
                    }
                } else {
                    args.add(queryString != null ? queryString : "");
                }
            } else if ("acls".equals(field)) {
                args.add(appendAcls(resource, renderContext, true));
            } else if ("wrapped".equals(field)) {
                args.add(String.valueOf(resource.hasWrapper()));
            } else if ("context".equals(field)) {
                args.add(String.valueOf(resource.getContextConfiguration()));
            } else if ("custom".equals(field)) {
                args.add((String) resource.getModuleParams().get("module.cache.additional.key"));
            } else if ("templateNodes".equals(field)) {
                final Template t = (Template) renderContext.getRequest().getAttribute("previousTemplate");
                args.add(t != null ? t.serialize() : "");
            } else if ("resourceID".equals(field)) {
                try {
                    args.add(resource.getNode().getIdentifier());
                } catch (RepositoryException e) {
                    logger.error(e.getMessage(), e);
                }
            } else if ("inArea".equals(field)) {
                Object inArea = renderContext.getRequest().getAttribute("inArea");
                args.add(inArea != null ? inArea.toString() : "");
            }
        }
        return args.toArray(new String[KNOWN_FIELDS.size()]);
    }

    public String appendAcls(Resource resource, RenderContext renderContext, boolean appendNodePath) {
        try {
            if (renderContext.getRequest() != null && Boolean.TRUE.equals(renderContext.getRequest().getAttribute("cache.perUser"))) {
                return PER_USER;
            }

            JCRNodeWrapper node = resource.getNode();
            boolean checkRootPath = true;
            Element element = permissionCache.get(node.getPath());
            if(element!=null && Boolean.TRUE==((Boolean)element.getValue())) {
                node = renderContext.getMainResource().getNode();
                checkRootPath = false;
            } else if(element==null) {
                if (node.hasProperty("j:requiredPermissions")) {
                    permissionCache.put(new Element(node.getPath(),Boolean.TRUE));
                    node = renderContext.getMainResource().getNode();
                    checkRootPath = false;
                } else {
                    permissionCache.put(new Element(node.getPath(),Boolean.FALSE));
                }
            }
            String nodePath = node.getPath();
            return getAclsKeyPart(renderContext, checkRootPath, nodePath, appendNodePath);

        } catch (RepositoryException e) {
            logger.error(e.getMessage(), e);
        }
        return "";
    }

    public String getAclsKeyPart(RenderContext renderContext, boolean checkRootPath, String nodePath, boolean appendNodePath)
            throws RepositoryException {
        // Search for user specific acl
        JahiaUser principal = renderContext.getUser();
        final String userName = principal.getUsername();
        Element element = hasUserAcl(userName);
        if (element!=null) {
            Map<String, String> map = (Map<String, String>) element.getValue();
            String path = nodePath;
            while ((!path.equals("")) && !map.containsKey(path)) {
                path = StringUtils.substringBeforeLast(path, "/");
            }
            if (checkRootPath) {
                if (path.equals("")) {
                    path = "/";
                }
            }
            if (map.containsKey(path)) {
                return (String) map.get(path)+"_p_"+checkRootPath + (appendNodePath ? nodePath : "");
            }
        }
        synchronized (aclGroups) {
            StringBuilder b = new StringBuilder();
            String path = nodePath;
            Map<String, Set<JahiaGroup>> allAclsGroups = getAllAclsGroups();
            if (checkRootPath) {
                while (!allAclsGroups.containsKey(path) && !path.equals("")) {
                    path = StringUtils.substringBeforeLast(path, "/");
                }
                if (path.equals("")) {
                    path = "/";
                }
            }

            Set<JahiaGroup> aclGroups = new HashSet<JahiaGroup>();

            String fakePath = path;
            while (!fakePath.equals("")) {
                while (!allAclsGroups.containsKey(fakePath) && !fakePath.equals("")) {
                    fakePath = StringUtils.substringBeforeLast(fakePath, "/");
                }
                if (fakePath.equals("")) {
                    fakePath = "/";
                }
                aclGroups.addAll(allAclsGroups.get(fakePath));
                fakePath = StringUtils.substringBeforeLast(fakePath, "/");
            }

            for (JahiaGroup g : aclGroups) {
                if (g != null && g.isMember(principal)) {
                    if (b.length() > 0) {
                        b.append("|");
                    }
                    b.append(g.getGroupname());
                }
            }

            if (b.toString().equals(JahiaGroupManagerService.GUEST_GROUPNAME) && !userName.equals(
                    JahiaUserManagerService.GUEST_USERNAME)) {
                b.append("|" + JahiaGroupManagerService.USERS_GROUPNAME);
            }
            String userKey = b.toString();
            if ("".equals(userKey.trim()) && userName.equals(JahiaUserManagerService.GUEST_USERNAME)) {
                userKey = userName;
            }
            if ("".equals(userKey.trim())) {
                throw new RepositoryException(
                        "Userkey is empty while generating cache key for path " + path + " and nodepath = " + nodePath +
                        " checkrootpath = " + checkRootPath);
            }
            Set<String> roles = ((JahiaAccessManager) ((JCRNodeWrapper) renderContext.getMainResource().getNode()).getAccessControlManager()).getRoles(
                    path);
            b = new StringBuilder();
            for (String g : roles) {
                if (b.length() > 0) {
                    b.append("|");
                }
                b.append(g);
            }
            Map<String, String> map;
            element = (Element) cache.get(userName);
            if (element == null) {
                map = new LinkedHashMap<String, String>();
            } else {
                map = (Map<String, String>) element.getValue();
            }
            String value = userKey + "_r_" + b.toString();
            map.put(path, value);
            element = new Element(userName, map);
            element.setEternal(true);
            cache.put(element);
            return value + "_p_" + checkRootPath + (appendNodePath ? nodePath : "");
        }
    }

    private Element hasUserAcl(final String userName) throws RepositoryException {
        Element element = cache.get(userName);
        if (element == null) {
            initCache(userName);
            element = cache.get(userName);
        }
        return element;
    }

    private void initCache(final String userName) throws RepositoryException {
        template.doExecuteWithSystemSession(null, Constants.LIVE_WORKSPACE, new JCRCallback<Object>() {
            public Object doInJCR(JCRSessionWrapper session) throws RepositoryException {
                Query query = session.getWorkspace().getQueryManager().createQuery(
                        "select * from [jnt:ace] as ace where ace.[j:principal] = 'u:" + userName + "'",
                        Query.JCR_SQL2);
                QueryResult queryResult = query.execute();
                NodeIterator rowIterator = queryResult.getNodes();
                while (rowIterator.hasNext()) {
                    Node node = (Node) rowIterator.next();
                    String userPath = userManagerService.getUserSplittingRule().getPathForUsername(userName);
                    if (!node.getPath().startsWith(userPath + "/j:acl")) {
                        Map<String, String> map;
                        if (!cache.isKeyInCache(userName)) {
                            map = new LinkedHashMap<String, String>();
                        } else {
                            map = (Map<String, String>) ((Element) cache.get(userName)).getValue();
                        }
                        String path = node.getParent().getParent().getPath();
                        StringBuilder b = new StringBuilder();
                        Set<String> foundRoles = new HashSet<String>();
                        boolean granted = node.getProperty("j:aceType").getString().equals("GRANT");
                        if (granted) {
                            Value[] roles = node.getProperty(Constants.J_ROLES).getValues();
                            for (Value r : roles) {
                                String role = r.getString();
                                if (!foundRoles.contains(role)) {
                                    if (b.length() > 0) {
                                        b.append("|");
                                    }
                                    b.append(role);
                                    foundRoles.add(role);
                                }
                            }
                            map.put(path, userName + "_r_" + b.toString());
                            if (!"/".equals(path)) {
                                path = node.getParent().getParent().getParent().getPath();
                            }
                            map.put(path, userName + "_r_" + b.toString());
                            final Element element = new Element(userName, map);
                            element.setEternal(true);
                            cache.put(element);
                        }
                    }
                }
                return null;
            }
        });
    }

    private Map<String, Set<JahiaGroup>> getAllAclsGroups() throws RepositoryException {
        if (aclGroups.isEmpty()) {
            initAclGroups();
        }
        return aclGroups;
    }

    private void initAclGroups() throws RepositoryException {
        if (!aclGroups.isEmpty()) {
            return;
        }
//        synchronized (aclGroups) {
            if (aclGroups.isEmpty()) {
                template.doExecuteWithSystemSession(null, Constants.LIVE_WORKSPACE, new JCRCallback<Object>() {
                    public Object doInJCR(JCRSessionWrapper session) throws RepositoryException {
                        aclGroups.put("/", new HashSet<JahiaGroup>());
                        aclGroups.get("/").add(groupManagerService.lookupGroup(groupManagerService.ADMINISTRATORS_GROUPNAME));
                        Query groupQuery = session.getWorkspace().getQueryManager().createQuery(
                                "select * from [jnt:ace] as u where u.[j:principal] like 'g%'", Query.JCR_SQL2);
                        QueryResult groupQueryResult = groupQuery.execute();
                        final NodeIterator nodeIterator = groupQueryResult.getNodes();
                        while (nodeIterator.hasNext()) {
                            JCRNodeWrapper node = (JCRNodeWrapper) nodeIterator.next();
                            String s = StringUtils.substringAfter(node.getProperty("j:principal").getString(), ":");
                            JahiaGroup group = groupManagerService.lookupGroup(node.getResolveSite().getID(), s);
                            if (group == null) {
                                group = groupManagerService.lookupGroup(s);
                            }
                            if (group != null) {
                                String path = node.getParent().getParent().getPath();
                                boolean granted = node.getProperty("j:aceType").getString().equals("GRANT");
                                if (granted) {
                                    Set<JahiaGroup> groups;
                                    if (!aclGroups.containsKey(path)) {
                                        groups = new LinkedHashSet<JahiaGroup>();
                                        aclGroups.put(path, groups);
                                    } else {
                                        groups = aclGroups.get(path);
                                    }
                                    groups.add(group);
                                }
                            }
                        }
                        return null;
                    }
                });
            }
//        }
    }

    public String getPath(String key) throws ParseException {
        return parse(key).get("path").replaceAll("_mr_", "");
    }

    public Map<String, String> parse(String key) throws ParseException {
        Object[] values = format.parse(key);
        Map<String, String> result = new LinkedHashMap<String, String>(fields.size());
        for (int i = 0; i < values.length; i++) {
            String value = (String) values[i];
            result.put(fields.get(i), value == null || value.equals("null") ? null : value.replaceAll("_mr_", ""));
        }
        return result;
    }

    private Map<String, String> parseAsIs(String key) throws ParseException {
        Object[] values = format.parse(key);
        Map<String, String> result = new LinkedHashMap<String, String>(fields.size());
        for (int i = 0; i < values.length; i++) {
            String value = (String) values[i];
            result.put(fields.get(i), value == null || value.equals("null") ? null : value);
        }
        return result;
    }

    public String replaceField(String key, String fieldName, String newValue) throws ParseException {
        Map<String, String> args = parseAsIs(key);
        args.put(fieldName, newValue);
        return format.format(args.values().toArray(new String[KNOWN_FIELDS.size()]), new StringBuffer(32),
                new FieldPosition(0)).toString();
    }

    @SuppressWarnings("unchecked")
    public void setFields(List<String> fields) {
        this.fields = ListUtils.predicatedList(fields, new Predicate() {
            public boolean evaluate(Object object) {
                return (object instanceof String) && KNOWN_FIELDS.contains(object);
            }
        });
    }

    public void setFormat(String format) {
        this.format = new MessageFormat(format);
    }

    public void flushUsersGroupsKey() {
        synchronized (aclGroups) {
            aclGroups.clear();
            cache.removeAll();
            cache.flush();
        }
    }

    public void setCacheProvider(EhCacheProvider cacheProvider) {
        this.cacheProvider = cacheProvider;
    }

    /**
     * Invoked by a BeanFactory after it has set all bean properties supplied
     * (and satisfied BeanFactoryAware and ApplicationContextAware).
     * <p>This method allows the bean instance to perform initialization only
     * possible when all bean properties have been set and to throw an
     * exception in the event of misconfiguration.
     *
     * @throws Exception in the event of misconfiguration (such
     *                   as failure to set an essential property) or if initialization fails.
     */
    public void afterPropertiesSet() throws Exception {
        CacheManager cacheManager = cacheProvider.getCacheManager();
        cache = cacheManager.getCache(CACHE_NAME);
        if (cache == null) {
            cacheManager.addCache(CACHE_NAME);
            cache = cacheManager.getCache(CACHE_NAME);
        }
        cache.setStatisticsEnabled(cacheProvider.isStatisticsEnabled());

        permissionCache = cacheManager.getCache(PROPERTY_CACHE_NAME);
        if (permissionCache == null) {
            cacheManager.addCache(PROPERTY_CACHE_NAME);
            permissionCache = cacheManager.getCache(PROPERTY_CACHE_NAME);
        }
        permissionCache.setStatisticsEnabled(cacheProvider.isStatisticsEnabled());
    }

    public void setTemplate(JCRTemplate template) {
        this.template = template;
    }
}
TOP

Related Classes of org.jahia.services.render.filter.cache.DefaultCacheKeyGenerator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.