Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[automation] implement a listener for ScriptEngineFactory changes #2459

Merged
merged 2 commits into from
Aug 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -62,8 +62,8 @@
* @author Jonathan Gilbert - added dependency tracking & per-script start levels
*/
@Component(immediate = true)
public class ScriptFileWatcher extends AbstractWatchService
implements ReadyService.ReadyTracker, DependencyTracker.DependencyChangeListener {
public class ScriptFileWatcher extends AbstractWatchService implements ReadyService.ReadyTracker,
DependencyTracker.DependencyChangeListener, ScriptEngineManager.FactoryChangeListener {

private static final String FILE_DIRECTORY = "automation" + File.separator + "jsr223";
private static final long RECHECK_INTERVAL = 20;
Expand All @@ -75,10 +75,10 @@ public class ScriptFileWatcher extends AbstractWatchService
private final ReadyService readyService;

private @Nullable ScheduledExecutorService scheduler;
private Supplier<ScheduledExecutorService> executerFactory;
private Supplier<ScheduledExecutorService> executorFactory;

private final Set<ScriptFileReference> pending = new HashSet<>();
private final Set<ScriptFileReference> loaded = new HashSet<>();
private final Set<ScriptFileReference> pending = ConcurrentHashMap.newKeySet();
private final Set<ScriptFileReference> loaded = ConcurrentHashMap.newKeySet();

private volatile int currentStartLevel = 0;

Expand All @@ -89,15 +89,17 @@ public ScriptFileWatcher(final @Reference ScriptEngineManager manager,
this.manager = manager;
this.dependencyTracker = dependencyTracker;
this.readyService = readyService;
this.executerFactory = () -> Executors
this.executorFactory = () -> Executors
.newSingleThreadScheduledExecutor(new NamedThreadFactory("scriptwatcher"));

manager.addFactoryChangeListener(this);
readyService.registerTracker(this, new ReadyMarkerFilter().withType(StartLevelService.STARTLEVEL_MARKER_TYPE));
}

@Deactivate
@Override
public void deactivate() {
manager.removeFactoryChangeListener(this);
readyService.unregisterTracker(this);

ScheduledExecutorService localScheduler = scheduler;
Expand All @@ -112,10 +114,10 @@ public void deactivate() {
/**
* Override the executor service. Can be used for testing.
*
* @param executerFactory supplier of ScheduledExecutorService
* @param executorFactory supplier of ScheduledExecutorService
*/
void setExecuterFactory(Supplier<ScheduledExecutorService> executerFactory) {
this.executerFactory = executerFactory;
void setExecutorFactory(Supplier<ScheduledExecutorService> executorFactory) {
this.executorFactory = executorFactory;
}

/**
Expand Down Expand Up @@ -261,7 +263,7 @@ private synchronized void onStartLevelChanged(int newLevel) {

if (previousLevel < StartLevelService.STARTLEVEL_MODEL) { // not yet started
if (newLevel >= StartLevelService.STARTLEVEL_MODEL) { // start
ScheduledExecutorService localScheduler = executerFactory.get();
ScheduledExecutorService localScheduler = executorFactory.get();
scheduler = localScheduler;
localScheduler.submit(() -> importResources(new File(pathToWatch)));
localScheduler.scheduleWithFixedDelay(() -> checkFiles(currentStartLevel), 0, RECHECK_INTERVAL,
Expand Down Expand Up @@ -309,4 +311,16 @@ public synchronized void onReadyMarkerRemoved(ReadyMarker readyMarker) {
onStartLevelChanged(newLevel);
}
}

@Override
public void factoryAdded(@Nullable String scriptType) {
}

@Override
public void factoryRemoved(@Nullable String scriptType) {
if (scriptType == null) {
return;
}
loaded.stream().filter(ref -> scriptType.equals(ref.getScriptType().get())).forEach(this::importFileWhenReady);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ public void testLoadOneDefaultFileDelayedSupport() {
ScheduledExecutorService scheduledExecutorService = spy(
new DelegatingScheduledExecutorService(Executors.newSingleThreadScheduledExecutor()));
ArgumentCaptor<Runnable> scheduledTask = ArgumentCaptor.forClass(Runnable.class);
scriptFileWatcher.setExecuterFactory(() -> scheduledExecutorService);
scriptFileWatcher.setExecutorFactory(() -> scheduledExecutorService);

when(scriptEngineManager.isSupported("js")).thenReturn(false);
ScriptEngineContainer scriptEngineContainer = mock(ScriptEngineContainer.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,35 @@ void loadScript(String engineIdentifier, InputStreamReader scriptData,
* @return true, if supported, else false
*/
boolean isSupported(String scriptType);

/**
* Add a listener that is notified when a ScriptEngineFactory is added or removed
*
* @param listener an object that implements {@link FactoryChangeListener}
*/
void addFactoryChangeListener(FactoryChangeListener listener);

/**
* Remove a listener that is notified when a ScriptEngineFactory is added or removed
*
* @param listener an object that implements {@link FactoryChangeListener}
*/
void removeFactoryChangeListener(FactoryChangeListener listener);

interface FactoryChangeListener {

/**
* Called by the {@link ScriptEngineManager} when a ScriptEngineFactory is added
*
* @param scriptType the script type supported by the newly added factory
*/
void factoryAdded(String scriptType);

/**
* Called by the {@link ScriptEngineManager} when a ScriptEngineFactory is removed
*
* @param scriptType the script type supported by the removed factory
*/
void factoryRemoved(String scriptType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@

import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.script.Invocable;
import javax.script.ScriptContext;
Expand Down Expand Up @@ -54,6 +56,7 @@ public class ScriptEngineManagerImpl implements ScriptEngineManager {
private final Map<String, ScriptEngineFactory> customSupport = new HashMap<>();
private final Map<String, ScriptEngineFactory> genericSupport = new HashMap<>();
private final ScriptExtensionManager scriptExtensionManager;
private final Set<FactoryChangeListener> listeners = new HashSet<>();

@Activate
public ScriptEngineManagerImpl(final @Reference ScriptExtensionManager scriptExtensionManager) {
Expand All @@ -70,6 +73,7 @@ public void addScriptEngineFactory(ScriptEngineFactory engineFactory) {
} else {
this.genericSupport.put(scriptType, engineFactory);
}
listeners.forEach(listener -> listener.factoryAdded(scriptType));
}
if (logger.isDebugEnabled()) {
if (!scriptTypes.isEmpty()) {
Expand Down Expand Up @@ -99,6 +103,7 @@ public void removeScriptEngineFactory(ScriptEngineFactory engineFactory) {
} else {
this.genericSupport.remove(scriptType, engineFactory);
}
listeners.forEach(listener -> listener.factoryRemoved(scriptType));
}
logger.debug("Removed {}", engineFactory.getClass().getSimpleName());
}
Expand Down Expand Up @@ -248,4 +253,14 @@ private void addAttributeToScriptContext(ScriptEngine engine, String name, Objec

scriptContext.setAttribute(name, value, ScriptContext.ENGINE_SCOPE);
}

@Override
public void addFactoryChangeListener(FactoryChangeListener listener) {
listeners.add(listener);
}

@Override
public void removeFactoryChangeListener(FactoryChangeListener listener) {
listeners.remove(listener);
}
}