Skip to content

Commit

Permalink
Add WatchService
Browse files Browse the repository at this point in the history
Signed-off-by: Jan N. Klug <github@klug.nrw>
  • Loading branch information
J-N-K committed Jan 14, 2023
1 parent ecbb854 commit 389888b
Show file tree
Hide file tree
Showing 52 changed files with 1,322 additions and 1,486 deletions.
19 changes: 19 additions & 0 deletions bom/compile/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,25 @@
<scope>compile</scope>
</dependency>

<!-- dirwatcher -->
<dependency>
<groupId>org.openhab.osgiify</groupId>
<artifactId>io.methvin.directory-watcher</artifactId>
<version>0.17.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.12.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.12.1</version>
<scope>compile</scope>
</dependency>
</dependencies>

</project>
20 changes: 20 additions & 0 deletions bom/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,26 @@
<version>3.27.0-GA</version>
<scope>compile</scope>
</dependency>

<!-- dirwatcher -->
<dependency>
<groupId>org.openhab.osgiify</groupId>
<artifactId>io.methvin.directory-watcher</artifactId>
<version>0.17.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.12.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.12.1</version>
<scope>compile</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.openhab.core.automation.module.script.rulesupport.loader.AbstractScriptFileWatcher;
import org.openhab.core.service.ReadyService;
import org.openhab.core.service.StartLevelService;
import org.openhab.core.service.WatchService;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
Expand All @@ -37,19 +38,18 @@ public class DefaultScriptFileWatcher extends AbstractScriptFileWatcher {
private static final String FILE_DIRECTORY = "automation" + File.separator + "jsr223";

@Activate
public DefaultScriptFileWatcher(final @Reference ScriptEngineManager manager,
public DefaultScriptFileWatcher(final @Reference(target = WatchService.CONFIG_WATCHER_FILTER) WatchService watchService
, final @Reference ScriptEngineManager manager,
final @Reference ReadyService readyService, final @Reference StartLevelService startLevelService) {
super(manager, readyService, startLevelService, FILE_DIRECTORY);
super(watchService, manager, readyService, startLevelService, FILE_DIRECTORY, true);
}

@Activate
@Override
public void activate() {
super.activate();
}

@Deactivate
@Override
public void deactivate() {
super.deactivate();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,21 @@
*/
package org.openhab.core.automation.module.script.rulesupport.loader;

import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import static org.openhab.core.service.WatchService.Kind.CREATE;
import static org.openhab.core.service.WatchService.Kind.DELETE;
import static org.openhab.core.service.WatchService.Kind.MODIFY;

import java.io.File;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.module.script.ScriptDependencyTracker;
import org.openhab.core.automation.module.script.rulesupport.internal.loader.BidiSetBag;
import org.openhab.core.service.AbstractWatchService;
import org.openhab.core.service.WatchService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -41,54 +39,35 @@
* @author Jan N. Klug - Refactored to OSGi service
*/
@NonNullByDefault
public abstract class AbstractScriptDependencyTracker implements ScriptDependencyTracker {
public abstract class AbstractScriptDependencyTracker
implements ScriptDependencyTracker, WatchService.WatchEventListener {
private final Logger logger = LoggerFactory.getLogger(AbstractScriptDependencyTracker.class);

protected final String libraryPath;
protected final Path libraryPath;

private final Set<ScriptDependencyTracker.Listener> dependencyChangeListeners = ConcurrentHashMap.newKeySet();

private final BidiSetBag<String, String> scriptToLibs = new BidiSetBag<>();
private @Nullable AbstractWatchService dependencyWatchService;
private final WatchService watchService;

public AbstractScriptDependencyTracker(final String libraryPath) {
this.libraryPath = libraryPath;
}
public AbstractScriptDependencyTracker(WatchService watchService, final String libraryPath) {
this.libraryPath = Path.of(libraryPath);
this.watchService = watchService;

public void activate() {
AbstractWatchService dependencyWatchService = createDependencyWatchService();
dependencyWatchService.activate();
this.dependencyWatchService = dependencyWatchService;
watchService.registerListener(this, this.libraryPath);
}

public void deactivate() {
AbstractWatchService dependencyWatchService = this.dependencyWatchService;
if (dependencyWatchService != null) {
dependencyWatchService.deactivate();
}
watchService.unregisterListener(this);
}

protected AbstractWatchService createDependencyWatchService() {
return new AbstractWatchService(libraryPath) {
@Override
protected boolean watchSubDirectories() {
return true;
}

@Override
protected WatchEvent.Kind<?> @Nullable [] getWatchEventKinds(Path path) {
return new WatchEvent.Kind<?>[] { ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY };
}

@Override
protected void processWatchEvent(WatchEvent<?> watchEvent, WatchEvent.Kind<?> kind, Path path) {
File file = path.toFile();
if (!file.isHidden() && (kind.equals(ENTRY_DELETE)
|| (file.canRead() && (kind.equals(ENTRY_CREATE) || kind.equals(ENTRY_MODIFY))))) {
dependencyChanged(file.getPath());
}
}
};
@Override
public void processWatchEvent(WatchService.Kind kind, Path path) {
File file = path.toFile();
if (!file.isHidden()
&& (kind.equals(DELETE) || (file.canRead() && (kind.equals(CREATE) || kind.equals(MODIFY))))) {
dependencyChanged(file.getPath());
}
}

protected void dependencyChanged(String dependency) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@
*/
package org.openhab.core.automation.module.script.rulesupport.loader;

import static java.nio.file.StandardWatchEventKinds.*;
import static org.openhab.core.service.WatchService.Kind.CREATE;
import static org.openhab.core.service.WatchService.Kind.DELETE;
import static org.openhab.core.service.WatchService.Kind.MODIFY;

import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.util.Collection;
import java.util.List;
import java.util.Map;
Expand All @@ -48,11 +48,11 @@
import org.openhab.core.automation.module.script.ScriptEngineManager;
import org.openhab.core.automation.module.script.rulesupport.internal.loader.ScriptFileReference;
import org.openhab.core.common.NamedThreadFactory;
import org.openhab.core.service.AbstractWatchService;
import org.openhab.core.service.ReadyMarker;
import org.openhab.core.service.ReadyMarkerFilter;
import org.openhab.core.service.ReadyService;
import org.openhab.core.service.StartLevelService;
import org.openhab.core.service.WatchService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -67,7 +67,7 @@
* @author Jan N. Klug - Refactored dependency tracking to script engine factories
*/
@NonNullByDefault
public abstract class AbstractScriptFileWatcher extends AbstractWatchService implements ReadyService.ReadyTracker,
public class AbstractScriptFileWatcher implements WatchService.WatchEventListener, ReadyService.ReadyTracker,
ScriptDependencyTracker.Listener, ScriptEngineManager.FactoryChangeListener, ScriptFileWatcher {

private static final Set<String> EXCLUDED_FILE_EXTENSIONS = Set.of("txt", "old", "example", "backup", "md", "swp",
Expand All @@ -82,20 +82,25 @@ public abstract class AbstractScriptFileWatcher extends AbstractWatchService imp

private final ScriptEngineManager manager;
private final ReadyService readyService;
private final WatchService watchService;
private final Path watchPath;
private final boolean watchSubDirectories;

protected ScheduledExecutorService scheduler;

private final Map<String, ScriptFileReference> scriptMap = new ConcurrentHashMap<>();
private final Map<String, Lock> scriptLockMap = new ConcurrentHashMap<>();
private final CompletableFuture<@Nullable Void> initialized = new CompletableFuture<>();

private volatile int currentStartLevel = 0;
private volatile int currentStartLevel;

public AbstractScriptFileWatcher(final ScriptEngineManager manager, final ReadyService readyService,
final StartLevelService startLevelService, final String fileDirectory) {
super(OpenHAB.getConfigFolder() + File.separator + fileDirectory);
public AbstractScriptFileWatcher(final WatchService watchService, final ScriptEngineManager manager, final ReadyService readyService,
final StartLevelService startLevelService, final String fileDirectory, boolean watchSubDirectories) {
this.watchService = watchService;
this.manager = manager;
this.readyService = readyService;
this.watchSubDirectories = watchSubDirectories;
this.watchPath = Path.of(OpenHAB.getConfigFolder()).resolve(fileDirectory);

manager.addFactoryChangeListener(this);
readyService.registerTracker(this, new ReadyMarkerFilter().withType(StartLevelService.STARTLEVEL_MARKER_TYPE));
Expand All @@ -108,13 +113,6 @@ public AbstractScriptFileWatcher(final ScriptEngineManager manager, final ReadyS
}
}

@Override
public void activate() {
// TODO: needed to initialize underlying AbstractWatchService, should be removed when we switch to PR
// openhab-core#3004
super.activate();
}

/**
* Can be overridden by subclasses (e.g. for testing)
*
Expand All @@ -124,14 +122,26 @@ protected ScheduledExecutorService getScheduler() {
return Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("scriptwatcher"));
}

@Override
public void activate() {
if (!Files.exists(watchPath)) {
try {
Files.createDirectories(watchPath);
} catch (IOException e) {
logger.warn("Failed to create watched directory: {}", watchPath);
}
} else if (!Files.isDirectory(watchPath)) {
logger.warn("Trying to watch directory {}, however it is a file", watchPath);
}
watchService.registerListener(this, watchPath, watchSubDirectories);
}

public void deactivate() {
watchService.unregisterListener(this);
manager.removeFactoryChangeListener(this);
readyService.unregisterTracker(this);
super.deactivate();

CompletableFuture.allOf(
Set.copyOf(scriptMap.keySet()).stream().map(this::removeFile).toArray(CompletableFuture<?>[]::new))
Set.copyOf(scriptMap.keySet()).stream().map(this::removeFile).toArray(CompletableFuture<?>[]::new))
.thenRun(scheduler::shutdownNow);
}

Expand Down Expand Up @@ -200,22 +210,12 @@ private List<Path> listFiles(Path path, boolean includeSubDirectory) {
}

@Override
protected boolean watchSubDirectories() {
return true;
}

@Override
protected Kind<?> @Nullable [] getWatchEventKinds(@Nullable Path subDir) {
return new Kind<?>[] { ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY };
}

@Override
protected void processWatchEvent(@Nullable WatchEvent<?> event, @Nullable Kind<?> kind, @Nullable Path path) {
public void processWatchEvent(WatchService.Kind kind, Path path) {
File file = path.toFile();
if (!file.isHidden()) {
if (ENTRY_DELETE.equals(kind)) {
if (DELETE.equals(kind)) {
if (file.isDirectory()) {
if (watchSubDirectories()) {
if (watchSubDirectories) {
synchronized (this) {
String prefix = path.getParent().toString();
Set<String> toRemove = scriptMap.keySet().stream().filter(ref -> ref.startsWith(prefix))
Expand All @@ -228,8 +228,8 @@ protected void processWatchEvent(@Nullable WatchEvent<?> event, @Nullable Kind<?
}
}

if (file.canRead() && (ENTRY_CREATE.equals(kind) || ENTRY_MODIFY.equals(kind))) {
addFiles(listFiles(file.toPath(), watchSubDirectories()));
if (file.canRead() && (CREATE.equals(kind) || MODIFY.equals(kind))) {
addFiles(listFiles(file.toPath(), watchSubDirectories));
}
}
}
Expand Down Expand Up @@ -341,17 +341,17 @@ private boolean createAndLoad(ScriptFileReference ref) {
}

private void initialImport() {
File directory = new File(pathToWatch);
File directory = watchPath.toFile();

if (!directory.exists()) {
if (!directory.mkdirs()) {
logger.warn("Failed to create watched directory: {}", pathToWatch);
logger.warn("Failed to create watched directory: {}", watchPath);
}
} else if (directory.isFile()) {
logger.warn("Trying to watch directory {}, however it is a file", pathToWatch);
logger.warn("Trying to watch directory {}, however it is a file", watchPath);
}

addFiles(listFiles(directory.toPath(), watchSubDirectories())).thenRun(() -> initialized.complete(null));
addFiles(listFiles(directory.toPath(), watchSubDirectories)).thenRun(() -> initialized.complete(null));
}

@Override
Expand Down Expand Up @@ -413,4 +413,4 @@ public void factoryRemoved(@Nullable String scriptType) {
.map(ScriptFileReference::getScriptIdentifier).collect(Collectors.toSet());
toRemove.forEach(this::removeFile);
}
}
}
Loading

0 comments on commit 389888b

Please sign in to comment.