From 52ced669dd4d3efa1fe0af8d5a1eb871cab27861 Mon Sep 17 00:00:00 2001 From: Zlatin Balevsky Date: Mon, 17 Jun 2019 16:36:12 +0100 Subject: [PATCH 01/11] basic watching of directories --- .../main/groovy/com/muwire/core/Core.groovy | 7 ++ .../muwire/core/files/DirectoryWatcher.groovy | 65 +++++++++++++++++++ .../muwire/core/files/HasherService.groovy | 2 +- 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/core/src/main/groovy/com/muwire/core/Core.groovy b/core/src/main/groovy/com/muwire/core/Core.groovy index e77548c5..4dd272a5 100644 --- a/core/src/main/groovy/com/muwire/core/Core.groovy +++ b/core/src/main/groovy/com/muwire/core/Core.groovy @@ -23,6 +23,7 @@ import com.muwire.core.files.FileSharedEvent import com.muwire.core.files.FileUnsharedEvent import com.muwire.core.files.HasherService import com.muwire.core.files.PersisterService +import com.muwire.core.files.DirectoryWatcher import com.muwire.core.hostcache.CacheClient import com.muwire.core.hostcache.HostCache import com.muwire.core.hostcache.HostDiscoveredEvent @@ -70,6 +71,7 @@ public class Core { private final ConnectionEstablisher connectionEstablisher private final HasherService hasherService private final DownloadManager downloadManager + private final DirectoryWatcher directoryWatcher public Core(MuWireSettings props, File home, String myVersion) { this.home = home @@ -213,6 +215,9 @@ public class Core { connectionAcceptor = new ConnectionAcceptor(eventBus, connectionManager, props, i2pAcceptor, hostCache, trustService, searchManager, uploadManager, connectionEstablisher) + log.info("initializing directory watcher") + directoryWatcher = new DirectoryWatcher(eventBus) + eventBus.register(FileSharedEvent.class, directoryWatcher) log.info("initializing hasher service") hasherService = new HasherService(new FileHasher(), eventBus, fileManager) @@ -221,6 +226,7 @@ public class Core { public void startServices() { hasherService.start() + directoryWatcher.start() trustService.start() trustService.waitForLoad() persisterService.start() @@ -242,6 +248,7 @@ public class Core { connectionEstablisher.stop() log.info("shutting down connection manager") connectionManager.shutdown() + directoryWatcher.stop() } static main(args) { diff --git a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy index 1410786a..295e7557 100644 --- a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy +++ b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy @@ -1,4 +1,69 @@ package com.muwire.core.files +import java.nio.file.FileSystem +import java.nio.file.FileSystems +import java.nio.file.Path +import java.nio.file.Paths +import java.nio.file.StandardWatchEventKinds +import java.nio.file.WatchEvent +import java.nio.file.WatchKey +import java.nio.file.WatchService + +import com.muwire.core.EventBus + +import groovy.util.logging.Log + +@Log class DirectoryWatcher { + + private final EventBus eventBus + private final Thread watcherThread + private WatchService watchService + + DirectoryWatcher(EventBus eventBus) { + this.eventBus = eventBus + this.watcherThread = new Thread({watch() } as Runnable, "directory-watcher") + } + + void start() { + watchService = FileSystems.getDefault().newWatchService() + watcherThread.start() + } + + void stop() { + watcherThread.interrupt() + watchService.close() + } + + void onFileSharedEvent(FileSharedEvent e) { + if (!e.file.isDirectory()) + return + Path path = e.file.getCanonicalFile().toPath() + path.register(watchService, + StandardWatchEventKinds.ENTRY_MODIFY, + StandardWatchEventKinds.ENTRY_DELETE) + + } + + private void watch() { + while(true) { + WatchKey key = watchService.take() + key.pollEvents().each { + if (it.kind() == StandardWatchEventKinds.ENTRY_MODIFY) + processModified(key.watchable(), it.context()) + } + key.reset() + } + } + + + private void processModified(Path parent, Path path) { + File f = join(parent, path) + eventBus.publish(new FileSharedEvent(file : f)) + } + + private static File join(Path parent, Path path) { + File parentFile = parent.toFile().getCanonicalFile() + new File(parentFile, path.toFile().getName()) + } } diff --git a/core/src/main/groovy/com/muwire/core/files/HasherService.groovy b/core/src/main/groovy/com/muwire/core/files/HasherService.groovy index 9607b1ea..b3701a30 100644 --- a/core/src/main/groovy/com/muwire/core/files/HasherService.groovy +++ b/core/src/main/groovy/com/muwire/core/files/HasherService.groovy @@ -32,7 +32,7 @@ class HasherService { private void process(File f) { f = f.getCanonicalFile() if (f.isDirectory()) { - f.listFiles().each {onFileSharedEvent new FileSharedEvent(file: it) } + f.listFiles().each {eventBus.publish new FileSharedEvent(file: it) } } else { if (f.length() == 0) { eventBus.publish new FileHashedEvent(error: "Not sharing empty file $f") From e78016ead4ea92cd7107e218c83a16e8798c65ef Mon Sep 17 00:00:00 2001 From: Zlatin Balevsky Date: Mon, 17 Jun 2019 19:23:04 +0100 Subject: [PATCH 02/11] ui panel for managing watched directories --- .../com/muwire/core/MuWireSettings.groovy | 28 +++++++--- .../com/muwire/gui/MainFrameController.groovy | 7 +++ gui/griffon-app/lifecycle/Ready.groovy | 7 +-- .../com/muwire/gui/MainFrameModel.groovy | 8 ++- .../views/com/muwire/gui/MainFrameView.groovy | 53 +++++++++++++++---- 5 files changed, 78 insertions(+), 25 deletions(-) diff --git a/core/src/main/groovy/com/muwire/core/MuWireSettings.groovy b/core/src/main/groovy/com/muwire/core/MuWireSettings.groovy index d1078ceb..76ced3b3 100644 --- a/core/src/main/groovy/com/muwire/core/MuWireSettings.groovy +++ b/core/src/main/groovy/com/muwire/core/MuWireSettings.groovy @@ -1,6 +1,11 @@ package com.muwire.core +import java.util.stream.Collectors + import com.muwire.core.hostcache.CrawlerResponse +import com.muwire.core.util.DataUtil + +import net.i2p.data.Base64 class MuWireSettings { @@ -10,10 +15,9 @@ class MuWireSettings { int updateCheckInterval String nickname File downloadLocation - String sharedFiles CrawlerResponse crawlerResponse boolean shareDownloadedFiles - boolean watchSharedDirectories + Set watchedDirectories MuWireSettings() { this(new Properties()) @@ -26,11 +30,16 @@ class MuWireSettings { nickname = props.getProperty("nickname","MuWireUser") downloadLocation = new File((String)props.getProperty("downloadLocation", System.getProperty("user.home"))) - sharedFiles = props.getProperty("sharedFiles") downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","15")) updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","36")) shareDownloadedFiles = Boolean.parseBoolean(props.getProperty("shareDownloadedFiles","true")) - watchSharedDirectories = Boolean.parseBoolean(props.getProperty("watchSharedDirectories","true")) + + watchedDirectories = new HashSet<>() + if (props.containsKey("watchedDirectories")) { + String[] encoded = props.getProperty("watchedDirectories").split(",") + encoded.each { watchedDirectories << DataUtil.readi18nString(Base64.decode(it)) } + } + } void write(OutputStream out) throws IOException { @@ -43,9 +52,14 @@ class MuWireSettings { props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval)) props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval)) props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles)) - props.setProperty("watchSharedDirectories", String.valueOf(watchSharedDirectories)) - if (sharedFiles != null) - props.setProperty("sharedFiles", sharedFiles) + + if (!watchedDirectories.isEmpty()) { + String encoded = watchedDirectories.stream(). + map({Base64.encode(DataUtil.encodei18nString(it))}). + collect(Collectors.joining(",")) + props.setProperty("watchedDirectories", encoded) + } + props.store(out, "") } diff --git a/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy b/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy index 7102eff1..609e7480 100644 --- a/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy +++ b/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy @@ -194,6 +194,13 @@ class MainFrameController { println "unsharing selected files" } + void saveMuWireSettings() { + File f = new File(core.home, "MuWire.properties") + f.withOutputStream { + core.muOptions.write(it) + } + } + void mvcGroupInit(Map args) { application.addPropertyChangeListener("core", {e-> core = e.getNewValue() diff --git a/gui/griffon-app/lifecycle/Ready.groovy b/gui/griffon-app/lifecycle/Ready.groovy index 7f8419f5..61bdd49b 100644 --- a/gui/griffon-app/lifecycle/Ready.groovy +++ b/gui/griffon-app/lifecycle/Ready.groovy @@ -1,3 +1,4 @@ + import griffon.core.GriffonApplication import griffon.core.env.Metadata import groovy.util.logging.Log @@ -104,12 +105,6 @@ class Ready extends AbstractLifecycleHandler { it.propertyChange(new PropertyChangeEvent(this, "core", null, core)) } - if (props.sharedFiles != null) { - props.sharedFiles.split(",").each { - core.eventBus.publish(new FileSharedEvent(file : new File(it))) - } - } - core.eventBus.publish(new UILoadedEvent()) } } diff --git a/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy b/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy index e50c3d38..9cc4710f 100644 --- a/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy +++ b/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy @@ -9,6 +9,7 @@ import javax.swing.JTable import com.muwire.core.Core import com.muwire.core.InfoHash +import com.muwire.core.MuWireSettings import com.muwire.core.Persona import com.muwire.core.connection.ConnectionAttemptStatus import com.muwire.core.connection.ConnectionEvent @@ -53,6 +54,7 @@ class MainFrameModel { def downloads = [] def uploads = [] def shared = [] + def watched = [] def connectionList = [] def searches = new LinkedList() def trusted = [] @@ -133,7 +135,7 @@ class MainFrameModel { core.eventBus.register(FileDownloadedEvent.class, this) timer.schedule({ - int retryInterval = application.context.get("muwire-settings").downloadRetryInterval + int retryInterval = core.muOptions.downloadRetryInterval if (retryInterval > 0) { retryInterval *= 60000 long now = System.currentTimeMillis() @@ -156,6 +158,10 @@ class MainFrameModel { runInsideUIAsync { trusted.addAll(core.trustService.good.values()) distrusted.addAll(core.trustService.bad.values()) + + watched.addAll(core.muOptions.watchedDirectories) + builder.getVariable("watched-directories-table").model.fireTableDataChanged() + watched.each { core.eventBus.publish(new FileSharedEvent(file : new File(it))) } } }) diff --git a/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy b/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy index 3a5b941d..0e850ffb 100644 --- a/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy +++ b/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy @@ -139,16 +139,32 @@ class MainFrameView { panel (constraints: "uploads window"){ gridLayout(cols : 1, rows : 2) panel { - borderLayout() - panel (constraints : BorderLayout.NORTH) { - button(text : "Click here to share files", actionPerformed : shareFiles) + gridLayout(cols : 2, rows : 1) + panel { + borderLayout() + panel (constraints : BorderLayout.NORTH) { + button(text : "Add directories to watch", actionPerformed : watchDirectories) + } + scrollPane (constraints : BorderLayout.CENTER) { + table(id : "watched-directories-table", autoCreateRowSorter: true) { + tableModel(list : model.watched) { + closureColumn(header: "Watched Directories", type : String, read : { it }) + } + } + } } - scrollPane ( constraints : BorderLayout.CENTER) { - table(id : "shared-files-table", autoCreateRowSorter: true) { - tableModel(list : model.shared) { - closureColumn(header : "Name", preferredWidth : 550, type : String, read : {row -> row.file.getAbsolutePath()}) - closureColumn(header : "Size", preferredWidth : 50, type : Long, read : {row -> row.file.length() }) - } + panel { + borderLayout() + panel (constraints : BorderLayout.NORTH) { + button(text : "Share files", actionPerformed : shareFiles) + } + scrollPane(constraints : BorderLayout.CENTER) { + table(id : "shared-files-table", autoCreateRowSorter: true) { + tableModel(list : model.shared) { + closureColumn(header : "Name", preferredWidth : 500, type : String, read : {row -> row.file.getAbsolutePath()}) + closureColumn(header : "Size", preferredWidth : 100, type : Long, read : {row -> row.file.length() }) + } + } } } } @@ -466,11 +482,26 @@ class MainFrameView { def shareFiles = { def chooser = new JFileChooser() - chooser.setDialogTitle("Select file or directory to share") - chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES) + chooser.setDialogTitle("Select file to share") + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY) int rv = chooser.showOpenDialog(null) if (rv == JFileChooser.APPROVE_OPTION) { model.core.eventBus.publish(new FileSharedEvent(file : chooser.getSelectedFile())) } } + + def watchDirectories = { + def chooser = new JFileChooser() + chooser.setDialogTitle("Select directory to watch") + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY) + int rv = chooser.showOpenDialog(null) + if (rv == JFileChooser.APPROVE_OPTION) { + File f = chooser.getSelectedFile() + model.watched << f.getAbsolutePath() + application.context.get("muwire-settings").watchedDirectories << f.getAbsolutePath() + mvcGroup.controller.saveMuWireSettings() + builder.getVariable("watched-directories-table").model.fireTableDataChanged() + model.core.eventBus.publish(new FileSharedEvent(file : f)) + } + } } \ No newline at end of file From aef7533bd5f1f44fa17791ed50609dabb2a8328d Mon Sep 17 00:00:00 2001 From: Zlatin Balevsky Date: Mon, 17 Jun 2019 19:58:57 +0100 Subject: [PATCH 03/11] make watcher thread daemon --- .../main/groovy/com/muwire/core/files/DirectoryWatcher.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy index 295e7557..ca97b10b 100644 --- a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy +++ b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy @@ -23,6 +23,7 @@ class DirectoryWatcher { DirectoryWatcher(EventBus eventBus) { this.eventBus = eventBus this.watcherThread = new Thread({watch() } as Runnable, "directory-watcher") + watcherThread.setDaemon(true) } void start() { From d5eb65bdc20314e620293c2b07145452d9b2a4e8 Mon Sep 17 00:00:00 2001 From: Zlatin Balevsky Date: Mon, 17 Jun 2019 21:58:44 +0100 Subject: [PATCH 04/11] do not print stacktrace on clean shutdown --- .../muwire/core/files/DirectoryWatcher.groovy | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy index ca97b10b..51081fce 100644 --- a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy +++ b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy @@ -19,6 +19,7 @@ class DirectoryWatcher { private final EventBus eventBus private final Thread watcherThread private WatchService watchService + private volatile boolean shutdown DirectoryWatcher(EventBus eventBus) { this.eventBus = eventBus @@ -32,6 +33,7 @@ class DirectoryWatcher { } void stop() { + shutdown = true watcherThread.interrupt() watchService.close() } @@ -45,15 +47,20 @@ class DirectoryWatcher { StandardWatchEventKinds.ENTRY_DELETE) } - + private void watch() { - while(true) { - WatchKey key = watchService.take() - key.pollEvents().each { - if (it.kind() == StandardWatchEventKinds.ENTRY_MODIFY) - processModified(key.watchable(), it.context()) + try { + while(!shutdown) { + WatchKey key = watchService.take() + key.pollEvents().each { + if (it.kind() == StandardWatchEventKinds.ENTRY_MODIFY) + processModified(key.watchable(), it.context()) + } + key.reset() } - key.reset() + } catch (InterruptedException e) { + if (!shutdown) + throw e } } From 71a919e62b654dd878d4823f56367e0c7c6fb37a Mon Sep 17 00:00:00 2001 From: Zlatin Balevsky Date: Mon, 17 Jun 2019 22:15:50 +0100 Subject: [PATCH 05/11] shut down watcher before connection manager --- core/src/main/groovy/com/muwire/core/Core.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/groovy/com/muwire/core/Core.groovy b/core/src/main/groovy/com/muwire/core/Core.groovy index 4dd272a5..287ef738 100644 --- a/core/src/main/groovy/com/muwire/core/Core.groovy +++ b/core/src/main/groovy/com/muwire/core/Core.groovy @@ -246,9 +246,10 @@ public class Core { connectionAcceptor.stop() log.info("shutting down connection establisher") connectionEstablisher.stop() + log.info("shutting down directory watcher") + directoryWatcher.stop() log.info("shutting down connection manager") connectionManager.shutdown() - directoryWatcher.stop() } static main(args) { From 7e2c4d48c6566e54151b7db758097d24d453aa7a Mon Sep 17 00:00:00 2001 From: Zlatin Balevsky Date: Mon, 17 Jun 2019 22:34:19 +0100 Subject: [PATCH 06/11] wait for UI to load before loading files --- cli/src/main/groovy/com/muwire/cli/Cli.groovy | 4 +++- core/src/main/groovy/com/muwire/core/Core.groovy | 2 +- .../com/muwire/core/files/PersisterService.groovy | 11 ++++++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/cli/src/main/groovy/com/muwire/cli/Cli.groovy b/cli/src/main/groovy/com/muwire/cli/Cli.groovy index c1ee95cb..2dbcd9c0 100644 --- a/cli/src/main/groovy/com/muwire/cli/Cli.groovy +++ b/cli/src/main/groovy/com/muwire/cli/Cli.groovy @@ -4,6 +4,7 @@ import java.util.concurrent.CountDownLatch import com.muwire.core.Core import com.muwire.core.MuWireSettings +import com.muwire.core.UILoadedEvent import com.muwire.core.connection.ConnectionAttemptStatus import com.muwire.core.connection.ConnectionEvent import com.muwire.core.connection.DisconnectionEvent @@ -83,7 +84,8 @@ class Cli { } core.eventBus.register(AllFilesLoadedEvent.class, fileLoader) core.startServices() - + + core.eventBus.publish(new UILoadedEvent()) println "waiting for files to load" latch.await() // now we begin diff --git a/core/src/main/groovy/com/muwire/core/Core.groovy b/core/src/main/groovy/com/muwire/core/Core.groovy index 287ef738..f844de58 100644 --- a/core/src/main/groovy/com/muwire/core/Core.groovy +++ b/core/src/main/groovy/com/muwire/core/Core.groovy @@ -164,6 +164,7 @@ public class Core { log.info "initializing persistence service" persisterService = new PersisterService(new File(home, "files.json"), eventBus, 15000, fileManager) + eventBus.register(UILoadedEvent.class, persisterService) log.info("initializing host cache") File hostStorage = new File(home, "hosts.json") @@ -229,7 +230,6 @@ public class Core { directoryWatcher.start() trustService.start() trustService.waitForLoad() - persisterService.start() hostCache.start() connectionManager.start() cacheClient.start() diff --git a/core/src/main/groovy/com/muwire/core/files/PersisterService.groovy b/core/src/main/groovy/com/muwire/core/files/PersisterService.groovy index 63911113..051cde0a 100644 --- a/core/src/main/groovy/com/muwire/core/files/PersisterService.groovy +++ b/core/src/main/groovy/com/muwire/core/files/PersisterService.groovy @@ -11,6 +11,7 @@ import com.muwire.core.EventBus import com.muwire.core.InfoHash import com.muwire.core.Service import com.muwire.core.SharedFile +import com.muwire.core.UILoadedEvent import com.muwire.core.util.DataUtil import groovy.json.JsonOutput @@ -36,14 +37,14 @@ class PersisterService extends Service { timer = new Timer("file persister", true) } - void start() { - timer.schedule({load()} as TimerTask, 1) - } - void stop() { timer.cancel() } - + + void onUILoadedEvent(UILoadedEvent e) { + timer.schedule({load()} as TimerTask, 1) + } + void load() { if (location.exists() && location.isFile()) { def slurper = new JsonSlurper() From c46f1b1ccd9da6d946c147b0ed055ae93d3e7b18 Mon Sep 17 00:00:00 2001 From: Zlatin Balevsky Date: Mon, 17 Jun 2019 23:08:16 +0100 Subject: [PATCH 07/11] delay processing of files until after 1 second after the last MODIFY event --- .../muwire/core/files/DirectoryWatcher.groovy | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy index 51081fce..d2b162ef 100644 --- a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy +++ b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy @@ -8,6 +8,7 @@ import java.nio.file.StandardWatchEventKinds import java.nio.file.WatchEvent import java.nio.file.WatchKey import java.nio.file.WatchService +import java.util.concurrent.ConcurrentHashMap import com.muwire.core.EventBus @@ -16,8 +17,11 @@ import groovy.util.logging.Log @Log class DirectoryWatcher { + private static final long WAIT_TIME = 1000 + private final EventBus eventBus - private final Thread watcherThread + private final Thread watcherThread, publisherThread + private final Map waitingFiles = new ConcurrentHashMap<>() private WatchService watchService private volatile boolean shutdown @@ -25,16 +29,20 @@ class DirectoryWatcher { this.eventBus = eventBus this.watcherThread = new Thread({watch() } as Runnable, "directory-watcher") watcherThread.setDaemon(true) + this.publisherThread = new Thread({publish()} as Runnable, "watched-files-publisher") + publisherThread.setDaemon(true) } void start() { watchService = FileSystems.getDefault().newWatchService() watcherThread.start() + publisherThread.start() } void stop() { shutdown = true watcherThread.interrupt() + publisherThread.interrupt() watchService.close() } @@ -67,11 +75,33 @@ class DirectoryWatcher { private void processModified(Path parent, Path path) { File f = join(parent, path) - eventBus.publish(new FileSharedEvent(file : f)) + waitingFiles.put(f, System.currentTimeMillis()) } private static File join(Path parent, Path path) { File parentFile = parent.toFile().getCanonicalFile() new File(parentFile, path.toFile().getName()) } + + private void publish() { + try { + while(!shutdown) { + Thread.sleep(WAIT_TIME) + long now = System.currentTimeMillis() + def published = [] + waitingFiles.each { file, timestamp -> + if (now - timestamp > WAIT_TIME) { + eventBus.publish new FileSharedEvent(file : file) + published << file + } + } + published.each { + waitingFiles.remove(it) + } + } + } catch (InterruptedException e) { + if (!shutdown) + throw e + } + } } From 84a9bb9482effc5597ace120a725c46753e568d8 Mon Sep 17 00:00:00 2001 From: Zlatin Balevsky Date: Tue, 18 Jun 2019 04:15:44 +0100 Subject: [PATCH 08/11] watch deleting of files --- core/src/main/groovy/com/muwire/core/Core.groovy | 2 +- .../com/muwire/core/files/DirectoryWatcher.groovy | 14 +++++++++++++- .../models/com/muwire/gui/MainFrameModel.groovy | 13 +++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/core/src/main/groovy/com/muwire/core/Core.groovy b/core/src/main/groovy/com/muwire/core/Core.groovy index f844de58..5dee8d85 100644 --- a/core/src/main/groovy/com/muwire/core/Core.groovy +++ b/core/src/main/groovy/com/muwire/core/Core.groovy @@ -217,7 +217,7 @@ public class Core { i2pAcceptor, hostCache, trustService, searchManager, uploadManager, connectionEstablisher) log.info("initializing directory watcher") - directoryWatcher = new DirectoryWatcher(eventBus) + directoryWatcher = new DirectoryWatcher(eventBus, fileManager) eventBus.register(FileSharedEvent.class, directoryWatcher) log.info("initializing hasher service") diff --git a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy index d2b162ef..01540058 100644 --- a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy +++ b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy @@ -11,6 +11,7 @@ import java.nio.file.WatchService import java.util.concurrent.ConcurrentHashMap import com.muwire.core.EventBus +import com.muwire.core.SharedFile import groovy.util.logging.Log @@ -20,13 +21,15 @@ class DirectoryWatcher { private static final long WAIT_TIME = 1000 private final EventBus eventBus + private final FileManager fileManager private final Thread watcherThread, publisherThread private final Map waitingFiles = new ConcurrentHashMap<>() private WatchService watchService private volatile boolean shutdown - DirectoryWatcher(EventBus eventBus) { + DirectoryWatcher(EventBus eventBus, FileManager fileManager) { this.eventBus = eventBus + this.fileManager = fileManager this.watcherThread = new Thread({watch() } as Runnable, "directory-watcher") watcherThread.setDaemon(true) this.publisherThread = new Thread({publish()} as Runnable, "watched-files-publisher") @@ -63,6 +66,8 @@ class DirectoryWatcher { key.pollEvents().each { if (it.kind() == StandardWatchEventKinds.ENTRY_MODIFY) processModified(key.watchable(), it.context()) + if (it.kind() == StandardWatchEventKinds.ENTRY_DELETE) + processDeleted(key.watchable(), it.context()) } key.reset() } @@ -78,6 +83,13 @@ class DirectoryWatcher { waitingFiles.put(f, System.currentTimeMillis()) } + private void processDeleted(Path parent, Path path) { + File f = join(parent, path) + SharedFile sf = fileManager.fileToSharedFile.get(f) + if (sf != null) + eventBus.publish(new FileUnsharedEvent(unsharedFile : sf)) + } + private static File join(Path parent, Path path) { File parentFile = parent.toFile().getCanonicalFile() new File(parentFile, path.toFile().getName()) diff --git a/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy b/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy index 9cc4710f..90b7911a 100644 --- a/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy +++ b/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy @@ -20,6 +20,7 @@ import com.muwire.core.files.FileDownloadedEvent import com.muwire.core.files.FileHashedEvent import com.muwire.core.files.FileLoadedEvent import com.muwire.core.files.FileSharedEvent +import com.muwire.core.files.FileUnsharedEvent import com.muwire.core.search.QueryEvent import com.muwire.core.search.UIResultBatchEvent import com.muwire.core.search.UIResultEvent @@ -133,6 +134,7 @@ class MainFrameModel { core.eventBus.register(QueryEvent.class, this) core.eventBus.register(UpdateAvailableEvent.class, this) core.eventBus.register(FileDownloadedEvent.class, this) + core.eventBus.register(FileUnsharedEvent.class, this) timer.schedule({ int retryInterval = core.muOptions.downloadRetryInterval @@ -242,6 +244,17 @@ class MainFrameModel { } } + void onFileUnsharedEvent(FileUnsharedEvent e) { + InfoHash infohash = e.unsharedFile.infoHash + if (!infoHashes.remove(infohash)) + return + runInsideUIAsync { + shared.remove(e.unsharedFile) + JTable table = builder.getVariable("shared-files-table") + table.model.fireTableDataChanged() + } + } + void onUploadEvent(UploadEvent e) { runInsideUIAsync { uploads << e.uploader From 9c049b93019eae665917bc411b8668083c333f15 Mon Sep 17 00:00:00 2001 From: Zlatin Balevsky Date: Tue, 18 Jun 2019 05:26:41 +0100 Subject: [PATCH 09/11] special case mac --- .../muwire/core/files/DirectoryWatcher.groovy | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy index 01540058..a88d7d6f 100644 --- a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy +++ b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy @@ -4,7 +4,7 @@ import java.nio.file.FileSystem import java.nio.file.FileSystems import java.nio.file.Path import java.nio.file.Paths -import java.nio.file.StandardWatchEventKinds +import static java.nio.file.StandardWatchEventKinds.* import java.nio.file.WatchEvent import java.nio.file.WatchKey import java.nio.file.WatchService @@ -14,12 +14,21 @@ import com.muwire.core.EventBus import com.muwire.core.SharedFile import groovy.util.logging.Log +import net.i2p.util.SystemVersion @Log class DirectoryWatcher { private static final long WAIT_TIME = 1000 + private static final WatchEvent.Kind[] kinds + static { + if (SystemVersion.isMac()) + kinds = [ENTRY_MODIFY, ENTRY_DELETE] + else + kinds = [ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE] + } + private final EventBus eventBus private final FileManager fileManager private final Thread watcherThread, publisherThread @@ -53,9 +62,7 @@ class DirectoryWatcher { if (!e.file.isDirectory()) return Path path = e.file.getCanonicalFile().toPath() - path.register(watchService, - StandardWatchEventKinds.ENTRY_MODIFY, - StandardWatchEventKinds.ENTRY_DELETE) + path.register(watchService, kinds) } @@ -64,10 +71,11 @@ class DirectoryWatcher { while(!shutdown) { WatchKey key = watchService.take() key.pollEvents().each { - if (it.kind() == StandardWatchEventKinds.ENTRY_MODIFY) - processModified(key.watchable(), it.context()) - if (it.kind() == StandardWatchEventKinds.ENTRY_DELETE) - processDeleted(key.watchable(), it.context()) + switch(it.kind()) { + case ENTRY_CREATE: processCreated(key.watchable(), it.context()); break + case ENTRY_MODIFY: processModified(key.watchable(), it.context()); break + case ENTRY_DELETE: processDeleted(key.watchable(), it.context()); break + } } key.reset() } @@ -77,14 +85,21 @@ class DirectoryWatcher { } } - + + private void processCreated(Path parent, Path path) { + File f= join(parent, path) + log.fine("created entry $f") + } + private void processModified(Path parent, Path path) { File f = join(parent, path) + log.fine("modified entry $f") waitingFiles.put(f, System.currentTimeMillis()) } private void processDeleted(Path parent, Path path) { File f = join(parent, path) + log.fine("deleted entry $f") SharedFile sf = fileManager.fileToSharedFile.get(f) if (sf != null) eventBus.publish(new FileUnsharedEvent(unsharedFile : sf)) @@ -103,6 +118,7 @@ class DirectoryWatcher { def published = [] waitingFiles.each { file, timestamp -> if (now - timestamp > WAIT_TIME) { + log.info("publishing file $file") eventBus.publish new FileSharedEvent(file : file) published << file } From c698cbd7370ca6b1791f24c39c52ee2d9ef8e580 Mon Sep 17 00:00:00 2001 From: Zlatin Balevsky Date: Tue, 18 Jun 2019 05:43:41 +0100 Subject: [PATCH 10/11] register created directories recursively --- .../main/groovy/com/muwire/core/files/DirectoryWatcher.groovy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy index a88d7d6f..ca8e0023 100644 --- a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy +++ b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy @@ -89,6 +89,8 @@ class DirectoryWatcher { private void processCreated(Path parent, Path path) { File f= join(parent, path) log.fine("created entry $f") + if (f.isDirectory()) + f.toPath().register(watchService, kinds) } private void processModified(Path parent, Path path) { From 6a407878633fe3b16174c354b202a09b472be589 Mon Sep 17 00:00:00 2001 From: Zlatin Balevsky Date: Tue, 18 Jun 2019 05:46:16 +0100 Subject: [PATCH 11/11] fine log --- .../main/groovy/com/muwire/core/files/DirectoryWatcher.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy index ca8e0023..e0b5121e 100644 --- a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy +++ b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy @@ -120,7 +120,7 @@ class DirectoryWatcher { def published = [] waitingFiles.each { file, timestamp -> if (now - timestamp > WAIT_TIME) { - log.info("publishing file $file") + log.fine("publishing file $file") eventBus.publish new FileSharedEvent(file : file) published << file }