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

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


The following commit(s) were added to refs/heads/master by this push:
     new c8fb61b  SLING-11608 - Cache script dependency resolution
c8fb61b is described below

commit c8fb61bc639b363153dbc34de14e2a44c7be03cf
Author: Radu Cotescu <[email protected]>
AuthorDate: Mon Oct 3 18:42:49 2022 +0200

    SLING-11608 - Cache script dependency resolution
    
    * implemented a cache in ScriptDependencyResolver
---
 pom.xml                                            |   5 +
 .../impl/engine/SightlyEngineConfiguration.java    |  13 ++
 .../engine/compiled/SlingHTLMasterCompiler.java    |  13 +-
 .../engine/extension/use/RenderUnitProvider.java   |   7 +-
 .../engine/extension/use/ScriptUseProvider.java    |  12 +-
 .../impl/utils/ScriptDependencyResolver.java       | 199 +++++++++++++++++++++
 .../scripting/sightly/impl/utils/ScriptUtils.java  |  63 -------
 7 files changed, 231 insertions(+), 81 deletions(-)

diff --git a/pom.xml b/pom.xml
index 2cf7c35..68b3366 100644
--- a/pom.xml
+++ b/pom.xml
@@ -65,6 +65,11 @@
     <!-- 
======================================================================= -->
     <build>
         <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.10.1</version>
+            </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
diff --git 
a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/SightlyEngineConfiguration.java
 
b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/SightlyEngineConfiguration.java
index 44ed570..adca8e9 100644
--- 
a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/SightlyEngineConfiguration.java
+++ 
b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/SightlyEngineConfiguration.java
@@ -73,6 +73,13 @@ public class SightlyEngineConfiguration {
         )
         boolean legacyBooleanCasting() default true;
 
+        @AttributeDefinition(
+                name = "Script Resolution Cache Size",
+                description = "The Script Resolution Cache allows caching 
script dependencies resolution based on the the script caller, " +
+                        "reducing the number of resource tree lookups. A value 
lower than 1024 disables the cache."
+        )
+        int scriptResolutionCacheSize() default 0;
+
     }
 
     private String engineVersion = "0";
@@ -80,6 +87,7 @@ public class SightlyEngineConfiguration {
     private String bundleSymbolicName = "org.apache.sling.scripting.sightly";
     private Set<String> allowedExpressionOptions;
     private boolean legacyBooleanCasting;
+    private int scriptResolutionCacheSize = 0;
 
     public static final boolean LEGACY_BOOLEAN_CASTING_DEFAULT = true;
 
@@ -107,6 +115,10 @@ public class SightlyEngineConfiguration {
         return legacyBooleanCasting;
     }
 
+    public int getScriptResolutionCacheSize() {
+        return scriptResolutionCacheSize;
+    }
+
     @Activate
     protected void activate(Configuration configuration) {
         InputStream ins = null;
@@ -136,5 +148,6 @@ public class SightlyEngineConfiguration {
         keepGenerated = configuration.keepGenerated();
         allowedExpressionOptions = new 
HashSet<>(Arrays.asList(configuration.allowedExpressionOptions()));
         legacyBooleanCasting = configuration.legacyBooleanCasting();
+        scriptResolutionCacheSize = configuration.scriptResolutionCacheSize();
     }
 }
diff --git 
a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/compiled/SlingHTLMasterCompiler.java
 
b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/compiled/SlingHTLMasterCompiler.java
index bfa4b3c..53b4de0 100644
--- 
a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/compiled/SlingHTLMasterCompiler.java
+++ 
b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/compiled/SlingHTLMasterCompiler.java
@@ -63,7 +63,7 @@ import 
org.apache.sling.scripting.sightly.impl.engine.SightlyEngineConfiguration
 import org.apache.sling.scripting.sightly.impl.engine.SightlyScriptEngine;
 import org.apache.sling.scripting.sightly.impl.utils.BindingsUtils;
 import org.apache.sling.scripting.sightly.impl.utils.Patterns;
-import org.apache.sling.scripting.sightly.impl.utils.ScriptUtils;
+import org.apache.sling.scripting.sightly.impl.utils.ScriptDependencyResolver;
 import 
org.apache.sling.scripting.sightly.java.compiler.GlobalShadowCheckBackendCompiler;
 import 
org.apache.sling.scripting.sightly.java.compiler.JavaClassBackendCompiler;
 import org.apache.sling.scripting.sightly.render.RenderContext;
@@ -106,6 +106,9 @@ public class SlingHTLMasterCompiler {
     @Reference
     private ScriptCache scriptCache;
 
+    @Reference
+    private ScriptDependencyResolver scriptDependencyResolver;
+
     private static final String NO_SCRIPT = "NO_SCRIPT";
     private static final String JAVA_EXTENSION = ".java";
     static final String SIGHTLY_CONFIG_FILE = "/sightly.config";
@@ -181,11 +184,7 @@ public class SlingHTLMasterCompiler {
                     return getUseObjectAndRecompileIfNeeded(pojoResource);
                 }
             } else {
-                Resource pojoResource = ScriptUtils.resolveScript(
-                        
scriptingResourceResolverProvider.getRequestScopedResourceResolver(),
-                        renderContext,
-                        className + JAVA_EXTENSION
-                );
+                Resource pojoResource = 
scriptDependencyResolver.resolveScript(renderContext, className + 
JAVA_EXTENSION);
                 if (pojoResource != null) {
                     return getUseObjectAndRecompileIfNeeded(pojoResource);
                 }
@@ -293,7 +292,7 @@ public class SlingHTLMasterCompiler {
                 pathElements.append("/");
             }
         }
-        return resolver.getResource(pathElements.toString() + JAVA_EXTENSION);
+        return resolver.getResource(pathElements + JAVA_EXTENSION);
     }
 
     /**
diff --git 
a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/use/RenderUnitProvider.java
 
b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/use/RenderUnitProvider.java
index 2dd9236..cebe1dd 100644
--- 
a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/use/RenderUnitProvider.java
+++ 
b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/use/RenderUnitProvider.java
@@ -41,7 +41,7 @@ import 
org.apache.sling.scripting.sightly.impl.engine.SightlyScriptEngine;
 import 
org.apache.sling.scripting.sightly.impl.engine.SightlyScriptEngineFactory;
 import 
org.apache.sling.scripting.sightly.impl.engine.bundled.BundledUnitManagerImpl;
 import org.apache.sling.scripting.sightly.impl.utils.BindingsUtils;
-import org.apache.sling.scripting.sightly.impl.utils.ScriptUtils;
+import org.apache.sling.scripting.sightly.impl.utils.ScriptDependencyResolver;
 import org.apache.sling.scripting.sightly.render.RenderContext;
 import org.apache.sling.scripting.sightly.render.RenderUnit;
 import org.apache.sling.scripting.sightly.use.ProviderOutcome;
@@ -84,7 +84,7 @@ public class RenderUnitProvider implements UseProvider {
     private ScriptEngineManager scriptEngineManager;
 
     @Reference
-    private ScriptingResourceResolverProvider 
scriptingResourceResolverProvider;
+    private ScriptDependencyResolver scriptDependencyResolver;
 
     @Override
     public ProviderOutcome provide(String identifier, RenderContext 
renderContext, Bindings arguments) {
@@ -92,8 +92,7 @@ public class RenderUnitProvider implements UseProvider {
             Bindings globalBindings = renderContext.getBindings();
             SlingScriptHelper sling = BindingsUtils.getHelper(globalBindings);
             SlingHttpServletRequest request = 
BindingsUtils.getRequest(globalBindings);
-            final Resource renderUnitResource = 
ScriptUtils.resolveScript(scriptingResourceResolverProvider
-                    .getRequestScopedResourceResolver(), renderContext, 
identifier);
+            final Resource renderUnitResource = 
scriptDependencyResolver.resolveScript(renderContext, identifier);
             if (renderUnitResource == null) {
                 // attempt to find a bundled render unit that does not expose 
a servlet resource via the search paths
                 RenderUnit renderUnit = 
bundledUnitManager.getRenderUnit(globalBindings, identifier);
diff --git 
a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/use/ScriptUseProvider.java
 
b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/use/ScriptUseProvider.java
index c425a68..736ab8a 100644
--- 
a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/use/ScriptUseProvider.java
+++ 
b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/use/ScriptUseProvider.java
@@ -36,12 +36,11 @@ import org.apache.sling.api.scripting.SlingBindings;
 import org.apache.sling.api.scripting.SlingScript;
 import org.apache.sling.scripting.api.CachedScript;
 import org.apache.sling.scripting.api.ScriptCache;
-import 
org.apache.sling.scripting.api.resource.ScriptingResourceResolverProvider;
 import org.apache.sling.scripting.core.ScriptNameAwareReader;
 import 
org.apache.sling.scripting.sightly.impl.engine.SightlyScriptEngineFactory;
 import 
org.apache.sling.scripting.sightly.impl.engine.bundled.BundledUnitManagerImpl;
 import org.apache.sling.scripting.sightly.impl.utils.BindingsUtils;
-import org.apache.sling.scripting.sightly.impl.utils.ScriptUtils;
+import org.apache.sling.scripting.sightly.impl.utils.ScriptDependencyResolver;
 import org.apache.sling.scripting.sightly.render.RenderContext;
 import org.apache.sling.scripting.sightly.use.ProviderOutcome;
 import org.apache.sling.scripting.sightly.use.UseProvider;
@@ -82,9 +81,6 @@ public class ScriptUseProvider implements UseProvider {
 
     private static final Logger log = 
LoggerFactory.getLogger(ScriptUseProvider.class);
 
-    @Reference
-    private ScriptingResourceResolverProvider 
scriptingResourceResolverProvider;
-
     @Reference
     private BundledUnitManagerImpl bundledUnitManager;
 
@@ -94,6 +90,9 @@ public class ScriptUseProvider implements UseProvider {
     @Reference
     private ScriptCache scriptCache;
 
+    @Reference
+    protected ScriptDependencyResolver scriptDependencyResolver;
+
     @Override
     public ProviderOutcome provide(String scriptName, RenderContext 
renderContext, Bindings arguments) {
         Bindings globalBindings = renderContext.getBindings();
@@ -135,8 +134,7 @@ public class ScriptUseProvider implements UseProvider {
                 return ProviderOutcome.failure(e);
             }
         }
-        Resource scriptResource = 
ScriptUtils.resolveScript(scriptingResourceResolverProvider.getRequestScopedResourceResolver(),
-                renderContext, scriptName);
+        Resource scriptResource = 
scriptDependencyResolver.resolveScript(renderContext, scriptName);
         if (scriptResource == null) {
             log.debug("Path does not match an existing resource: {}", 
scriptName);
             return ProviderOutcome.failure();
diff --git 
a/src/main/java/org/apache/sling/scripting/sightly/impl/utils/ScriptDependencyResolver.java
 
b/src/main/java/org/apache/sling/scripting/sightly/impl/utils/ScriptDependencyResolver.java
new file mode 100644
index 0000000..034de29
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/scripting/sightly/impl/utils/ScriptDependencyResolver.java
@@ -0,0 +1,199 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ 
******************************************************************************/
+package org.apache.sling.scripting.sightly.impl.utils;
+
+import java.util.Dictionary;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.api.SlingHttpServletRequest;
+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.ResourceUtil;
+import 
org.apache.sling.api.resource.observation.ExternalResourceChangeListener;
+import org.apache.sling.api.resource.observation.ResourceChange;
+import org.apache.sling.api.resource.observation.ResourceChangeListener;
+import org.apache.sling.api.scripting.SlingScriptHelper;
+import 
org.apache.sling.scripting.api.resource.ScriptingResourceResolverProvider;
+import org.apache.sling.scripting.sightly.engine.ResourceResolution;
+import 
org.apache.sling.scripting.sightly.impl.engine.SightlyEngineConfiguration;
+import org.apache.sling.scripting.sightly.render.RenderContext;
+import org.jetbrains.annotations.NotNull;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+@Component(
+        service = {
+                ScriptDependencyResolver.class,
+                ResourceChangeListener.class,
+        },
+        property = {
+                // listen to changes to all search paths
+                ResourceChangeListener.PATHS + "=.",
+                ResourceChangeListener.CHANGES + "=" + 
ResourceChangeListener.CHANGE_ADDED,
+                ResourceChangeListener.CHANGES + "=" + 
ResourceChangeListener.CHANGE_CHANGED,
+                ResourceChangeListener.CHANGES + "=" + 
ResourceChangeListener.CHANGE_REMOVED,
+        }
+)
+public class ScriptDependencyResolver implements ResourceChangeListener, 
ExternalResourceChangeListener, BundleListener {
+
+    public static final String BUNDLED_SCRIPTS_REQUIREMENT = 
"osgi.extender;filter:=\"(&(osgi.extender=sling.scripting)(version>=1.0.0)(!" +
+            "(version>=2.0.0)))\"";
+
+    @Reference
+    private SightlyEngineConfiguration sightlyEngineConfiguration;
+
+    @Reference
+    private ScriptingResourceResolverProvider 
scriptingResourceResolverProvider;
+
+    // not used, however we want this component to restart if the RRF is 
reconfigured
+    @Reference
+    private ResourceResolverFactory resourceResolverFactory;
+
+    private Map<String, String> resolutionCache = new Cache(0);
+    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
+    private final Lock readLock = rwl.readLock();
+    private final Lock writeLock = rwl.writeLock();
+
+    @Activate
+    private void activate(ComponentContext componentContext) {
+        int cacheSize = 
sightlyEngineConfiguration.getScriptResolutionCacheSize();
+        if (cacheSize < 1024) {
+            resolutionCache = new Cache(0);
+        } else {
+            resolutionCache = new Cache(cacheSize);
+        }
+        componentContext.getBundleContext().addBundleListener(this);
+    }
+
+    public Resource resolveScript(RenderContext renderContext, String 
scriptIdentifier) {
+        readLock.lock();
+        try {
+            SlingHttpServletRequest request = 
BindingsUtils.getRequest(renderContext.getBindings());
+            String cacheKey = request.getResource().getResourceType() + ":" + 
scriptIdentifier;
+            Resource result = null;
+            if (!resolutionCache.containsKey(cacheKey)) {
+                readLock.unlock();
+                writeLock.lock();
+                try {
+                    Resource caller =
+                            
ResourceResolution.getResourceForRequest(scriptingResourceResolverProvider.getRequestScopedResourceResolver(),
+                                    request);
+                    result = 
ResourceResolution.getResourceFromSearchPath(caller, scriptIdentifier);
+                    if (result == null) {
+                        SlingScriptHelper sling = 
BindingsUtils.getHelper(renderContext.getBindings());
+                        if (sling != null) {
+                            caller = 
getResource(scriptingResourceResolverProvider.getRequestScopedResourceResolver(),
+                                    sling.getScript().getScriptResource());
+                            result = 
ResourceResolution.getResourceFromSearchPath(caller, scriptIdentifier);
+                        }
+                    }
+                    if (result != null) {
+                       resolutionCache.put(cacheKey, result.getPath());
+                    }
+                    readLock.lock();
+                } finally {
+                    writeLock.unlock();
+                }
+            } else {
+                String scriptPath = resolutionCache.get(cacheKey);
+                result = 
scriptingResourceResolverProvider.getRequestScopedResourceResolver().getResource(scriptPath);
+            }
+            return result;
+        } finally {
+            readLock.unlock();
+        }
+    }
+
+    @Override
+    public void onChange(@NotNull List<ResourceChange> changes) {
+        // we won't be specific about the changes; wipe the whole cache
+        writeLock.lock();
+        try {
+            resolutionCache.clear();
+        } finally {
+            writeLock.unlock();
+        }
+    }
+
+    @Override
+    public void bundleChanged(BundleEvent event) {
+        // we won't be specific about the changes; wipe the whole cache
+        Dictionary<String, String> bundleHeaders = 
event.getBundle().getHeaders();
+        String requireCapabilityHeader = 
bundleHeaders.get("Require-Capability");
+        if (StringUtils.isNotEmpty(requireCapabilityHeader) && 
requireCapabilityHeader.contains(BUNDLED_SCRIPTS_REQUIREMENT)) {
+            writeLock.lock();
+            try {
+                resolutionCache.clear();
+            } finally {
+                writeLock.unlock();
+            }
+        }
+    }
+
+    private Resource getResource(@NotNull ResourceResolver resolver, @NotNull 
Resource resource) {
+        String path = resource.getPath();
+        if (path.startsWith("/")) {
+            return resolver.getResource(path);
+        } else {
+            for (String sp : resolver.getSearchPath()) {
+                String absolutePath = ResourceUtil.normalize(sp + path);
+                if (absolutePath != null) {
+                    Resource resolved = resolver.getResource(absolutePath);
+                    if (resolved != null) {
+                        return resolved;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    private static class Cache extends LinkedHashMap<String, String> {
+
+        private final int cacheSize;
+
+        public Cache(int cacheSize) {
+            super();
+            this.cacheSize = cacheSize;
+        }
+
+        @Override
+        protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
+            return size() > cacheSize;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o instanceof Cache) {
+                Cache other = (Cache) o;
+                return super.equals(o) && cacheSize == other.cacheSize;
+            }
+            return false;
+        }
+    }
+
+}
diff --git 
a/src/main/java/org/apache/sling/scripting/sightly/impl/utils/ScriptUtils.java 
b/src/main/java/org/apache/sling/scripting/sightly/impl/utils/ScriptUtils.java
deleted file mode 100644
index d045624..0000000
--- 
a/src/main/java/org/apache/sling/scripting/sightly/impl/utils/ScriptUtils.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*******************************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- 
******************************************************************************/
-package org.apache.sling.scripting.sightly.impl.utils;
-
-import org.apache.sling.api.SlingHttpServletRequest;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.api.resource.ResourceResolver;
-import org.apache.sling.api.resource.ResourceUtil;
-import org.apache.sling.api.scripting.SlingScriptHelper;
-import org.apache.sling.scripting.sightly.engine.ResourceResolution;
-import org.apache.sling.scripting.sightly.render.RenderContext;
-import org.jetbrains.annotations.NotNull;
-
-public class ScriptUtils {
-
-    private ScriptUtils() {}
-
-    public static Resource resolveScript(ResourceResolver resolver, 
RenderContext renderContext, String scriptIdentifier) {
-        SlingHttpServletRequest request = 
BindingsUtils.getRequest(renderContext.getBindings());
-        Resource caller = ResourceResolution.getResourceForRequest(resolver, 
request);
-        Resource result = ResourceResolution.getResourceFromSearchPath(caller, 
scriptIdentifier);
-        if (result == null) {
-            SlingScriptHelper sling = 
BindingsUtils.getHelper(renderContext.getBindings());
-            if (sling != null) {
-                caller = getResource(resolver, 
sling.getScript().getScriptResource());
-                result = ResourceResolution.getResourceFromSearchPath(caller, 
scriptIdentifier);
-            }
-        }
-        return result;
-    }
-
-    private static Resource getResource(@NotNull ResourceResolver resolver, 
@NotNull Resource resource) {
-        String path = resource.getPath();
-        if (path.startsWith("/")) {
-            return resolver.getResource(path);
-        } else {
-            for (String sp : resolver.getSearchPath()) {
-                String absolutePath = ResourceUtil.normalize(sp + path);
-                if (absolutePath != null) {
-                    Resource resolved = resolver.getResource(absolutePath);
-                    if (resolved != null) {
-                        return resolved;
-                    }
-                }
-            }
-        }
-        return null;
-    }
-}

Reply via email to