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();