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;
- }
-}