This is an automated email from the ASF dual-hosted git repository.
cziegeler pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-scripting-core.git
The following commit(s) were added to refs/heads/master by this push:
new 3249d39 SLING-12194 : Cycle between scripting services
3249d39 is described below
commit 3249d3989bd0ca7f7dc5552d4ce6f4853c577425
Author: Carsten Ziegeler <[email protected]>
AuthorDate: Fri Dec 8 16:03:42 2023 +0100
SLING-12194 : Cycle between scripting services
---
.../core/impl/ScriptCacheConsolePlugin.java | 3 +-
.../sling/scripting/core/impl/ScriptCacheImpl.java | 200 +++------------------
.../core/impl/ScriptCacheInvalidator.java | 177 ++++++++++++++++++
3 files changed, 204 insertions(+), 176 deletions(-)
diff --git
a/src/main/java/org/apache/sling/scripting/core/impl/ScriptCacheConsolePlugin.java
b/src/main/java/org/apache/sling/scripting/core/impl/ScriptCacheConsolePlugin.java
index 8285594..e520a20 100644
---
a/src/main/java/org/apache/sling/scripting/core/impl/ScriptCacheConsolePlugin.java
+++
b/src/main/java/org/apache/sling/scripting/core/impl/ScriptCacheConsolePlugin.java
@@ -20,7 +20,6 @@
package org.apache.sling.scripting.core.impl;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -81,7 +80,7 @@ public class ScriptCacheConsolePlugin extends
AbstractWebConsolePlugin {
throws ServletException, IOException {
if (scriptCache instanceof ScriptCacheImpl) {
ScriptCacheImpl scriptCacheImpl = (ScriptCacheImpl) scriptCache;
- List<String> scripts = new
ArrayList<>(scriptCacheImpl.getCachedScripts());
+ List<String> scripts = scriptCacheImpl.getCachedScripts();
StringBuilder sb = new StringBuilder();
sb.append("<script type='text/javascript'
src='").append(RESOURCES).append("/").append(SCRIPTCACHE_JS).append("'></script>");
sb.append("<div id='cached-scripts' class='ui-widget statline'>");
diff --git
a/src/main/java/org/apache/sling/scripting/core/impl/ScriptCacheImpl.java
b/src/main/java/org/apache/sling/scripting/core/impl/ScriptCacheImpl.java
index e1259a9..336f2a6 100644
--- a/src/main/java/org/apache/sling/scripting/core/impl/ScriptCacheImpl.java
+++ b/src/main/java/org/apache/sling/scripting/core/impl/ScriptCacheImpl.java
@@ -16,60 +16,32 @@
* specific language governing permissions and limitations
* under the License.
******************************************************************************/
-
package org.apache.sling.scripting.core.impl;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Dictionary;
import java.util.HashSet;
-import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.TreeSet;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
-import javax.script.Compilable;
-import javax.script.ScriptEngine;
-import javax.script.ScriptEngineFactory;
-
import org.apache.sling.api.resource.ResourceResolverFactory;
-import
org.apache.sling.api.resource.observation.ExternalResourceChangeListener;
-import org.apache.sling.api.resource.observation.ResourceChange;
-import org.apache.sling.api.resource.observation.ResourceChange.ChangeType;
-import org.apache.sling.api.resource.observation.ResourceChangeListener;
import org.apache.sling.scripting.api.CachedScript;
import org.apache.sling.scripting.api.ScriptCache;
import org.apache.sling.scripting.core.impl.helper.CachingMap;
-import org.apache.sling.scripting.core.impl.jsr223.SlingScriptEngineManager;
import org.apache.sling.serviceusermapping.ServiceUserMapped;
-import org.jetbrains.annotations.NotNull;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.event.Event;
-import org.osgi.service.event.EventConstants;
-import org.osgi.service.event.EventHandler;
import org.osgi.service.metatype.annotations.Designate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(
- service = {ScriptCache.class, EventHandler.class},
- property = {
- Constants.SERVICE_VENDOR + "=The Apache Software Foundation",
- EventConstants.EVENT_TOPIC +
"=org/apache/sling/scripting/core/impl/jsr223/SlingScriptEngineManager/*"
- }
+ immediate = true, // cache should be immediate
+ service = {ScriptCache.class}
)
@Designate(
ocd = ScriptCacheImplConfiguration.class
@@ -78,46 +50,28 @@ import org.slf4j.LoggerFactory;
* The {@code ScriptCache} stores information about {@link CompiledScript}
instances evaluated by various {@link ScriptEngine}s that
* implement the {@link Compilable} interface.
*/
-public class ScriptCacheImpl implements ScriptCache, ResourceChangeListener,
ExternalResourceChangeListener, EventHandler {
+public class ScriptCacheImpl implements ScriptCache {
private final Logger logger =
LoggerFactory.getLogger(ScriptCacheImpl.class);
public static final int DEFAULT_CACHE_SIZE = 65536;
- private final BundleContext bundleContext;
private final Map<String, SoftReference<CachedScript>> internalMap;
- private final Set<String> extensions = new TreeSet<>();
- private final String[] additionalExtensions;
-
- private volatile ServiceRegistration<ResourceChangeListener>
resourceChangeListener;
// use a static policy so that we can reconfigure the watched script files
if the search paths are changed
@Reference
private ResourceResolverFactory rrf;
- private final SlingScriptEngineManager slingScriptEngineManager;
-
- private final ExecutorService threadPool;
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock readLock = rwl.readLock();
private final Lock writeLock = rwl.writeLock();
- private volatile boolean active = false;
@Reference
private ServiceUserMapped serviceUserMapped;
@Activate
- public ScriptCacheImpl(@Reference final SlingScriptEngineManager
slingScriptEngineManager,
- final ScriptCacheImplConfiguration configuration,
- final BundleContext bundleCtx) {
- this.slingScriptEngineManager = slingScriptEngineManager;
- this.threadPool = Executors.newSingleThreadExecutor();
- this.bundleContext = bundleCtx;
- this.additionalExtensions =
configuration.org_apache_sling_scripting_cache_additional__extensions();
+ public ScriptCacheImpl(final ScriptCacheImplConfiguration configuration) {
this.internalMap = new
CachingMap<>(configuration.org_apache_sling_scripting_cache_size());
- this.initializeExtensions();
- this.active = true;
- this.configureCache();
}
@Override
@@ -156,143 +110,41 @@ public class ScriptCacheImpl implements ScriptCache,
ResourceChangeListener, Ext
}
@Override
- public boolean removeScript(String scriptPath) {
+ public boolean removeScript(final String scriptPath) {
writeLock.lock();
try {
- SoftReference<CachedScript> reference =
internalMap.remove(scriptPath);
- boolean result = reference != null;
- if (result) {
- logger.debug("Removed script {} from script cache.",
scriptPath);
- }
- return result;
- } finally {
- writeLock.unlock();
- }
- }
-
- @Override
- public void onChange(@NotNull List<ResourceChange> list) {
- for (final ResourceChange change : list) {
- Runnable eventTask = () -> {
- String path = change.getPath();
- writeLock.lock();
- try {
- final boolean removed = internalMap.remove(path) != null;
- logger.debug("Detected script change for {} - removed
entry from the cache.", path);
- if ( !removed && change.getType() == ChangeType.REMOVED ) {
- final String prefix = path + "/";
- final Set<String> removal = new HashSet<>();
- for(final Map.Entry<String,
SoftReference<CachedScript>> entry : internalMap.entrySet()) {
- if ( entry.getKey().startsWith(prefix) ) {
- removal.add(entry.getKey());
- }
- }
- for(final String key : removal) {
- internalMap.remove(key);
- logger.debug("Detected removal for {} - removed
entry {} from the cache.", path, key);
- }
+ boolean result = false;
+ if (scriptPath.endsWith("/") ) {
+ // prefix removal
+ final Set<String> removal = new HashSet<>();
+ for(final Map.Entry<String, SoftReference<CachedScript>> entry
: internalMap.entrySet()) {
+ if ( entry.getKey().startsWith(scriptPath) ) {
+ removal.add(entry.getKey());
}
- } finally {
- writeLock.unlock();
}
- };
- threadPool.execute(eventTask);
- }
- }
-
- protected Set<String> getCachedScripts() {
- readLock.lock();
- try {
- return internalMap.keySet();
- } finally {
- readLock.unlock();
- }
- }
-
- private void configureCache() {
- writeLock.lock();
- try {
- if (active) {
- this.clear();
- if (extensions.isEmpty()) {
- if (resourceChangeListener != null) {
- resourceChangeListener.unregister();
- resourceChangeListener = null;
- }
- } else {
- final List<String> globPatterns = new
ArrayList<>(extensions.size());
- for (final String extension : extensions) {
- globPatterns.add("glob:**/*.".concat(extension));
- }
- final String[] paths = globPatterns.toArray(new
String[globPatterns.size()]);
- if (resourceChangeListener != null) {
- final Dictionary<String, Object>
resourceChangeListenerProperties =
resourceChangeListener.getReference().getProperties();
- if ( !Arrays.equals(paths,
(String[])resourceChangeListenerProperties.get(ResourceChangeListener.PATHS))) {
-
resourceChangeListenerProperties.put(ResourceChangeListener.PATHS, paths);
-
resourceChangeListener.setProperties(resourceChangeListenerProperties);
- }
- } else {
- final Dictionary<String, Object>
resourceChangeListenerProperties = new Hashtable<>();
-
resourceChangeListenerProperties.put(ResourceChangeListener.PATHS, paths);
-
resourceChangeListenerProperties.put(ResourceChangeListener.CHANGES,
- new
String[]{ResourceChange.ChangeType.CHANGED.name(),
ResourceChange.ChangeType.REMOVED.name()});
- resourceChangeListener =
- bundleContext.registerService(
- ResourceChangeListener.class,
- this,
- resourceChangeListenerProperties
- );
- }
+ for(final String key : removal) {
+ internalMap.remove(key);
+ logger.debug("Detected removal for {} - removed entry {}
from the cache.", scriptPath, key);
+ result = true;
+ }
+ } else {
+ result = internalMap.remove(scriptPath) != null;
+ if (result) {
+ logger.debug("Removed script {} from script cache.",
scriptPath);
}
}
+ return result;
} finally {
writeLock.unlock();
}
}
- @Deactivate
- protected void deactivate() {
- this.active = false;
- writeLock.lock();
- try {
- internalMap.clear();
- if (resourceChangeListener != null) {
- resourceChangeListener.unregister();
- resourceChangeListener = null;
- }
- threadPool.shutdown();
- try {
- threadPool.awaitTermination(1, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- logger.warn("Unable to shutdown script cache thread in time");
- }
- } finally {
- writeLock.unlock();
- }
- }
-
- private void initializeExtensions() {
- for (final ScriptEngineFactory factory :
slingScriptEngineManager.getEngineFactories()) {
- final ScriptEngine scriptEngine = factory.getScriptEngine();
- if (scriptEngine instanceof Compilable) {
- extensions.addAll(factory.getExtensions());
- }
- }
- if (this.additionalExtensions != null) {
- extensions.addAll(Arrays.asList(this.additionalExtensions));
- }
- }
-
- @Override
- public void handleEvent(Event event) {
- writeLock.lock();
+ protected List<String> getCachedScripts() {
+ readLock.lock();
try {
- this.clear();
- this.extensions.clear();
- this.initializeExtensions();
- this.configureCache();
+ return new ArrayList<>(internalMap.keySet());
} finally {
- writeLock.unlock();
+ readLock.unlock();
}
}
}
diff --git
a/src/main/java/org/apache/sling/scripting/core/impl/ScriptCacheInvalidator.java
b/src/main/java/org/apache/sling/scripting/core/impl/ScriptCacheInvalidator.java
new file mode 100644
index 0000000..4961fbc
--- /dev/null
+++
b/src/main/java/org/apache/sling/scripting/core/impl/ScriptCacheInvalidator.java
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.core.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import javax.script.Compilable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+
+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.scripting.api.ScriptCache;
+import org.apache.sling.scripting.core.impl.jsr223.SlingScriptEngineManager;
+import org.jetbrains.annotations.NotNull;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventConstants;
+import org.osgi.service.event.EventHandler;
+import org.osgi.service.metatype.annotations.Designate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(
+ immediate = true, // event handler should be immediate
+ service = {EventHandler.class},
+ property = {
+ EventConstants.EVENT_TOPIC +
"=org/apache/sling/scripting/core/impl/jsr223/SlingScriptEngineManager/*"
+ }
+)
+@Designate(
+ ocd = ScriptCacheImplConfiguration.class
+)
+public class ScriptCacheInvalidator implements ResourceChangeListener,
ExternalResourceChangeListener, EventHandler {
+
+ private final Logger logger =
LoggerFactory.getLogger(ScriptCacheInvalidator.class);
+
+ private final BundleContext bundleContext;
+ private final Set<String> extensions = new TreeSet<>();
+ private final String[] additionalExtensions;
+
+ private volatile ServiceRegistration<ResourceChangeListener>
resourceChangeListener;
+
+ private final SlingScriptEngineManager slingScriptEngineManager;
+
+ private final ExecutorService threadPool;
+
+ private final ScriptCache scriptCache;
+
+ @Activate
+ public ScriptCacheInvalidator(@Reference final SlingScriptEngineManager
slingScriptEngineManager,
+ @Reference final ScriptCache scriptCache,
+ final ScriptCacheImplConfiguration configuration,
+ final BundleContext bundleCtx) {
+
+ this.slingScriptEngineManager = slingScriptEngineManager;
+ this.scriptCache = scriptCache;
+ this.threadPool = Executors.newSingleThreadExecutor();
+ this.bundleContext = bundleCtx;
+ this.additionalExtensions =
configuration.org_apache_sling_scripting_cache_additional__extensions();
+ this.handleEvent(null);
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ if (resourceChangeListener != null) {
+ resourceChangeListener.unregister();
+ resourceChangeListener = null;
+ }
+ threadPool.shutdown();
+ try {
+ threadPool.awaitTermination(1, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ logger.warn("Unable to shutdown script cache thread in time");
+ }
+ }
+
+ @Override
+ public void onChange(@NotNull List<ResourceChange> list) {
+ for (final ResourceChange change : list) {
+ Runnable eventTask = () -> {
+ final String path = change.getPath();
+ if (!this.scriptCache.removeScript(path)) {
+ this.scriptCache.removeScript(path.concat("/"));
+ }
+ };
+ threadPool.execute(eventTask);
+ }
+ }
+
+ private void configureListener() {
+ this.scriptCache.clear();
+ if (extensions.isEmpty()) {
+ if (resourceChangeListener != null) {
+ resourceChangeListener.unregister();
+ resourceChangeListener = null;
+ }
+ } else {
+ final List<String> globPatterns = new
ArrayList<>(extensions.size());
+ for (final String extension : extensions) {
+ globPatterns.add("glob:**/*.".concat(extension));
+ }
+ final String[] paths = globPatterns.toArray(new
String[globPatterns.size()]);
+ if (resourceChangeListener != null) {
+ final Dictionary<String, Object>
resourceChangeListenerProperties =
resourceChangeListener.getReference().getProperties();
+ if ( !Arrays.equals(paths,
(String[])resourceChangeListenerProperties.get(ResourceChangeListener.PATHS))) {
+
resourceChangeListenerProperties.put(ResourceChangeListener.PATHS, paths);
+
resourceChangeListener.setProperties(resourceChangeListenerProperties);
+ }
+ } else {
+ final Dictionary<String, Object>
resourceChangeListenerProperties = new Hashtable<>();
+
resourceChangeListenerProperties.put(ResourceChangeListener.PATHS, paths);
+
resourceChangeListenerProperties.put(ResourceChangeListener.CHANGES,
+ new String[]{ResourceChange.ChangeType.CHANGED.name(),
ResourceChange.ChangeType.REMOVED.name()});
+ resourceChangeListener =
+ bundleContext.registerService(
+ ResourceChangeListener.class,
+ this,
+ resourceChangeListenerProperties
+ );
+ }
+ }
+ }
+
+ private void initializeExtensions() {
+ this.extensions.clear();
+ for (final ScriptEngineFactory factory :
this.slingScriptEngineManager.getEngineFactories()) {
+ final ScriptEngine scriptEngine = factory.getScriptEngine();
+ if (scriptEngine instanceof Compilable) {
+ extensions.addAll(factory.getExtensions());
+ }
+ }
+ if (this.additionalExtensions != null) {
+ extensions.addAll(Arrays.asList(this.additionalExtensions));
+ }
+ }
+
+ @Override
+ public void handleEvent(final Event event) {
+ synchronized ( this.extensions ) {
+ this.initializeExtensions();
+ this.configureListener();
+ }
+ }
+}