You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by jo...@apache.org on 2021/04/07 08:04:00 UTC

[sling-org-apache-sling-resourceresolver] branch master updated: SLING-10269 cache the result of isResourceType() (#43)

This is an automated email from the ASF dual-hosted git repository.

joerghoh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourceresolver.git


The following commit(s) were added to refs/heads/master by this push:
     new 72c0576  SLING-10269 cache the result of isResourceType() (#43)
72c0576 is described below

commit 72c05768f4cc38c8e8073a2eaebbec1b894b8ab4
Author: Jörg Hoh <jo...@users.noreply.github.com>
AuthorDate: Wed Apr 7 10:03:54 2021 +0200

    SLING-10269 cache the result of isResourceType() (#43)
---
 .../impl/ResourceResolverImpl.java                 | 101 +++++++++++++++++----
 .../impl/ResourceResolverImplTest.java             |  28 ++++++
 2 files changed, 109 insertions(+), 20 deletions(-)

diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
index 2e47747..255176d 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
@@ -27,8 +27,10 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.StringTokenizer;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.commons.lang3.StringUtils;
 import org.jetbrains.annotations.Nullable;
@@ -92,6 +94,9 @@ public class ResourceResolverImpl extends SlingAdaptable implements ResourceReso
     /** Resource resolver context. */
     private final ResourceResolverContext context;
 
+    protected final Map<ResourceTypeInformation,Boolean> resourceTypeLookupCache = new ConcurrentHashMap<>();
+
+
     private volatile Exception closedResolverException;
 
     public ResourceResolverImpl(final CommonResourceResolverFactoryImpl factory, final boolean isAdmin, final Map<String, Object> authenticationInfo) throws LoginException {
@@ -998,6 +1003,7 @@ public class ResourceResolverImpl extends SlingAdaptable implements ResourceReso
     @Override
     public void commit() throws PersistenceException {
         this.control.commit(this.context);
+        resourceTypeLookupCache.clear();
     }
 
     /**
@@ -1044,30 +1050,46 @@ public class ResourceResolverImpl extends SlingAdaptable implements ResourceReso
      */
     @Override
     public boolean isResourceType(final Resource resource, final String resourceType) {
-        boolean result = false;
+
         if ( resource != null && resourceType != null ) {
-             // Check if the resource is of the given type. This method first checks the
-             // resource type of the resource, then its super resource type and continues
-             //  to go up the resource super type hierarchy.
-             if (ResourceTypeUtil.areResourceTypesEqual(resourceType, resource.getResourceType(), factory.getSearchPath())) {
-                 result = true;
-             } else {
-                 Set<String> superTypesChecked = new HashSet<>();
-                 String superType = this.getParentResourceType(resource);
-                 while (!result && superType != null) {
-                     if (ResourceTypeUtil.areResourceTypesEqual(resourceType, superType, factory.getSearchPath())) {
-                         result = true;
-                     } else {
-                         superTypesChecked.add(superType);
-                         superType = this.getParentResourceType(superType);
-                         if (superType != null && superTypesChecked.contains(superType)) {
-                             throw new SlingException("Cyclic dependency for resourceSuperType hierarchy detected on resource " + resource.getPath(), null);
-                         }
+             ResourceTypeInformation key = new ResourceTypeInformation(resource.getResourceType(),resource.getResourceSuperType(), resourceType);
+             Boolean value = resourceTypeLookupCache.computeIfAbsent(key,
+                     (k) -> isResourceTypeInternal(resource, resourceType));
+             return value.booleanValue();
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Check if the resource is of the given type. This method first checks the
+     * resource type of the resource, then its super resource type and continues
+     * to go up the resource super type hierarchy.
+     * @param resource the resource
+     * @param resourceType the resource type to compare to
+     * @return
+     */
+    boolean isResourceTypeInternal(final Resource resource, final String resourceType) {
+        boolean result = false;
+        if (ResourceTypeUtil.areResourceTypesEqual(resourceType, resource.getResourceType(), factory.getSearchPath())) {
+             // direct match
+             result = true;
+         } else {
+
+             Set<String> superTypesChecked = new HashSet<>();
+             String superType = this.getParentResourceType(resource);
+             while (!result && superType != null) {
+                 if (ResourceTypeUtil.areResourceTypesEqual(resourceType, superType, factory.getSearchPath())) {
+                     result = true;
+                 } else {
+                     superTypesChecked.add(superType);
+                     superType = this.getParentResourceType(superType);
+                     if (superType != null && superTypesChecked.contains(superType)) {
+                         throw new SlingException("Cyclic dependency for resourceSuperType hierarchy detected on resource " + resource.getPath(), null);
                      }
                  }
              }
-
-        }
+         }
         return result;
     }
 
@@ -1077,6 +1099,7 @@ public class ResourceResolverImpl extends SlingAdaptable implements ResourceReso
     @Override
     public void refresh() {
         this.control.refresh(this.context);
+        resourceTypeLookupCache.clear();
     }
 
     @Override
@@ -1117,4 +1140,42 @@ public class ResourceResolverImpl extends SlingAdaptable implements ResourceReso
         }
         return rsrc;
     }
+
+
+
+    // Simple pojo acting as key for the resourceTypeLookupCache 
+    public class ResourceTypeInformation {
+ 
+        String s1;
+        String s2;
+        String s3;
+
+        public ResourceTypeInformation (String resourceType, String resourceSuperType, String resourceTypeToCompareTo) {
+            this.s1 = resourceType;
+            this.s2 = resourceSuperType;
+            this.s3 = resourceTypeToCompareTo;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + Objects.hash(s1, s2, s3);
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            ResourceTypeInformation other = (ResourceTypeInformation) obj;
+            return Objects.equals(s1, other.s1) && Objects.equals(s2, other.s2) && Objects.equals(s3, other.s3);
+        }
+    }
+
+
 }
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java
index 3eee2cf..eb9531d 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java
@@ -29,7 +29,11 @@ import static org.junit.Assert.fail;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
 
+import static org.mockito.Mockito.eq;
+
+import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -49,6 +53,7 @@ import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceResolverFactory;
 import org.apache.sling.api.resource.SyntheticResource;
+import org.apache.sling.resourceresolver.impl.ResourceResolverImpl.ResourceTypeInformation;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker;
@@ -57,6 +62,7 @@ import org.apache.sling.spi.resource.provider.ResourceContext;
 import org.apache.sling.spi.resource.provider.ResourceProvider;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mockito;
 import org.mockito.internal.util.reflection.Whitebox;
 import org.osgi.framework.Bundle;
 
@@ -521,6 +527,28 @@ public class ResourceResolverImplTest {
         assertFalse(resolver.isResourceType(r, "h:p"));
     }
 
+    @Test public void testIsResourceTypeCached() throws Exception {
+        final PathBasedResourceResolverImpl resolver = Mockito.spy(getPathBasedResourceResolver());
+        final Resource r1 = resolver.add(new SyntheticResource(resolver, "/a", "a:b"));
+        final Resource r2 = resolver.add(new SyntheticResourceWithSupertype(resolver, "/b", "a:b", "c:d"));
+
+        // 1st lookup needs to get through, 2nd will be taken from cache
+        assertTrue(resolver.isResourceType(r1, "a:b"));
+        Mockito.verify(resolver, Mockito.times(1)).isResourceTypeInternal(eq(r1), eq("a:b"));
+        assertTrue(resolver.isResourceType(r1, "a:b"));
+        Mockito.verify(resolver, Mockito.times(1)).isResourceTypeInternal(eq(r1), eq("a:b"));
+
+        resolver.refresh();
+        assertTrue(resolver.isResourceType(r1, "a:b"));
+        Mockito.verify(resolver, Mockito.times(2)).isResourceTypeInternal(eq(r1), eq("a:b"));
+
+        // make sure that resources with the same resourceType but different resourceSuperType are
+        // treated differently
+        assertTrue(resolver.isResourceType(r2, "c:d"));
+        assertFalse(resolver.isResourceType(r1, "c:d"));
+
+    }
+
     @Test public void testIsResourceTypeWithPaths() {
         final PathBasedResourceResolverImpl resolver = getPathBasedResourceResolver();