diff --git a/bundles/core/org.eclipse.smarthome.core.test/src/test/groovy/org/eclipse/smarthome/core/service/AbstractWatchServiceTest.groovy b/bundles/core/org.eclipse.smarthome.core.test/src/test/groovy/org/eclipse/smarthome/core/service/AbstractWatchServiceTest.groovy index 86462d7e100..00de926274f 100644 --- a/bundles/core/org.eclipse.smarthome.core.test/src/test/groovy/org/eclipse/smarthome/core/service/AbstractWatchServiceTest.groovy +++ b/bundles/core/org.eclipse.smarthome.core.test/src/test/groovy/org/eclipse/smarthome/core/service/AbstractWatchServiceTest.groovy @@ -1,34 +1,26 @@ package org.eclipse.smarthome.core.service +import static java.nio.file.StandardWatchEventKinds.* import static org.hamcrest.CoreMatchers.* import static org.junit.Assert.* import static org.junit.matchers.JUnitMatchers.* -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 java.io.IOException; import java.nio.file.Path -import java.nio.file.WatchEvent; +import java.nio.file.WatchEvent import java.nio.file.WatchKey import java.nio.file.WatchService import java.nio.file.WatchEvent.Kind -import java.util.Map -import org.apache.commons.lang.SystemUtils; -import org.eclipse.smarthome.core.service.AbstractWatchServiceTest.FullEvent; +import org.apache.commons.lang.SystemUtils import org.eclipse.smarthome.test.OSGiTest import org.junit.After -import org.junit.AfterClass; -import org.junit.Before; +import org.junit.AfterClass import org.junit.BeforeClass -import org.junit.Ignore; -import org.junit.Test; +import org.junit.Test /** * Test for {@link AbstractWatchService}. - * + * * @author Dimitar Ivanov * */ @@ -116,7 +108,8 @@ class AbstractWatchServiceTest extends OSGiTest { isCreated = file.createNewFile() assertThat "The file '$file.absolutePath' was not created successfully", isCreated, is(true) - waitForAssert({assertThat "Exactly two watch events were expected within $NO_EVENT_TIMEOUT_IN_SECONDS seconds, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(2)},NO_EVENT_TIMEOUT_IN_SECONDS*1000,1000) + def expectedEvents = 2 + waitForAssert({assertThat "Exactly two watch events were expected within $NO_EVENT_TIMEOUT_IN_SECONDS seconds, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(expectedEvents)},NO_EVENT_TIMEOUT_IN_SECONDS*1000,1000) FullEvent innerFileEvent = watchService.allFullEvents[0] assertThat "The inner file '$innerfile.absolutePath' creation was not detected. All events detected: " + watchService.allFullEvents,innerFileEvent.eventKind,is(ENTRY_CREATE) @@ -128,7 +121,109 @@ class AbstractWatchServiceTest extends OSGiTest { } @Test - void 'directory changes are watched correctly'(){ + void 'subirs are registered and modifications are watched, folder is created after service activation'(){ + // Watch subdirectories and their modifications + // RelativeWatchService#processWatchEvent() provides implementation for watching folders after service activation + watchService = new RelativeWatchService(WATCHED_DIRECTORY,true,true) + + watchService.activate() + def subDirName = "subDir" + def fileName = "innerFile" + def innerFileName = subDirName + File.separatorChar + fileName + File innerFile = new File(WATCHED_DIRECTORY + File.separatorChar + innerFileName) + + // Make subdirectories + innerFile.getParentFile().mkdirs() + + assertDirectoryCraeteEventIsProcessed(subDirName) + + watchService.allFullEvents.clear(); + + boolean isCreated = innerFile.createNewFile() + assertThat "The file '$innerFile.absolutePath' was not created successfully", isCreated, is(true) + + assertAllEventsAreProcessed(subDirName,innerFile, innerFileName) + } + + @Test + void 'subdirs are not registered and modifications are watched, folder is created after service activation'(){ + // Do not watch subdirectories, but watch their modifications + // RelativeWatchService#processWatchEvent() provides implementation for watching folders after service activation + watchService = new RelativeWatchService(WATCHED_DIRECTORY,false,true) + watchService.activate() + + def subDirName = "subDir" + def fileName = "innerFile" + def innerFileName = subDirName + File.separatorChar + fileName + + File innerFile = new File(WATCHED_DIRECTORY + File.separatorChar + innerFileName) + + // Make subdirectories + innerFile.getParentFile().mkdirs() + + assertDirectoryCraeteEventIsProcessed(subDirName) + + watchService.allFullEvents.clear(); + + boolean isCreated = innerFile.createNewFile() + assertThat "The file '$innerFile.absolutePath' was not created successfully", isCreated, is(true) + + assertDirectoryModifyEventIsProcessed(subDirName) + } + + @Test + void 'subdirs are not registered and modifications are not watched, folder is created after service activation' () { + // Do not watch subdirectories and do not watch their modifications + // RelativeWatchService#processWatchEvent() provides implementation for watching folders after service activation + watchService = new RelativeWatchService(WATCHED_DIRECTORY,false,false) + watchService.activate() + + def subDirName = "subDir" + def fileName = "innerFile" + def innerFileName = subDirName + File.separatorChar + fileName + + File innerFile = new File(WATCHED_DIRECTORY + File.separatorChar + innerFileName) + + // Make subdirectories + innerFile.getParentFile().mkdirs() + + // Consequent creation and deletion in order to generate any watch events for the subdirectory + boolean isCreated = innerFile.createNewFile() + assertThat "The file '$innerFile.absolutePath' was not created successfully", isCreated, is(true) + + boolean isDeleted = innerFile.delete() + assertThat "Inner file is not deleted", isDeleted, is(true) + + assertNoEventsAreProcessed() + } + + @Test + void 'subdirs are registered, but dirs modifications are not watched, folder is created after service activation'() { + // Watch subdirectories, but not their modifications + // RelativeWatchService#processWatchEvent() provides implementation for watching folders after service activation + watchService = new RelativeWatchService(WATCHED_DIRECTORY,true,false) + + watchService.activate() + + def subDirName = "subDir" + def fileName = "innerFile" + def innerFileName = subDirName + File.separatorChar + fileName + + File innerFile = new File(WATCHED_DIRECTORY + File.separatorChar + innerFileName) + + // Make subdirectories + innerFile.getParentFile().mkdirs() + + //Create file in the subdirectory + boolean isCreated = innerFile.createNewFile() + assertThat "The file '$innerFile.absolutePath' was not created successfully", isCreated, is(true) + + //The subdirectory is created after the service initialization. As no modifications are watched the new subdir can not be registered + assertNoEventsAreProcessed() + } + + @Test + void 'subdirs are registered and modifications watched'(){ // Watch subdirectories and their modifications watchService = new RelativeWatchService(WATCHED_DIRECTORY,true,true) @@ -146,23 +241,11 @@ class AbstractWatchServiceTest extends OSGiTest { boolean isCreated = innerFile.createNewFile() assertThat "The file '$innerFile.absolutePath' was not created successfully", isCreated, is(true) - //This could vary across different platforms. For more information see "Platform dependencies" section in the WatchService documentation - def expectedEvents = SystemUtils.IS_OS_WINDOWS ? 2 : 1 - waitForAssert({assertThat "Exactly $expectedEvents watch events were expected within $NO_EVENT_TIMEOUT_IN_SECONDS seconds, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(expectedEvents)},NO_EVENT_TIMEOUT_IN_SECONDS*1000,1000) - - FullEvent fileEvent = watchService.allFullEvents[0] - assertThat "File '$innerFile.absolutePath' creation was not detected. All events detected: " + watchService.allFullEvents, fileEvent.eventKind, is(ENTRY_CREATE) - assertThat "File '$innerFile.absolutePath' name expected in the modified event. All events detected: " + watchService.allFullEvents, fileEvent.eventPath.toString(), is(innerFileName) - - if(SystemUtils.IS_OS_WINDOWS){ - FullEvent dirEvent = watchService.allFullEvents[1] - assertThat "Directory $subDirName modification was not detected. All events detected: " + watchService.allFullEvents, dirEvent.eventKind, is(ENTRY_MODIFY) - assertThat "Subdirectory was not found in the modified event", dirEvent.eventPath.toString(), is(subDirName) - } + assertAllEventsAreProcessed(subDirName,innerFile,innerFileName) } @Test - void 'subdirs are not registered and not watched'(){ + void 'subdirs are not registered and modifications are not watched'(){ // Do not watch the subdirectories of the root directory watchService = new RelativeWatchService(WATCHED_DIRECTORY,false,false) @@ -180,14 +263,33 @@ class AbstractWatchServiceTest extends OSGiTest { boolean isDeleted = innerFile.delete() assertThat "Inner file is not deleted", isDeleted, is(true) - // Wait for a possible event for the maximum timeout - sleep NO_EVENT_TIMEOUT_IN_SECONDS*1000 + assertNoEventsAreProcessed() + } - assertThat "No watch events are expected, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(0) + @Test + void 'subdirs are registered, but dirs modifications are not watched'() { + // Do watch the subdirectories of the root directory, but do not watch directory modifications + watchService = new RelativeWatchService(WATCHED_DIRECTORY,true,false) + + def innerFileName = "watchRequestSubDir"+ File.separatorChar + "watchRequestInnerFile" + File innerFile = new File(WATCHED_DIRECTORY + File.separatorChar + innerFileName) + // Make all the subdirectories before running the service + innerFile.getParentFile().mkdirs() + + watchService.activate() + + boolean isCreated = innerFile.createNewFile() + assertThat "The file '$innerFile.absolutePath' was not created successfully", isCreated, is(true) + + assertFileCreateEventIsProcessed(innerFile,innerFileName) + + watchService.allFullEvents.clear(); + + assertNoEventsAreProcessed() } @Test - void 'subdirs are not watched, but dirs modifications are watched'(){ + void 'subdirs are not registered, but dirs modifications are watched'(){ // Do not watch the subdirectories of the root directory, but watch directory modifications watchService = new RelativeWatchService(WATCHED_DIRECTORY,false,true) @@ -205,15 +307,7 @@ class AbstractWatchServiceTest extends OSGiTest { // Wait for possible watch event for the maximum timeout sleep NO_EVENT_TIMEOUT_IN_SECONDS*1000 - if(SystemUtils.IS_OS_WINDOWS){ - // This behavior could vary depending on the Platform (see the WatchService documentation) - assertThat "Exactly one event is expected, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(1) - FullEvent event = watchService.allFullEvents[0] - assertThat "Modification event for subdir '$subDirName' on file creation within it is expected", event.eventKind , is(ENTRY_MODIFY) - assertThat "The watch event does not contain the path of the modified directory", event.eventPath.toString() , is(subDirName) - }else { - assertThat "No events are expected if not Windows OS" + watchService.allFullEvents, watchService.allFullEvents.size(), is(0) - } + assertDirectoryModifyEventIsProcessed(subDirName) // Clear the asserted event watchService.allFullEvents.clear() @@ -221,10 +315,61 @@ class AbstractWatchServiceTest extends OSGiTest { boolean isDeleted = innerFile.delete() assertThat "Inner file '$innerFile.absolutePath' is not deleted", isDeleted, is(true) + assertNoEventsAreProcessed() + } + void assertNoEventsAreProcessed(){ // Wait for a possible event for the maximum timeout sleep NO_EVENT_TIMEOUT_IN_SECONDS*1000 - assertThat "No event is expected, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(0) + assertThat "No watch events are expected, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(0) + } + + void assertAllEventsAreProcessed(def subDirName,def innerFile, def innerFileName){ + //This could vary across different platforms. For more information see "Platform dependencies" section in the WatchService documentation + def expectedEvents = SystemUtils.IS_OS_WINDOWS ? 2 : 1 + waitForAssert({assertThat "Exactly $expectedEvents watch events were expected within $NO_EVENT_TIMEOUT_IN_SECONDS seconds, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(expectedEvents)},NO_EVENT_TIMEOUT_IN_SECONDS*1000,1000) + + FullEvent fileEvent = watchService.allFullEvents[0] + assertThat "File '$innerFile.absolutePath' creation was not detected. All events detected: " + watchService.allFullEvents, fileEvent.eventKind, is(ENTRY_CREATE) + assertThat "File '$innerFile.absolutePath' name expected in the modified event. All events detected: " + watchService.allFullEvents, fileEvent.eventPath.toString(), is(innerFileName) + + if(SystemUtils.IS_OS_WINDOWS){ + FullEvent dirEvent = watchService.allFullEvents[1] + assertThat "Directory $subDirName modification was not detected. All events detected: " + watchService.allFullEvents, dirEvent.eventKind, is(ENTRY_MODIFY) + assertThat "Subdirectory was not found in the modified event", dirEvent.eventPath.toString(), is(subDirName) + } + } + + void assertDirectoryCraeteEventIsProcessed(def subDirName) { + //Single event for directory creation is present + def expectedEvents = 1 + waitForAssert({assertThat "Exactly $expectedEvents watch events were expected within $NO_EVENT_TIMEOUT_IN_SECONDS seconds, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(expectedEvents)},NO_EVENT_TIMEOUT_IN_SECONDS*1000,1000) + FullEvent event = watchService.allFullEvents[0] + assertThat "Directory $subDirName craetion was not detected. All events detected: " + watchService.allFullEvents, event.eventKind, is(ENTRY_CREATE) + assertThat "Subdirectory was not found in the creation event", event.eventPath.toString(), is(subDirName) + } + + void assertFileCreateEventIsProcessed(def innerFile, def innerFileName) { + //Single event for file creation is present + def expectedEvents = 1 + waitForAssert({assertThat "Exactly $expectedEvents watch events were expected within $NO_EVENT_TIMEOUT_IN_SECONDS seconds, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(expectedEvents)},NO_EVENT_TIMEOUT_IN_SECONDS*1000,1000) + FullEvent fileEvent = watchService.allFullEvents[0] + assertThat "File '$innerFile.absolutePath' creation was not detected. All events detected: " + watchService.allFullEvents, fileEvent.eventKind, is(ENTRY_CREATE) + assertThat "File '$innerFile.absolutePath' name expected in the modified event. All events detected: " + watchService.allFullEvents, fileEvent.eventPath.toString(), is(innerFileName) + } + + void assertDirectoryModifyEventIsProcessed(def subDirName) { + //Create file is not detected, only the modification event is detected + def expectedEvents = SystemUtils.IS_OS_WINDOWS ? 1 : 0 + waitForAssert({assertThat "Exactly $expectedEvents watch events were expected within $NO_EVENT_TIMEOUT_IN_SECONDS seconds, but were: " + watchService.allFullEvents, watchService.allFullEvents.size(), is(expectedEvents)},NO_EVENT_TIMEOUT_IN_SECONDS*1000,1000) + + if(SystemUtils.IS_OS_WINDOWS){ + FullEvent fileEvent = watchService.allFullEvents[0] + assertThat "Directory $subDirName modification was not detected. All events detected: " + watchService.allFullEvents, fileEvent.eventKind, is(ENTRY_MODIFY) + assertThat "Subdirectory was not found in the modified event", fileEvent.eventPath.toString(), is(subDirName) + } else { + assertThat "No events are expected if not Windows OS" + watchService.allFullEvents, watchService.allFullEvents.size(), is(0) + } } void assertByRelativePath(String fileName) { @@ -254,8 +399,8 @@ class AbstractWatchServiceTest extends OSGiTest { void fullEventAssertionsByKind(String fileName, kind, osSpecific){ waitForAssert({ assertThat "At least one watch event is expected within $NO_EVENT_TIMEOUT_IN_SECONDS seconds.", - watchService.allFullEvents.size() >= 1, - is(true)},NO_EVENT_TIMEOUT_IN_SECONDS*1000,1000); + watchService.allFullEvents.size() >= 1, + is(true)},NO_EVENT_TIMEOUT_IN_SECONDS*1000,1000); if(osSpecific && kind.equals(ENTRY_DELETE)){ // There is possibility that one more modify event is triggered on some OS @@ -311,16 +456,23 @@ class AbstractWatchServiceTest extends OSGiTest { @Override - protected AbstractWatchQueueReader buildWatchQueueReader(WatchService watchService, Path toWatch,Map registeredKeys) { - def queueReader = new AbstractWatchQueueReader(watchService, toWatch,registeredKeys) { - @Override - protected void processWatchEvent(WatchEvent event, Kind kind, Path path) { - FullEvent fullEvent = new FullEvent(event,kind,path) - allFullEvents << fullEvent + protected AbstractWatchQueueReader buildWatchQueueReader(WatchService watchServiceImpl, Path toWatch,Map registeredKeys) { + def queueReader = new AbstractWatchQueueReader(watchServiceImpl, toWatch,registeredKeys) { + @Override + protected void processWatchEvent(WatchEvent event, Kind kind, Path path) { + FullEvent fullEvent = new FullEvent(event,kind,path) + if (kind.equals(ENTRY_CREATE)) { + Path relativePath = baseWatchedDir.resolve(path); + if (relativePath.toFile().isDirectory() && watchSubDirectories()) { + registerAllDirectoriesInternal(relativePath, registeredKeys); } - }; + } + allFullEvents << fullEvent + } + }; queueReader.setWatchingDirectoryChanges(watchDirectoryChanges) return queueReader + } @Override diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/service/AbstractWatchQueueReader.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/service/AbstractWatchQueueReader.java index ff970cbcded..e7857e5be54 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/service/AbstractWatchQueueReader.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/service/AbstractWatchQueueReader.java @@ -9,125 +9,6 @@ import static java.nio.file.StandardWatchEventKinds.OVERFLOW; -import java.nio.file.ClosedWatchServiceException; -import java.nio.file.Path; -import java.nio.file.WatchEvent; -import java.nio.file.WatchKey; -import java.nio.file.WatchService; -import java.text.MessageFormat; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Base class for watch queue readers - * - * @author Fabio Marini - * - */ -public abstract class AbstractWatchQueueReader implements Runnable { - - /** - * Default logger for ESH Watch Services - */ - protected final Logger logger = LoggerFactory.getLogger(AbstractWatchQueueReader.class); - - protected WatchService watchService; - - protected Path dir; - - /** - * Perform a simple cast of given event to WatchEvent - * - * @param event - * the event to cast - * @return the casted event - */ - @SuppressWarnings("unchecked") - static WatchEvent cast(WatchEvent event) { - return (WatchEvent) event; - } - - /** - * Build the object with the given parameters - * - * @param watchService - * the watch service - * @param dir - */ - public AbstractWatchQueueReader(WatchService watchService, Path dir) { - this.watchService = watchService; - this.dir = dir; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.smarthome.core.service.IWatchService#activate() - */ - @Override - public void run() { - try { - for (;;) { - WatchKey key = null; - try { - key = watchService.take(); - } catch (InterruptedException exc) { - logger.warn(MessageFormat.format("Catched InterruptedException : {0}", exc.getLocalizedMessage())); - - return; - } - - for (WatchEvent event : key.pollEvents()) { - WatchEvent.Kind kind = event.kind(); - - if (kind == OVERFLOW) { - logger.warn(MessageFormat.format("Found event with overflow kind : {0}", event)); - - continue; - } - - // Context for directory entry event is the file name of - // entry - WatchEvent ev = cast(event); - Path path = ev.context(); - - processWatchEvent(event, kind, path); - } - - key.reset(); - } - } catch (ClosedWatchServiceException ecx) { - logger.debug("ClosedWatchServiceException catched! {}. \n{} Stopping ", ecx.getLocalizedMessage(), Thread - .currentThread().getName()); - - return; - } - } - - /** - * Processes the given watch event - * - * @param event - * the watch event to perform - * @param kind - * the event's kind - * @param name - * the path of event - */ - protected abstract void processWatchEvent(WatchEvent event, WatchEvent.Kind kind, Path path); -======= -/** - * Copyright (c) 2014-2015 openHAB UG (haftungsbeschraenkt) and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.eclipse.smarthome.core.service; - -import static java.nio.file.StandardWatchEventKinds.OVERFLOW; - import java.nio.file.ClosedWatchServiceException; import java.nio.file.Path; import java.nio.file.WatchEvent; @@ -289,7 +170,8 @@ private Path resolveToRelativePath(WatchKey key, WatchEvent event) { /** * Processes the given watch event. Note that the kind and the number of the events for the watched directory is a * platform dependent (see the "Platform dependencies" sections of {@link WatchService}). - * + *

+ * If {@link #isWatchingDirectoryChanges()} is true, all events will be send to this method, otherwise directory events will be skipped * @param event the watch event to be handled * @param kind the event's kind * @param path the path of the event (relative to the {@link #baseWatchedDir} @@ -318,5 +200,4 @@ public boolean isWatchingDirectoryChanges() { public final void setWatchingDirectoryChanges(boolean watchDirectoryChanges) { this.watchingDirectoryChanges = watchDirectoryChanges; } - } \ No newline at end of file diff --git a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/service/AbstractWatchService.java b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/service/AbstractWatchService.java index 7b0dcd6e396..14f98f47289 100644 --- a/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/service/AbstractWatchService.java +++ b/bundles/core/org.eclipse.smarthome.core/src/main/java/org/eclipse/smarthome/core/service/AbstractWatchService.java @@ -14,8 +14,11 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; +import java.nio.file.WatchKey; import java.nio.file.WatchService; import java.nio.file.attribute.BasicFileAttributes; +import java.util.HashMap; +import java.util.Map; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; @@ -28,6 +31,7 @@ * >java docs for more details * * @author Fabio Marini + * @author Dimitar Ivanov - added javadoc; introduced WatchKey to directory mapping for the queue reader * */ public abstract class AbstractWatchService { @@ -74,23 +78,17 @@ protected void initializeWatchService() { if (StringUtils.isNotBlank(pathToWatch)) { Path toWatch = Paths.get(pathToWatch); try { + final Map registeredWatchKeys = new HashMap<>(); if (watchSubDirectories()) { watchService = FileSystems.getDefault().newWatchService(); - Files.walkFileTree(toWatch, new SimpleFileVisitor() { - @Override - public FileVisitResult preVisitDirectory(Path subDir, BasicFileAttributes attrs) - throws IOException { - registerDirectory(subDir); - return FileVisitResult.CONTINUE; - } - }); + registerAllDirectoriesInternal(toWatch, registeredWatchKeys); } else { watchService = toWatch.getFileSystem().newWatchService(); - registerDirectory(toWatch); + registerDirectoryInternal(toWatch, registeredWatchKeys); } - AbstractWatchQueueReader reader = buildWatchQueueReader(watchService, toWatch); + AbstractWatchQueueReader reader = buildWatchQueueReader(watchService, toWatch, registeredWatchKeys); Thread qr = new Thread(reader, "Dir Watcher"); qr.start(); @@ -100,41 +98,93 @@ public FileVisitResult preVisitDirectory(Path subDir, BasicFileAttributes attrs) } } + /** + * This method walks a file tree and registers all directories to be watched + * by the watch service. + *
+ *
+ * It can be used from the implementations of {@link AbstractWatchService} + * to register directories, created after the service initialization.
+ * In order to do that make sure that + * {@link AbstractWatchQueueReader#isWatchingDirectoryChanges()} is + * true and process the create events in {@link AbstractWatchQueueReader#processWatchEvent()} + *
+ * This method should be called only if {@link #watchSubDirectories} is true + * + * @param toWatch the directory being watched by the watch service + * @param registeredWatchKeys a mapping between the registered directories and their {@link WatchKey registration + * keys}. + * @throws IOException + */ + final protected void registerAllDirectoriesInternal(Path toWatch, final Map registeredWatchKeys) + throws IOException { + Files.walkFileTree(toWatch, new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path subDir, BasicFileAttributes attrs) throws IOException { + registerDirectoryInternal(subDir, registeredWatchKeys); + return FileVisitResult.CONTINUE; + } + }); + } + + private void registerDirectoryInternal(Path directory, Map registredWatchKeys) throws IOException { + WatchKey registrationKey = registerDirectory(directory); + if (registrationKey != null) { + registredWatchKeys.put(registrationKey, directory); + } else { + logger.info("The directory '{}' was not registered in the watch service", directory); + } + } + + /** + * This method will close the {@link #watchService}. + */ protected void stopWatchService() { - if(watchService != null) { - try { - watchService.close(); - } catch (IOException e) { - logger.warn("Cannot deactivate folder watcher", e); - } - - watchService = null; - } + if (watchService != null) { + try { + watchService.close(); + } catch (IOException e) { + logger.warn("Cannot deactivate folder watcher", e); + } + + watchService = null; + } } /** - * - * @param watchService - * @param toWatch - * @return + * Build a queue reader to process the watch events, provided by the watch service for the given directory + * + * @param watchService the watch service, providing the watch events for the watched directory + * @param toWatch the directory being watched by the watch service + * @param registredWatchKeys a mapping between the registered directories and their {@link WatchKey registration + * keys}. + * @return the concrete queue reader */ - protected abstract AbstractWatchQueueReader buildWatchQueueReader(WatchService watchService, Path toWatch); + protected abstract AbstractWatchQueueReader buildWatchQueueReader(WatchService watchService, Path toWatch, + Map registredWatchKeys); /** - * - * @return + * @return the path to be watched as a {@link String}. The returned path should be applicable for creating a + * {@link Path} with the {@link Paths#get(String, String...)} method. */ protected abstract String getSourcePath(); /** - * - * @return + * Determines whether the subdirectories of the source path (determined by the {@link #getSourcePath()}) will be + * watched or not. + * + * @return true if the subdirectories will be watched and false if only the source path + * (determined by the {@link #getSourcePath()}) will be watched */ protected abstract boolean watchSubDirectories(); /** - * @param subDir - * @throws IOException + * Registers a directory to be watched by the watch service. The {@link WatchKey} of the registration should be + * provided. + * + * @param directory the directory, which will be registered in the watch service + * @return The {@link WatchKey} of the registration or null if no registration has been done. + * @throws IOException if an error occurs while processing the given path */ - protected abstract void registerDirectory(Path subDir) throws IOException; -} \ No newline at end of file + protected abstract WatchKey registerDirectory(Path directory) throws IOException; +} diff --git a/bundles/model/org.eclipse.smarthome.model.core/src/main/java/org/eclipse/smarthome/model/core/internal/folder/FolderObserver.java b/bundles/model/org.eclipse.smarthome.model.core/src/main/java/org/eclipse/smarthome/model/core/internal/folder/FolderObserver.java index 6fa80851300..db973dead72 100644 --- a/bundles/model/org.eclipse.smarthome.model.core/src/main/java/org/eclipse/smarthome/model/core/internal/folder/FolderObserver.java +++ b/bundles/model/org.eclipse.smarthome.model.core/src/main/java/org/eclipse/smarthome/model/core/internal/folder/FolderObserver.java @@ -142,7 +142,7 @@ public WatchQueueReader(WatchService watchService, Path dirToWatch, Map event, Kind kind, Path path) { - File toCheck = getFileByFileExtMap(folderFileExtMap, path.toString()); + File toCheck = getFileByFileExtMap(folderFileExtMap, path.getFileName().toString()); if (toCheck != null) { checkFile(modelRepo, toCheck, kind); }