Skip to content

Commit

Permalink
Switch to barbarywatchservice, an implemention of Java 7's WatchServi…
Browse files Browse the repository at this point in the history
…ce for Java 6 on Mac OS X.
  • Loading branch information
dkocher committed Nov 2, 2009
1 parent 4892345 commit 564d0f0
Show file tree
Hide file tree
Showing 22 changed files with 1,705 additions and 70 deletions.
279 changes: 260 additions & 19 deletions Acknowledgments.rtf

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Credits.rtf
Expand Up @@ -35,6 +35,7 @@
\cf2 {\field{\*\fldinst{HYPERLINK "http://svn.oofn.net/"}}{\fldrslt CTGradient}}\cf0 Chad Weider\
\cf2 {\field{\*\fldinst{HYPERLINK "http://extendmac.com/EMKeychain/"}}{\fldrslt EMKeychain}}\cf0 Brian Amerige\
\cf2 {\field{\*\fldinst{HYPERLINK "http://www.aquaticmac.com/"}}{\fldrslt AquaticPrime}}\cf0 Lucas Newman\
\cf2 {\field{\*\fldinst{HYPERLINK "http://www.aquaticmac.com/"}}{\fldrslt barbarywatchservice}}\cf0 Steve McLeod\
\b0 \cf2 \
Dashboard Widget
Expand Down
108 changes: 64 additions & 44 deletions source/ch/cyberduck/core/io/FileWatcher.java
Expand Up @@ -20,67 +20,87 @@

import ch.cyberduck.core.Local;
import ch.cyberduck.core.LocalFactory;
import ch.cyberduck.ui.cocoa.foundation.NSAutoreleasePool;

import org.apache.log4j.Logger;

import com.barbarysoftware.watchservice.*;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import static com.barbarysoftware.watchservice.StandardWatchEventKind.*;

/**
* @version $Id$
*/
public class FileWatcher implements FileMonitor.FileListener {
public class FileWatcher {
private static Logger log = Logger.getLogger(FileWatcher.class);

private FileMonitor monitor = FileMonitor.getInstance();
private WatchService monitor;
private Local file;

private static FileWatcher instance = null;

private FileWatcher() {
monitor.addFileListener(this);
public FileWatcher(Local file) {
this.file = file;
this.monitor = WatchService.newWatchService();
}

private static final Object lock = new Object();
public void watch(final FileWatcherListener listener) throws IOException {
final WatchableFile watchable = new WatchableFile(new File(file.getParent().getAbsolute()));
watchable.register(monitor, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
final Thread consumer = new Thread(new Runnable() {
public void run() {
while(true) {
final NSAutoreleasePool pool = NSAutoreleasePool.push();

public static FileWatcher instance() {
synchronized(lock) {
if(null == instance) {
instance = new FileWatcher();
try {
// wait for key to be signaled
WatchKey key;
try {
key = monitor.take();
}
catch(ClosedWatchServiceException e) {
// If this watch service is closed
return;
}
catch(InterruptedException e) {
return;
}
for(WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
if(kind == OVERFLOW) {
continue;
}
// The filename is the context of the event.
WatchEvent<File> ev = (WatchEvent<File>) event;
log.debug("Detected file system event: " + ev.context() + " " + kind);
if(ENTRY_CREATE == kind) {
listener.fileWritten(LocalFactory.createLocal(ev.context()));
}
if(ENTRY_MODIFY == kind) {
listener.fileWritten(LocalFactory.createLocal(ev.context()));
}
if(ENTRY_DELETE == kind) {
listener.fileWritten(LocalFactory.createLocal(ev.context()));
}
}
// Reset the key -- this step is critical to receive further watch events.
boolean valid = key.reset();
if(!valid) {
// The key is no longer valid and the loop can exit.
break;
}
}
finally {
pool.release();
}
}
}
return instance;
}
}

private Map<File, FileWatcherListener> listeners = new HashMap<File, FileWatcherListener>();

public void watch(final Local file, final FileWatcherListener listener) throws IOException {
final File f = new File(file.getAbsolute());
monitor.addWatch(f);
listeners.put(f, listener);
}

public void unwatch(final Local file) throws IOException {
final File f = new File(file.getAbsolute());
monitor.removeWatch(f);
listeners.remove(f);
});
consumer.start();
}

public void fileChanged(FileMonitor.FileEvent e) {
final FileWatcherListener listener = listeners.get(e.getFile());
if(null == listener) {
log.error("No listener for " + e);
return;
}
if(e.getType() == FileMonitor.FILE_MODIFIED || e.getType() == FileMonitor.FILE_SIZE_CHANGED) {
listener.fileWritten(LocalFactory.createLocal(e.getFile()));
}
if(e.getType() == FileMonitor.FILE_DELETED) {
listener.fileDeleted(LocalFactory.createLocal(e.getFile()));
}
if(e.getType() == FileMonitor.FILE_RENAMED) {
listener.fileRenamed(LocalFactory.createLocal(e.getFile()));
}
public void unwatch() throws IOException {
monitor.close();
}
}
17 changes: 10 additions & 7 deletions source/ch/cyberduck/ui/cocoa/odb/WatchEditor.java
Expand Up @@ -39,6 +39,8 @@
public class WatchEditor extends Editor implements FileWatcherListener {
private static Logger log = Logger.getLogger(WatchEditor.class);

private FileWatcher monitor;

/**
* @param c
*/
Expand All @@ -59,7 +61,7 @@ public WatchEditor(CDBrowserController c, String bundleIdentifier, Path path) {
*/
@Override
public void edit() {
watch(edited.getLocal());
this.watch();
if(null == bundleIdentifier) {
NSWorkspace.sharedWorkspace().openFile(edited.getLocal().getAbsolute());
}
Expand All @@ -69,18 +71,19 @@ public void edit() {
}
}

private void watch(Local f) {
private void watch() {
monitor = new FileWatcher(edited.getLocal());
try {
FileWatcher.instance().watch(f, this);
monitor.watch(this);
}
catch(IOException e) {
log.error(e.getMessage());
}
}

private void unwatch(Local f) {
private void unwatch() {
try {
FileWatcher.instance().unwatch(f);
monitor.unwatch();
}
catch(IOException e) {
log.error(e.getMessage());
Expand All @@ -89,7 +92,7 @@ private void unwatch(Local f) {

@Override
protected void delete() {
this.unwatch(edited.getLocal());
this.unwatch();
super.delete();
}

Expand Down Expand Up @@ -131,7 +134,7 @@ public void fileDeleted(Local file) {
log.debug("fileDeleted:" + file);
if(file.exists()) {
this.save();
this.watch(edited.getLocal());
this.watch();
}
}
}
7 changes: 7 additions & 0 deletions source/com/barbarysoftware/jna/CFAllocatorRef.java
@@ -0,0 +1,7 @@
package com.barbarysoftware.jna;

import com.sun.jna.ptr.PointerByReference;

public class CFAllocatorRef extends PointerByReference {

}
7 changes: 7 additions & 0 deletions source/com/barbarysoftware/jna/CFArrayRef.java
@@ -0,0 +1,7 @@
package com.barbarysoftware.jna;

import com.sun.jna.ptr.PointerByReference;

public class CFArrayRef extends PointerByReference {

}
13 changes: 13 additions & 0 deletions source/com/barbarysoftware/jna/CFIndex.java
@@ -0,0 +1,13 @@
package com.barbarysoftware.jna;

import com.sun.jna.NativeLong;

public class CFIndex extends NativeLong {
private static final long serialVersionUID = 0;

public static CFIndex valueOf(int i) {
CFIndex idx = new CFIndex();
idx.setValue(i);
return idx;
}
}
7 changes: 7 additions & 0 deletions source/com/barbarysoftware/jna/CFRunLoopRef.java
@@ -0,0 +1,7 @@
package com.barbarysoftware.jna;

import com.sun.jna.ptr.PointerByReference;

public class CFRunLoopRef extends PointerByReference {

}
13 changes: 13 additions & 0 deletions source/com/barbarysoftware/jna/CFStringRef.java
@@ -0,0 +1,13 @@
package com.barbarysoftware.jna;

import com.sun.jna.ptr.PointerByReference;

public class CFStringRef extends PointerByReference {

public static CFStringRef toCFString(String s) {
final char[] chars = s.toCharArray();
int length = chars.length;
return CarbonAPI.INSTANCE.CFStringCreateWithCharacters(null, chars, CFIndex.valueOf(length));
}

}
50 changes: 50 additions & 0 deletions source/com/barbarysoftware/jna/CarbonAPI.java
@@ -0,0 +1,50 @@
package com.barbarysoftware.jna;

import com.sun.jna.*;

public interface CarbonAPI extends Library {
CarbonAPI INSTANCE = (CarbonAPI) Native.loadLibrary("Carbon", CarbonAPI.class);

CFArrayRef CFArrayCreate(
CFAllocatorRef allocator, // always set to Pointer.NULL
Pointer[] values,
CFIndex numValues,
Void callBacks // always set to Pointer.NULL
);

CFStringRef CFStringCreateWithCharacters(
Void alloc, // always pass NULL
char[] chars,
CFIndex numChars
);

public FSEventStreamRef FSEventStreamCreate(
Pointer v, // always use Pointer.NULL
FSEventStreamCallback callback,
Pointer context, // always use Pointer.NULL
CFArrayRef pathsToWatch,
long sinceWhen, // use -1 for events since now
double latency, // in seconds
int flags // 0 is good for now

);

boolean FSEventStreamStart(FSEventStreamRef streamRef);

void FSEventStreamStop(FSEventStreamRef streamRef);

void FSEventStreamScheduleWithRunLoop(FSEventStreamRef streamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode);

CFRunLoopRef CFRunLoopGetCurrent();

void CFRunLoopRun();

void CFRunLoopStop(CFRunLoopRef rl);

public interface FSEventStreamCallback extends Callback {
@SuppressWarnings({"UnusedDeclaration"})
void invoke(FSEventStreamRef streamRef, Pointer clientCallBackInfo, NativeLong numEvents, Pointer eventPaths, Pointer eventFlags, Pointer eventIds);
}


}
7 changes: 7 additions & 0 deletions source/com/barbarysoftware/jna/FSEventStreamRef.java
@@ -0,0 +1,7 @@
package com.barbarysoftware.jna;

import com.sun.jna.ptr.PointerByReference;

public class FSEventStreamRef extends PointerByReference {

}

0 comments on commit 564d0f0

Please sign in to comment.