Merge of split of masterfs into independent OS modules with most recent version of default branch
1.1 --- a/libs.jna/manifest.mf Sat Dec 24 00:28:54 2011 +0000
1.2 +++ b/libs.jna/manifest.mf Tue Dec 27 19:31:42 2011 +0100
1.3 @@ -3,4 +3,4 @@
1.4 OpenIDE-Module: org.netbeans.libs.jna/1
1.5 OpenIDE-Module-Localizing-Bundle: org/netbeans/libs/jna/Bundle.properties
1.6 AutoUpdate-Essential-Module: true
1.7 -OpenIDE-Module-Specification-Version: 1.17
1.8 +OpenIDE-Module-Specification-Version: 1.18
2.1 --- a/libs.jna/nbproject/project.xml Sat Dec 24 00:28:54 2011 +0000
2.2 +++ b/libs.jna/nbproject/project.xml Tue Dec 27 19:31:42 2011 +0100
2.3 @@ -57,7 +57,10 @@
2.4 <friend>org.netbeans.modules.dlight.nativeexecution</friend>
2.5 <friend>org.netbeans.modules.extexecution.destroy</friend>
2.6 <friend>org.netbeans.modules.keyring.impl</friend>
2.7 - <friend>org.netbeans.modules.masterfs</friend>
2.8 + <friend>org.netbeans.modules.masterfs.windows</friend>
2.9 + <friend>org.netbeans.modules.masterfs.linux</friend>
2.10 + <friend>org.netbeans.modules.masterfs.solaris</friend>
2.11 + <friend>org.netbeans.modules.masterfs.macosx</friend>
2.12 <friend>org.netbeans.modules.python.qshell</friend>
2.13 <friend>org.netbeans.libs.svnClientAdapter.svnkit</friend>
2.14 <package>com.sun.jna</package>
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/masterfs.linux/build.xml Tue Dec 27 19:31:42 2011 +0100
3.3 @@ -0,0 +1,5 @@
3.4 +<?xml version="1.0" encoding="UTF-8"?>
3.5 +<project basedir="." default="netbeans" name="masterfs.linux">
3.6 + <description>Builds, tests, and runs the project org.netbeans.modules.masterfs.linux</description>
3.7 + <import file="../nbbuild/templates/projectized.xml"/>
3.8 +</project>
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/masterfs.linux/manifest.mf Tue Dec 27 19:31:42 2011 +0100
4.3 @@ -0,0 +1,7 @@
4.4 +Manifest-Version: 1.0
4.5 +OpenIDE-Module: org.netbeans.modules.masterfs.linux
4.6 +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/masterfs/watcher/linux/Bundle.properties
4.7 +OpenIDE-Module-Specification-Version: 1.0
4.8 +OpenIDE-Module-Requires: org.openide.modules.os.Linux
4.9 +OpenIDE-Module-Provides: org.netbeans.modules.masterfs.providers.Notifier
4.10 +
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/masterfs.linux/nbproject/project.properties Tue Dec 27 19:31:42 2011 +0100
5.3 @@ -0,0 +1,3 @@
5.4 +is.autoload=true
5.5 +javac.source=1.6
5.6 +javac.compilerargs=-Xlint -Xlint:-serial
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/masterfs.linux/nbproject/project.xml Tue Dec 27 19:31:42 2011 +0100
6.3 @@ -0,0 +1,35 @@
6.4 +<?xml version="1.0" encoding="UTF-8"?>
6.5 +<project xmlns="http://www.netbeans.org/ns/project/1">
6.6 + <type>org.netbeans.modules.apisupport.project</type>
6.7 + <configuration>
6.8 + <data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
6.9 + <code-name-base>org.netbeans.modules.masterfs.linux</code-name-base>
6.10 + <module-dependencies>
6.11 + <dependency>
6.12 + <code-name-base>org.netbeans.libs.jna</code-name-base>
6.13 + <build-prerequisite/>
6.14 + <compile-dependency/>
6.15 + <run-dependency>
6.16 + <release-version>1</release-version>
6.17 + <specification-version>1.18</specification-version>
6.18 + </run-dependency>
6.19 + </dependency>
6.20 + <dependency>
6.21 + <code-name-base>org.netbeans.modules.masterfs</code-name-base>
6.22 + <build-prerequisite/>
6.23 + <compile-dependency/>
6.24 + <run-dependency>
6.25 + <release-version>2</release-version>
6.26 + <specification-version>2.36</specification-version>
6.27 + </run-dependency>
6.28 + </dependency>
6.29 + <dependency>
6.30 + <code-name-base>org.openide.util.lookup</code-name-base>
6.31 + <build-prerequisite/>
6.32 + <compile-dependency/>
6.33 + </dependency>
6.34 + </module-dependencies>
6.35 + <public-packages/>
6.36 + </data>
6.37 + </configuration>
6.38 +</project>
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/masterfs.linux/src/org/netbeans/modules/masterfs/watcher/linux/Bundle.properties Tue Dec 27 19:31:42 2011 +0100
7.3 @@ -0,0 +1,1 @@
7.4 +OpenIDE-Module-Name=Master Filesystem @ Linux
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/masterfs.linux/src/org/netbeans/modules/masterfs/watcher/linux/LinuxNotifier.java Tue Dec 27 19:31:42 2011 +0100
8.3 @@ -0,0 +1,215 @@
8.4 +/*
8.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
8.6 + *
8.7 + * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
8.8 + *
8.9 + * The contents of this file are subject to the terms of either the GNU
8.10 + * General Public License Version 2 only ("GPL") or the Common
8.11 + * Development and Distribution License("CDDL") (collectively, the
8.12 + * "License"). You may not use this file except in compliance with the
8.13 + * License. You can obtain a copy of the License at
8.14 + * http://www.netbeans.org/cddl-gplv2.html
8.15 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
8.16 + * specific language governing permissions and limitations under the
8.17 + * License. When distributing the software, include this License Header
8.18 + * Notice in each file and include the License file at
8.19 + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
8.20 + * particular file as subject to the "Classpath" exception as provided
8.21 + * by Sun in the GPL Version 2 section of the License file that
8.22 + * accompanied this code. If applicable, add the following below the
8.23 + * License Header, with the fields enclosed by brackets [] replaced by
8.24 + * your own identifying information:
8.25 + * "Portions Copyrighted [year] [name of copyright owner]"
8.26 + *
8.27 + * If you wish your version of this file to be governed by only the CDDL
8.28 + * or only the GPL Version 2, indicate your decision by adding
8.29 + * "[Contributor] elects to include this software in this distribution
8.30 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
8.31 + * single choice of license, a recipient has the option to distribute
8.32 + * your version of this file under either the CDDL, the GPL Version 2 or
8.33 + * to extend the choice of license to its licensees as provided above.
8.34 + * However, if you add GPL Version 2 code and therefore, elected the GPL
8.35 + * Version 2 license, then the option applies only if the new code is
8.36 + * made subject to such option by the copyright holder.
8.37 + *
8.38 + * Contributor(s):
8.39 + *
8.40 + * Portions Copyrighted 2010 Sun Microsystems, Inc.
8.41 + */
8.42 +
8.43 +package org.netbeans.modules.masterfs.watcher.linux;
8.44 +
8.45 +import org.netbeans.modules.masterfs.providers.Notifier;
8.46 +import com.sun.jna.Library;
8.47 +import com.sun.jna.Native;
8.48 +import com.sun.jna.NativeLibrary;
8.49 +import java.io.IOException;
8.50 +import java.nio.ByteBuffer;
8.51 +import java.nio.ByteOrder;
8.52 +import java.util.HashMap;
8.53 +import java.util.Map;
8.54 +import java.util.logging.Level;
8.55 +import java.util.logging.Logger;
8.56 +import org.openide.util.lookup.ServiceProvider;
8.57 +
8.58 +/**
8.59 + * A {@link Notifier} implementation based on Linux inotify mechanism.
8.60 + *
8.61 + * @author nenik
8.62 + */
8.63 +@ServiceProvider(service=Notifier.class, position=500)
8.64 +public final class LinuxNotifier extends Notifier<LinuxNotifier.LKey> {
8.65 + private static final Logger LOG = Logger.getLogger(LinuxNotifier.class.getName());
8.66 +
8.67 + private static interface InotifyImpl extends Library {
8.68 + public int inotify_init();
8.69 + public int inotify_init1(int flags);
8.70 + public int close(int fd);
8.71 + public int read(int fd, ByteBuffer buff, int count);
8.72 + public int inotify_add_watch(int fd, String pathname, int mask);
8.73 + public int inotify_rm_watch(int fd, int wd);
8.74 +
8.75 + public static final int O_CLOEXEC = 02000000; //0x80000
8.76 +
8.77 + // Masks
8.78 + public static final int IN_ACCESS = 0x0001; /* File was accessed */
8.79 + public static final int IN_MODIFY = 0x0002; /* File was modified */
8.80 + public static final int IN_ATTRIB = 0x0004; /* Metadata changed */
8.81 + public static final int IN_CLOSE_WRITE = 0x0008; /* Writtable file was closed */
8.82 + public static final int IN_CLOSE_NOWRITE = 0x0010; /* Unwrittable file closed */
8.83 + public static final int IN_OPEN = 0x0020; /* File was opened */
8.84 + public static final int IN_MOVED_FROM = 0x0040; /* File was moved from X */
8.85 + public static final int IN_MOVED_TO = 0x0080; /* File was moved to Y */
8.86 + public static final int IN_CREATE = 0x0100; /* Subfile was created */
8.87 + public static final int IN_DELETE = 0x0200; /* Subfile was deleted */
8.88 + public static final int IN_DELETE_SELF = 0x0400; /* Self was deleted */
8.89 + public static final int IN_MOVE_SELF = 0x0800; /* Self was moved */
8.90 +
8.91 + // additional event masks
8.92 + public static final int IN_UNMOUNT = 0x2000; /* Backing fs was unmounted */
8.93 + public static final int IN_Q_OVERFLOW = 0x4000; /* Event queued overflowed */
8.94 + public static final int IN_IGNORED = 0x8000; /* File was ignored */
8.95 +
8.96 + }
8.97 +
8.98 + final InotifyImpl IMPL;
8.99 + int fd;
8.100 + private ByteBuffer buff = ByteBuffer.allocateDirect(4096);
8.101 +
8.102 + // An array would serve nearly as well
8.103 + private Map<Integer, LKey> map = new HashMap<Integer, LKey>();
8.104 +
8.105 + public LinuxNotifier() {
8.106 + IMPL = (InotifyImpl) Native.loadLibrary("c", InotifyImpl.class);
8.107 + }
8.108 +
8.109 + private String getString(int maxLen) {
8.110 + if (maxLen < 1) return null; // no name field
8.111 + int stop = maxLen - 1;
8.112 + byte[] temp = new byte[maxLen];
8.113 + buff.get(temp);
8.114 + while (temp[stop] == 0) stop--;
8.115 + return new String(temp, 0, stop+1);
8.116 + }
8.117 +
8.118 + @Override public String nextEvent() throws IOException {
8.119 + /* inotify event structure layout:
8.120 + * int wd; // Watch descriptor
8.121 + * uint32_t mask; // Mask of events
8.122 + * uint32_t cookie;// Unique cookie associating related events (for rename(2))
8.123 + * uint32_t len; // Size of name field
8.124 + * char name[];// Optional null-terminated name
8.125 + */
8.126 + while (buff.remaining() < 16 || buff.remaining() < 16 + buff.getInt(buff.position() + 12)) {
8.127 + buff.compact();
8.128 + int len = IMPL.read(fd, buff, buff.remaining());
8.129 +
8.130 + if (len <= 0) {
8.131 + // lazily get a thread local errno
8.132 + int errno = NativeLibrary.getInstance("c").getFunction("errno").getInt(0);
8.133 + if (errno == 4) { // EINTR
8.134 + buff.flip();
8.135 + continue; // restart the I/O
8.136 + } else {
8.137 + throw new IOException("error reading from inotify: " + errno);
8.138 + }
8.139 + }
8.140 + buff.position(buff.position() + len);
8.141 + buff.flip();
8.142 + }
8.143 +
8.144 + // now we have enough data in the buffer
8.145 + int wd = buff.getInt();
8.146 + int mask = buff.getInt();
8.147 + int cookie = buff.getInt();
8.148 + int len = buff.getInt();
8.149 + String name = getString(len); // ignore
8.150 +
8.151 + LKey key = map.get(wd);
8.152 + if (key == null) { /* wd == -1 -> Queue overflow */
8.153 + return null;
8.154 + }
8.155 +
8.156 + return key.path;
8.157 + }
8.158 +
8.159 +
8.160 + static class LKey {
8.161 + int id;
8.162 + String path;
8.163 +
8.164 + public LKey(int id, String path) {
8.165 + this.id = id;
8.166 + this.path = path;
8.167 + }
8.168 +
8.169 + @Override
8.170 + public String toString() {
8.171 + return "LKey[" + id + " - '" + path + "']";
8.172 + }
8.173 + }
8.174 +
8.175 + @Override
8.176 + protected void start() throws IOException {
8.177 + buff.position(buff.capacity()); // make the buffer empty
8.178 + buff.order(ByteOrder.nativeOrder());
8.179 + fd = IMPL.inotify_init1(InotifyImpl.O_CLOEXEC);
8.180 + if (fd < 0) {
8.181 + LOG.log(
8.182 + Level.INFO, "Linux kernel {0} returned {1} from inotify_init1",
8.183 + new Object[]{System.getProperty("os.version"), fd});
8.184 + fd = IMPL.inotify_init();
8.185 + LOG.log(Level.INFO, "Trying inotify_init: {0}", fd);
8.186 + }
8.187 + if (fd < 0) {
8.188 + throw new IOException("inotify_init failed: " + fd);
8.189 + }
8.190 + }
8.191 +
8.192 + @Override public LKey addWatch(String path) throws IOException {
8.193 + // what if the file doesn't exist?
8.194 + int id = IMPL.inotify_add_watch(fd, path,
8.195 + InotifyImpl.IN_CREATE | InotifyImpl.IN_MOVED_TO |
8.196 + InotifyImpl.IN_DELETE | InotifyImpl.IN_MOVED_FROM |
8.197 + InotifyImpl.IN_MODIFY | InotifyImpl.IN_ATTRIB);
8.198 + //XXX handle error return value (-1)
8.199 + LOG.log(Level.FINEST, "addWatch{0} res: {1}", new Object[]{path, id});
8.200 + if (id <= 0) {
8.201 + // 28 == EINOSPC
8.202 + int errno = NativeLibrary.getInstance("c").getFunction("errno").getInt(0); // NOI18N
8.203 + throw new IOException("addWatch on " + path + " errno: " + errno); // NOI18N
8.204 + }
8.205 +
8.206 + LKey newKey = map.get(id);
8.207 + if (newKey == null) {
8.208 + newKey = new LKey(id, path);
8.209 + map.put(id, newKey);
8.210 + }
8.211 + return newKey;
8.212 + }
8.213 +
8.214 + @Override public void removeWatch(LKey lkey) {
8.215 + map.remove(lkey.id);
8.216 + IMPL.inotify_rm_watch(fd, lkey.id);
8.217 + }
8.218 +}
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
9.2 +++ b/masterfs.macosx/build.xml Tue Dec 27 19:31:42 2011 +0100
9.3 @@ -0,0 +1,5 @@
9.4 +<?xml version="1.0" encoding="UTF-8"?>
9.5 +<project basedir="." default="netbeans" name="masterfs.macosx">
9.6 + <description>Builds, tests, and runs the project org.netbeans.modules.masterfs.macosx</description>
9.7 + <import file="../nbbuild/templates/projectized.xml"/>
9.8 +</project>
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
10.2 +++ b/masterfs.macosx/manifest.mf Tue Dec 27 19:31:42 2011 +0100
10.3 @@ -0,0 +1,7 @@
10.4 +Manifest-Version: 1.0
10.5 +OpenIDE-Module: org.netbeans.modules.masterfs.macosx
10.6 +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/masterfs/watcher/macosx/Bundle.properties
10.7 +OpenIDE-Module-Specification-Version: 1.0
10.8 +OpenIDE-Module-Requires: org.openide.modules.os.MacOSX
10.9 +OpenIDE-Module-Provides: org.netbeans.modules.masterfs.providers.Notifier
10.10 +
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
11.2 +++ b/masterfs.macosx/nbproject/project.properties Tue Dec 27 19:31:42 2011 +0100
11.3 @@ -0,0 +1,3 @@
11.4 +is.autoload=true
11.5 +javac.source=1.6
11.6 +javac.compilerargs=-Xlint -Xlint:-serial
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
12.2 +++ b/masterfs.macosx/nbproject/project.xml Tue Dec 27 19:31:42 2011 +0100
12.3 @@ -0,0 +1,35 @@
12.4 +<?xml version="1.0" encoding="UTF-8"?>
12.5 +<project xmlns="http://www.netbeans.org/ns/project/1">
12.6 + <type>org.netbeans.modules.apisupport.project</type>
12.7 + <configuration>
12.8 + <data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
12.9 + <code-name-base>org.netbeans.modules.masterfs.macosx</code-name-base>
12.10 + <module-dependencies>
12.11 + <dependency>
12.12 + <code-name-base>org.netbeans.libs.jna</code-name-base>
12.13 + <build-prerequisite/>
12.14 + <compile-dependency/>
12.15 + <run-dependency>
12.16 + <release-version>1</release-version>
12.17 + <specification-version>1.18</specification-version>
12.18 + </run-dependency>
12.19 + </dependency>
12.20 + <dependency>
12.21 + <code-name-base>org.netbeans.modules.masterfs</code-name-base>
12.22 + <build-prerequisite/>
12.23 + <compile-dependency/>
12.24 + <run-dependency>
12.25 + <release-version>2</release-version>
12.26 + <specification-version>2.36</specification-version>
12.27 + </run-dependency>
12.28 + </dependency>
12.29 + <dependency>
12.30 + <code-name-base>org.openide.util.lookup</code-name-base>
12.31 + <build-prerequisite/>
12.32 + <compile-dependency/>
12.33 + </dependency>
12.34 + </module-dependencies>
12.35 + <public-packages/>
12.36 + </data>
12.37 + </configuration>
12.38 +</project>
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
13.2 +++ b/masterfs.macosx/src/org/netbeans/modules/masterfs/watcher/macosx/Bundle.properties Tue Dec 27 19:31:42 2011 +0100
13.3 @@ -0,0 +1,1 @@
13.4 +OpenIDE-Module-Name=Master Filesystem @ Mac OS X
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
14.2 +++ b/masterfs.macosx/src/org/netbeans/modules/masterfs/watcher/macosx/OSXNotifier.java Tue Dec 27 19:31:42 2011 +0100
14.3 @@ -0,0 +1,293 @@
14.4 +/*
14.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
14.6 + *
14.7 + * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
14.8 + *
14.9 + * The contents of this file are subject to the terms of either the GNU
14.10 + * General Public License Version 2 only ("GPL") or the Common
14.11 + * Development and Distribution License("CDDL") (collectively, the
14.12 + * "License"). You may not use this file except in compliance with the
14.13 + * License. You can obtain a copy of the License at
14.14 + * http://www.netbeans.org/cddl-gplv2.html
14.15 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
14.16 + * specific language governing permissions and limitations under the
14.17 + * License. When distributing the software, include this License Header
14.18 + * Notice in each file and include the License file at
14.19 + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
14.20 + * particular file as subject to the "Classpath" exception as provided
14.21 + * by Sun in the GPL Version 2 section of the License file that
14.22 + * accompanied this code. If applicable, add the following below the
14.23 + * License Header, with the fields enclosed by brackets [] replaced by
14.24 + * your own identifying information:
14.25 + * "Portions Copyrighted [year] [name of copyright owner]"
14.26 + *
14.27 + * If you wish your version of this file to be governed by only the CDDL
14.28 + * or only the GPL Version 2, indicate your decision by adding
14.29 + * "[Contributor] elects to include this software in this distribution
14.30 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
14.31 + * single choice of license, a recipient has the option to distribute
14.32 + * your version of this file under either the CDDL, the GPL Version 2 or
14.33 + * to extend the choice of license to its licensees as provided above.
14.34 + * However, if you add GPL Version 2 code and therefore, elected the GPL
14.35 + * Version 2 license, then the option applies only if the new code is
14.36 + * made subject to such option by the copyright holder.
14.37 + *
14.38 + * Contributor(s):
14.39 + *
14.40 + * Portions Copyrighted 2010 Sun Microsystems, Inc.
14.41 + */
14.42 +
14.43 +package org.netbeans.modules.masterfs.watcher.macosx;
14.44 +
14.45 +import org.netbeans.modules.masterfs.providers.Notifier;
14.46 +import com.sun.jna.Callback;
14.47 +import com.sun.jna.Library;
14.48 +import com.sun.jna.Native;
14.49 +import com.sun.jna.NativeLong;
14.50 +import com.sun.jna.Pointer;
14.51 +import java.io.IOException;
14.52 +import java.io.InterruptedIOException;
14.53 +import java.util.concurrent.BlockingQueue;
14.54 +import java.util.concurrent.Exchanger;
14.55 +import java.util.concurrent.ExecutorService;
14.56 +import java.util.concurrent.Executors;
14.57 +import java.util.concurrent.LinkedBlockingQueue;
14.58 +import java.util.concurrent.ThreadFactory;
14.59 +import java.util.logging.Level;
14.60 +import java.util.logging.Logger;
14.61 +import org.openide.util.lookup.ServiceProvider;
14.62 +
14.63 +/**
14.64 + *
14.65 + * @author Tomas Zezula
14.66 + */
14.67 +@ServiceProvider(service=Notifier.class, position=300)
14.68 +public final class OSXNotifier extends Notifier<Void> {
14.69 + private static final Level DEBUG_LOG_LEVEL = Level.FINE;
14.70 + private static final Level PERF_LOG_LEVEL = Level.FINE;
14.71 + private static final long kFSEventStreamEventIdSinceNow = 0xFFFFFFFFFFFFFFFFL;
14.72 + private static final int kFSEventStreamCreateFlagNoDefer = 0x00000002;
14.73 + private static final int kFSEventStreamEventFlagMustScanSubDirs = 0x00000001;
14.74 + private static final int kFSEventStreamEventFlagMount = 0x00000040;
14.75 + private static final int kFSEventStreamEventFlagUnmount = 0x00000080;
14.76 + private static final double LATENCY = 1.0f;
14.77 + private static final int ENC_MAC_ROMAN = 0;
14.78 + private static final String DEFAULT_RUN_LOOP_MODE = "kCFRunLoopDefaultMode"; //NOI18N
14.79 + private static final Logger LOG = Logger.getLogger(OSXNotifier.class.getName());
14.80 + private final CoreFoundation cf;
14.81 + private final CoreServices cs;
14.82 + private final EventCallback callback;
14.83 + private final BlockingQueue<String> events;
14.84 + //@GuardedBy("this")
14.85 + private ExecutorService worker;
14.86 + //@GuardedBy("this")
14.87 + private Pointer[] rtData;
14.88 +
14.89 + private static final String ALL_CHANGE = "ALL-CHANGE"; //xxx - shouldn't be global in Notifier rather than using null?
14.90 +
14.91 + public OSXNotifier() {
14.92 + cf = (CoreFoundation) Native.loadLibrary("CoreFoundation",CoreFoundation.class); //NOI18N
14.93 + cs = (CoreServices) Native.loadLibrary("CoreServices",CoreServices.class); //NOI18N
14.94 + callback = new EventCallbackImpl();
14.95 + events = new LinkedBlockingQueue<String>();
14.96 + }
14.97 +
14.98 +
14.99 + public @Override Void addWatch(String path) throws IOException {
14.100 + return null;
14.101 + }
14.102 +
14.103 + public @Override void removeWatch(Void key) throws IOException {
14.104 + // ignore
14.105 + }
14.106 +
14.107 + public @Override String nextEvent() throws IOException, InterruptedException {
14.108 + final String event = events.take();
14.109 + return event == ALL_CHANGE ? null : event;
14.110 + }
14.111 +
14.112 + public synchronized void start() throws IOException {
14.113 + if (worker != null) {
14.114 + throw new IllegalStateException("FileSystemWatcher already started."); //NOI18N
14.115 + }
14.116 + worker = Executors.newSingleThreadExecutor(new DaemonThreadFactory());
14.117 + final Exchanger<Object> exchanger = new Exchanger<Object>();
14.118 + worker.execute(new Runnable() {
14.119 + public @Override void run () {
14.120 + try {
14.121 + Pointer[] _rtData = null;
14.122 + try {
14.123 + _rtData = createFSEventStream();
14.124 + } catch (Throwable ex) {
14.125 + exchanger.exchange(ex);
14.126 + } finally {
14.127 + if (_rtData != null) {
14.128 + exchanger.exchange(_rtData);
14.129 + cf.CFRunLoopRun();
14.130 + }
14.131 + }
14.132 + } catch (InterruptedException ie) {
14.133 + LOG.log(Level.WARNING, "Watcher interruped during start", ie); //NOI18N
14.134 + }
14.135 + }
14.136 + });
14.137 + final Object _data;
14.138 + try {
14.139 + _data = exchanger.exchange(null);
14.140 + } catch (InterruptedException ex) {
14.141 + throw (InterruptedIOException)new InterruptedIOException().initCause(ex);
14.142 + }
14.143 + assert _data != null;
14.144 + if (_data instanceof Throwable) {
14.145 + worker.shutdown();
14.146 + worker = null;
14.147 + throw new IOException((Throwable)_data);
14.148 + } else {
14.149 + rtData = (Pointer[]) _data;
14.150 + }
14.151 + }
14.152 +
14.153 + @Override
14.154 + public synchronized void stop() throws IOException {
14.155 + if (worker == null) {
14.156 + throw new IllegalStateException("FileSystemWatcher is not started."); //NOI18N
14.157 + }
14.158 + assert rtData != null;
14.159 + assert rtData.length == 2;
14.160 + assert rtData[0] != null;
14.161 + assert rtData[1] != null;
14.162 + cs.FSEventStreamStop(rtData[0]);
14.163 + cs.FSEventStreamInvalidate(rtData[0]);
14.164 + cs.FSEventStreamRelease(rtData[0]);
14.165 + cf.CFRunLoopStop(rtData[1]);
14.166 + worker.shutdown();
14.167 + worker = null;
14.168 + rtData = null;
14.169 + }
14.170 +
14.171 + private Pointer[] createFSEventStream() throws IOException {
14.172 + final Pointer root = cf.CFStringCreateWithCString(Pointer.NULL,"/",ENC_MAC_ROMAN); //NOI18N
14.173 + if (root == Pointer.NULL) {
14.174 + throw new IOException("Path creation failed."); //NOI18N
14.175 + }
14.176 + final Pointer arr = cf.CFArrayCreateMutable(Pointer.NULL, new NativeLong(1), Pointer.NULL);
14.177 + if (arr == Pointer.NULL) {
14.178 + throw new IOException("Path list creation failed."); //NOI18N
14.179 + }
14.180 + cf.CFArrayAppendValue(arr, root);
14.181 +
14.182 + final Pointer eventStream = cs.FSEventStreamCreate(Pointer.NULL, callback, Pointer.NULL, arr, kFSEventStreamEventIdSinceNow, LATENCY, kFSEventStreamCreateFlagNoDefer);
14.183 + if (eventStream == Pointer.NULL) {
14.184 + throw new IOException("Creation of FSEventStream failed."); //NOI18N
14.185 + }
14.186 + final Pointer loop = cf.CFRunLoopGetCurrent();
14.187 + if (eventStream == Pointer.NULL) {
14.188 + throw new IOException("Cannot find run loop for caller."); //NOI18N
14.189 + }
14.190 + final Pointer kCFRunLoopDefaultMode = findDefaultMode(loop);
14.191 + if (kCFRunLoopDefaultMode == null) {
14.192 + throw new IOException("Caller has no defaul run loop mode."); //NOI18N
14.193 + }
14.194 + cs.FSEventStreamScheduleWithRunLoop(eventStream, loop, kCFRunLoopDefaultMode);
14.195 + if (LOG.isLoggable(DEBUG_LOG_LEVEL)) {
14.196 + LOG.log(DEBUG_LOG_LEVEL, getStreamDescription(eventStream));
14.197 + }
14.198 + cs.FSEventStreamStart(eventStream);
14.199 + return new Pointer[] {eventStream, loop};
14.200 + }
14.201 +
14.202 + private Pointer findDefaultMode(final Pointer runLoop) {
14.203 + final Pointer modes = cf.CFRunLoopCopyAllModes(runLoop);
14.204 + if (modes != Pointer.NULL) {
14.205 + final int modesCount = cf.CFArrayGetCount(modes).intValue();
14.206 + for (int i=0; i< modesCount; i++) {
14.207 + final Pointer mode = cf.CFArrayGetValueAtIndex(modes, new NativeLong(i));
14.208 + if (mode != Pointer.NULL && DEFAULT_RUN_LOOP_MODE.equals(cf.CFStringGetCStringPtr(mode, ENC_MAC_ROMAN))) {
14.209 + return mode;
14.210 + }
14.211 + }
14.212 + }
14.213 + return null;
14.214 + }
14.215 +
14.216 + private String getStreamDescription(final Pointer eventStream) {
14.217 + final Pointer desc = cs.FSEventStreamCopyDescription(eventStream);
14.218 + return desc == Pointer.NULL ? "" : cf.CFStringGetCStringPtr(desc, ENC_MAC_ROMAN); //NOI18N
14.219 + }
14.220 +
14.221 + public static interface EventCallback extends Callback {
14.222 + void invoke(Pointer streamRef,
14.223 + Pointer clientCallBackInfo,
14.224 + NativeLong numEvents,
14.225 + Pointer eventPaths,
14.226 + Pointer eventFlags,
14.227 + Pointer eventIds);
14.228 + }
14.229 +
14.230 + public static interface CoreFoundation extends Library {
14.231 + Pointer CFRunLoopGetCurrent();
14.232 + void CFRunLoopRun();
14.233 + void CFRunLoopStop(Pointer loop);
14.234 + Pointer CFRunLoopCopyAllModes(Pointer loop);
14.235 +
14.236 + Pointer CFArrayCreateMutable(Pointer allocator, NativeLong size, Pointer callback);
14.237 + void CFArrayAppendValue(Pointer theArray, Pointer value);
14.238 + Pointer CFArrayGetValueAtIndex(Pointer theArray, NativeLong index);
14.239 + NativeLong CFArrayGetCount(Pointer theArray);
14.240 +
14.241 + Pointer CFStringCreateWithCString(Pointer allocator, String string, int encoding);
14.242 + String CFStringGetCStringPtr(Pointer theString, int encoding);
14.243 + }
14.244 +
14.245 + public static interface CoreServices extends Library {
14.246 + Pointer FSEventStreamCreate(Pointer allocator, EventCallback callback, Pointer ctx, Pointer pathsToWatch, long sinceWhen, double latency, int flags);
14.247 + Pointer FSEventStreamCopyDescription(Pointer stream);
14.248 + void FSEventStreamScheduleWithRunLoop(Pointer stream, Pointer loop, Pointer mode);
14.249 + void FSEventStreamUnscheduleFromRunLoop(Pointer stream, Pointer loop, Pointer mode);
14.250 + void FSEventStreamStart(Pointer stream);
14.251 + void FSEventStreamStop(Pointer stream);
14.252 + void FSEventStreamInvalidate(Pointer stream);
14.253 + void FSEventStreamRelease(Pointer stream);
14.254 + }
14.255 +
14.256 + private static class DaemonThreadFactory implements ThreadFactory {
14.257 + @Override
14.258 + public Thread newThread(Runnable r) {
14.259 + final Thread t = new Thread(r);
14.260 + t.setDaemon(true);
14.261 + return t;
14.262 + }
14.263 +
14.264 + }
14.265 +
14.266 + private class EventCallbackImpl implements EventCallback {
14.267 + @Override
14.268 + public void invoke(Pointer streamRef, Pointer clientCallBackInfo, NativeLong numEvents, Pointer eventPaths, Pointer eventFlags, Pointer eventIds) {
14.269 + final long st = System.currentTimeMillis();
14.270 + final int length = numEvents.intValue();
14.271 + final Pointer[] pointers = eventPaths.getPointerArray(0, length);
14.272 + int flags[];
14.273 + if (eventFlags == null) {
14.274 + flags = new int[length];
14.275 + LOG.log(DEBUG_LOG_LEVEL, "FSEventStreamCallback eventFlags == null, expected int[] of size {0}", length); //NOI18N
14.276 + } else {
14.277 + flags = eventFlags.getIntArray(0, length);
14.278 + }
14.279 + for (int i=0; i<length; i++) {
14.280 + final Pointer p = pointers[i];
14.281 + int flag = flags[i];
14.282 + final String path = p.getString(0);
14.283 +
14.284 + if ((flag & kFSEventStreamEventFlagMustScanSubDirs) == kFSEventStreamEventFlagMustScanSubDirs ||
14.285 + (flag & kFSEventStreamEventFlagMount) == kFSEventStreamEventFlagMount ||
14.286 + (flag & kFSEventStreamEventFlagUnmount) == kFSEventStreamEventFlagUnmount) {
14.287 + events.add(ALL_CHANGE);
14.288 + } else {
14.289 + events.add(path);
14.290 + }
14.291 + LOG.log(DEBUG_LOG_LEVEL, "Event on {0}", new Object[]{path});
14.292 + }
14.293 + LOG.log(PERF_LOG_LEVEL, "Callback time: {0}", (System.currentTimeMillis() - st));
14.294 + }
14.295 + }
14.296 +}
15.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
15.2 +++ b/masterfs.solaris/build.xml Tue Dec 27 19:31:42 2011 +0100
15.3 @@ -0,0 +1,5 @@
15.4 +<?xml version="1.0" encoding="UTF-8"?>
15.5 +<project basedir="." default="netbeans" name="masterfs.solaris">
15.6 + <description>Builds, tests, and runs the project org.netbeans.modules.masterfs.solaris</description>
15.7 + <import file="../nbbuild/templates/projectized.xml"/>
15.8 +</project>
16.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
16.2 +++ b/masterfs.solaris/manifest.mf Tue Dec 27 19:31:42 2011 +0100
16.3 @@ -0,0 +1,6 @@
16.4 +Manifest-Version: 1.0
16.5 +OpenIDE-Module: org.netbeans.modules.masterfs.solaris
16.6 +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/masterfs/watcher/solaris/Bundle.properties
16.7 +OpenIDE-Module-Specification-Version: 1.0
16.8 +OpenIDE-Module-Requires: org.openide.modules.os.Solaris
16.9 +OpenIDE-Module-Provides: org.netbeans.modules.masterfs.providers.Notifier
17.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
17.2 +++ b/masterfs.solaris/nbproject/project.properties Tue Dec 27 19:31:42 2011 +0100
17.3 @@ -0,0 +1,3 @@
17.4 +is.autoload=true
17.5 +javac.source=1.6
17.6 +javac.compilerargs=-Xlint -Xlint:-serial
18.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
18.2 +++ b/masterfs.solaris/nbproject/project.xml Tue Dec 27 19:31:42 2011 +0100
18.3 @@ -0,0 +1,35 @@
18.4 +<?xml version="1.0" encoding="UTF-8"?>
18.5 +<project xmlns="http://www.netbeans.org/ns/project/1">
18.6 + <type>org.netbeans.modules.apisupport.project</type>
18.7 + <configuration>
18.8 + <data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
18.9 + <code-name-base>org.netbeans.modules.masterfs.solaris</code-name-base>
18.10 + <module-dependencies>
18.11 + <dependency>
18.12 + <code-name-base>org.netbeans.libs.jna</code-name-base>
18.13 + <build-prerequisite/>
18.14 + <compile-dependency/>
18.15 + <run-dependency>
18.16 + <release-version>1</release-version>
18.17 + <specification-version>1.18</specification-version>
18.18 + </run-dependency>
18.19 + </dependency>
18.20 + <dependency>
18.21 + <code-name-base>org.netbeans.modules.masterfs</code-name-base>
18.22 + <build-prerequisite/>
18.23 + <compile-dependency/>
18.24 + <run-dependency>
18.25 + <release-version>2</release-version>
18.26 + <specification-version>2.36</specification-version>
18.27 + </run-dependency>
18.28 + </dependency>
18.29 + <dependency>
18.30 + <code-name-base>org.openide.util.lookup</code-name-base>
18.31 + <build-prerequisite/>
18.32 + <compile-dependency/>
18.33 + </dependency>
18.34 + </module-dependencies>
18.35 + <public-packages/>
18.36 + </data>
18.37 + </configuration>
18.38 +</project>
19.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
19.2 +++ b/masterfs.solaris/src/org/netbeans/modules/masterfs/watcher/solaris/Bundle.properties Tue Dec 27 19:31:42 2011 +0100
19.3 @@ -0,0 +1,1 @@
19.4 +OpenIDE-Module-Name=Master Filesystem @ Solaris
20.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
20.2 +++ b/masterfs.solaris/src/org/netbeans/modules/masterfs/watcher/solaris/FAMNotifier.java Tue Dec 27 19:31:42 2011 +0100
20.3 @@ -0,0 +1,244 @@
20.4 +/*
20.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
20.6 + *
20.7 + * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
20.8 + *
20.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
20.10 + * Other names may be trademarks of their respective owners.
20.11 + *
20.12 + * The contents of this file are subject to the terms of either the GNU
20.13 + * General Public License Version 2 only ("GPL") or the Common
20.14 + * Development and Distribution License("CDDL") (collectively, the
20.15 + * "License"). You may not use this file except in compliance with the
20.16 + * License. You can obtain a copy of the License at
20.17 + * http://www.netbeans.org/cddl-gplv2.html
20.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
20.19 + * specific language governing permissions and limitations under the
20.20 + * License. When distributing the software, include this License Header
20.21 + * Notice in each file and include the License file at
20.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
20.23 + * particular file as subject to the "Classpath" exception as provided
20.24 + * by Oracle in the GPL Version 2 section of the License file that
20.25 + * accompanied this code. If applicable, add the following below the
20.26 + * License Header, with the fields enclosed by brackets [] replaced by
20.27 + * your own identifying information:
20.28 + * "Portions Copyrighted [year] [name of copyright owner]"
20.29 + *
20.30 + * If you wish your version of this file to be governed by only the CDDL
20.31 + * or only the GPL Version 2, indicate your decision by adding
20.32 + * "[Contributor] elects to include this software in this distribution
20.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
20.34 + * single choice of license, a recipient has the option to distribute
20.35 + * your version of this file under either the CDDL, the GPL Version 2 or
20.36 + * to extend the choice of license to its licensees as provided above.
20.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
20.38 + * Version 2 license, then the option applies only if the new code is
20.39 + * made subject to such option by the copyright holder.
20.40 + *
20.41 + * Contributor(s):
20.42 + *
20.43 + * Portions Copyrighted 2010 Sun Microsystems, Inc.
20.44 + */
20.45 +
20.46 +package org.netbeans.modules.masterfs.watcher.solaris;
20.47 +
20.48 +import org.netbeans.modules.masterfs.providers.Notifier;
20.49 +import com.sun.jna.Library;
20.50 +import com.sun.jna.Native;
20.51 +import com.sun.jna.Pointer;
20.52 +import com.sun.jna.Structure;
20.53 +import com.sun.jna.Structure.ByReference;
20.54 +import java.io.IOException;
20.55 +import java.util.Collections;
20.56 +import java.util.HashMap;
20.57 +import java.util.HashSet;
20.58 +import java.util.Map;
20.59 +import java.util.Set;
20.60 +import java.util.concurrent.BlockingQueue;
20.61 +import java.util.concurrent.LinkedBlockingQueue;
20.62 +import java.util.logging.Level;
20.63 +import java.util.logging.Logger;
20.64 +import org.openide.util.lookup.ServiceProvider;
20.65 +
20.66 +/**
20.67 + * Notifier implementation using fam library
20.68 + *
20.69 + * @author Egor Ushakov
20.70 + */
20.71 +@ServiceProvider(service=Notifier.class, position=1000)
20.72 +public final class FAMNotifier extends Notifier<Integer> {
20.73 + private final FAMLibrary.FAMConnection conn;
20.74 + private final FAMLibrary lib;
20.75 + private final Map<Integer, String> map = Collections.synchronizedMap(new HashMap<Integer, String>());
20.76 +
20.77 + private final BlockingQueue<String> events = new LinkedBlockingQueue<String>();
20.78 + private final Thread eventReader;
20.79 + private volatile boolean stopped = false;
20.80 +
20.81 + // limit unanswered requests to avoid FAM hang, see IZ 199497
20.82 + private static int ACTIVE_REQUESTS_LIMIT = 100;
20.83 + private final Set<Integer> activeRequests = Collections.synchronizedSet(new HashSet<Integer>());
20.84 +
20.85 + private static final Logger LOG = Logger.getLogger(FAMNotifier.class.getName());
20.86 +
20.87 + public FAMNotifier() {
20.88 + if (Boolean.getBoolean("org.netbeans.modules.masterfs.watcher.FAM")) {
20.89 + conn = null;
20.90 + lib = null;
20.91 + eventReader = null;
20.92 + return;
20.93 + }
20.94 + FAMLibrary library;
20.95 + try {
20.96 + // first try gamin
20.97 + library = (FAMLibrary) Native.loadLibrary("gamin-1", FAMLibrary.class); //NOI18N
20.98 + } catch (LinkageError x) {
20.99 + // then fam
20.100 + library = (FAMLibrary) Native.loadLibrary("fam", FAMLibrary.class); //NOI18N
20.101 + }
20.102 + this.lib = library;
20.103 + this.conn = new FAMLibrary.FAMConnection();
20.104 + eventReader = new Thread(new Runnable() {
20.105 + @Override
20.106 + public void run() {
20.107 + while (!stopped) {
20.108 + while (lib.FAMPending(conn) > 0) {
20.109 + FAMLibrary.FAMEvent evt = new FAMLibrary.FAMEvent();
20.110 + if (lib.FAMNextEvent(conn, evt) != -1) {
20.111 + if (evt.code == FAMLibrary.FAMEndExist || evt.code == FAMLibrary.FAMAcknowledge) {
20.112 + synchronized (activeRequests) {
20.113 + activeRequests.remove(evt.fr.reqnum);
20.114 + activeRequests.notifyAll();
20.115 + }
20.116 + }
20.117 + String path = map.get(evt.fr.reqnum);
20.118 + if (path != null) {
20.119 + events.add(path);
20.120 + }
20.121 + }
20.122 + }
20.123 + // now sleep
20.124 + try {
20.125 + Thread.sleep(1000);
20.126 + } catch (InterruptedException ex) {
20.127 + }
20.128 + }
20.129 + }
20.130 + }, "FAM events reader"); //NOI18N
20.131 + }
20.132 +
20.133 + @Override
20.134 + public Integer addWatch(String path) throws IOException {
20.135 + startRequest();
20.136 + FAMLibrary.FAMRequest request = new FAMLibrary.FAMRequest();
20.137 + lib.FAMMonitorDirectory(conn, path, request, null);
20.138 + LOG.log(Level.FINEST, "addWatch {0}({1}), queue length: {2}", new Object[]{path, request.reqnum, activeRequests.size()});
20.139 + activeRequests.add(request.reqnum);
20.140 + map.put(request.reqnum, path);
20.141 + return request.reqnum;
20.142 + }
20.143 +
20.144 + private void startRequest() {
20.145 + synchronized (activeRequests) {
20.146 + while (activeRequests.size() > ACTIVE_REQUESTS_LIMIT) {
20.147 + long start = System.currentTimeMillis();
20.148 + try {
20.149 + activeRequests.wait();
20.150 + } catch (InterruptedException ex) {
20.151 + }
20.152 + LOG.warning("Blocking FAM requests for " + (System.currentTimeMillis()-start) + "ms, requests queue is full"); //NOI18N
20.153 + }
20.154 + }
20.155 + }
20.156 +
20.157 + @Override
20.158 + public String nextEvent() throws IOException, InterruptedException {
20.159 + return events.take();
20.160 + }
20.161 +
20.162 + @Override
20.163 + public void removeWatch(Integer key) throws IOException {
20.164 + LOG.log(Level.FINEST, "removeWatch {0}({1}), queue length: {2}", new Object[]{map.get(key), key, activeRequests.size()});
20.165 + startRequest();
20.166 + activeRequests.add(key);
20.167 + lib.FAMCancelMonitor(conn, new FAMLibrary.FAMRequest(key));
20.168 + map.remove(key);
20.169 + }
20.170 +
20.171 + @Override
20.172 + protected void start() throws IOException {
20.173 + if (lib == null || lib.FAMOpen(conn) != 0) {
20.174 + throw new IOException();
20.175 + }
20.176 + eventReader.start();
20.177 + }
20.178 +
20.179 + @Override
20.180 + protected void stop() throws IOException {
20.181 + stopped = true;
20.182 + try {
20.183 + eventReader.join(2000);
20.184 + } catch (InterruptedException ex) {
20.185 + }
20.186 + lib.FAMClose(conn);
20.187 + }
20.188 +
20.189 + interface FAMLibrary extends Library {
20.190 + static class FAMConnection extends Structure {
20.191 + public static class Reference extends FAMConnection implements ByReference {}
20.192 + public int fd;
20.193 + public Pointer client;
20.194 + }
20.195 +
20.196 + static class FAMRequest extends Structure {
20.197 +
20.198 + public FAMRequest() {
20.199 + super();
20.200 + }
20.201 +
20.202 + public FAMRequest(int reqnum) {
20.203 + super();
20.204 + this.reqnum = reqnum;
20.205 + }
20.206 +
20.207 + public int reqnum;
20.208 + };
20.209 +
20.210 + public static final int PATH_MAX = 1024;
20.211 +
20.212 + public static final int FAMChanged = 1;
20.213 + public static final int FAMDeleted = 2;
20.214 + public static final int FAMStartExecuting = 3;
20.215 + public static final int FAMStopExecuting = 4;
20.216 + public static final int FAMCreated = 5;
20.217 + public static final int FAMMoved = 6;
20.218 + public static final int FAMAcknowledge = 7;
20.219 + public static final int FAMExists = 8;
20.220 + public static final int FAMEndExist = 9;
20.221 +
20.222 + static class FAMEvent extends Structure {
20.223 + public FAMConnection.Reference fc; /* The fam connection that event occurred on */
20.224 + public FAMRequest fr; /* Corresponds to the FamRequest from monitor */
20.225 + public String hostname; /* host and filename - pointer to which */
20.226 + public byte[] filename = new byte[PATH_MAX]; /* file changed */
20.227 + public Pointer userdata; /* userdata associated with this monitor req. */
20.228 + public int code; /* What happened to file - see above */
20.229 + }
20.230 +
20.231 +
20.232 + int FAMOpen(FAMConnection fc);
20.233 + int FAMClose(FAMConnection fc);
20.234 +
20.235 + int FAMMonitorDirectory(FAMConnection fc,
20.236 + String filename,
20.237 + FAMRequest fr,
20.238 + Pointer userData);
20.239 + int FAMCancelMonitor(FAMConnection fc,
20.240 + FAMRequest fr);
20.241 +
20.242 + int FAMPending(FAMConnection fc);
20.243 + int FAMNextEvent(FAMConnection fc,
20.244 + FAMEvent fe);
20.245 + }
20.246 +
20.247 +}
21.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
21.2 +++ b/masterfs.windows/build.xml Tue Dec 27 19:31:42 2011 +0100
21.3 @@ -0,0 +1,5 @@
21.4 +<?xml version="1.0" encoding="UTF-8"?>
21.5 +<project basedir="." default="netbeans" name="masterfs.windows">
21.6 + <description>Builds, tests, and runs the project org.netbeans.modules.masterfs.windows</description>
21.7 + <import file="../nbbuild/templates/projectized.xml"/>
21.8 +</project>
22.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
22.2 +++ b/masterfs.windows/manifest.mf Tue Dec 27 19:31:42 2011 +0100
22.3 @@ -0,0 +1,7 @@
22.4 +Manifest-Version: 1.0
22.5 +OpenIDE-Module: org.netbeans.modules.masterfs.windows
22.6 +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/masterfs/watcher/windows/Bundle.properties
22.7 +OpenIDE-Module-Requires: org.openide.modules.os.Windows
22.8 +OpenIDE-Module-Provides: org.netbeans.modules.masterfs.providers.Notifier
22.9 +OpenIDE-Module-Specification-Version: 1.0
22.10 +
23.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
23.2 +++ b/masterfs.windows/nbproject/project.properties Tue Dec 27 19:31:42 2011 +0100
23.3 @@ -0,0 +1,3 @@
23.4 +is.autoload=true
23.5 +javac.source=1.6
23.6 +javac.compilerargs=-Xlint -Xlint:-serial
24.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
24.2 +++ b/masterfs.windows/nbproject/project.xml Tue Dec 27 19:31:42 2011 +0100
24.3 @@ -0,0 +1,35 @@
24.4 +<?xml version="1.0" encoding="UTF-8"?>
24.5 +<project xmlns="http://www.netbeans.org/ns/project/1">
24.6 + <type>org.netbeans.modules.apisupport.project</type>
24.7 + <configuration>
24.8 + <data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
24.9 + <code-name-base>org.netbeans.modules.masterfs.windows</code-name-base>
24.10 + <module-dependencies>
24.11 + <dependency>
24.12 + <code-name-base>org.netbeans.libs.jna</code-name-base>
24.13 + <build-prerequisite/>
24.14 + <compile-dependency/>
24.15 + <run-dependency>
24.16 + <release-version>1</release-version>
24.17 + <specification-version>1.18</specification-version>
24.18 + </run-dependency>
24.19 + </dependency>
24.20 + <dependency>
24.21 + <code-name-base>org.netbeans.modules.masterfs</code-name-base>
24.22 + <build-prerequisite/>
24.23 + <compile-dependency/>
24.24 + <run-dependency>
24.25 + <release-version>2</release-version>
24.26 + <specification-version>2.36</specification-version>
24.27 + </run-dependency>
24.28 + </dependency>
24.29 + <dependency>
24.30 + <code-name-base>org.openide.util.lookup</code-name-base>
24.31 + <build-prerequisite/>
24.32 + <compile-dependency/>
24.33 + </dependency>
24.34 + </module-dependencies>
24.35 + <public-packages/>
24.36 + </data>
24.37 + </configuration>
24.38 +</project>
25.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
25.2 +++ b/masterfs.windows/src/org/netbeans/modules/masterfs/watcher/windows/Bundle.properties Tue Dec 27 19:31:42 2011 +0100
25.3 @@ -0,0 +1,1 @@
25.4 +OpenIDE-Module-Name=Master Filesystem @ Windows
26.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
26.2 +++ b/masterfs.windows/src/org/netbeans/modules/masterfs/watcher/windows/WindowsNotifier.java Tue Dec 27 19:31:42 2011 +0100
26.3 @@ -0,0 +1,422 @@
26.4 +/*
26.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
26.6 + *
26.7 + * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
26.8 + *
26.9 + * The contents of this file are subject to the terms of either the GNU
26.10 + * General Public License Version 2 only ("GPL") or the Common
26.11 + * Development and Distribution License("CDDL") (collectively, the
26.12 + * "License"). You may not use this file except in compliance with the
26.13 + * License. You can obtain a copy of the License at
26.14 + * http://www.netbeans.org/cddl-gplv2.html
26.15 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
26.16 + * specific language governing permissions and limitations under the
26.17 + * License. When distributing the software, include this License Header
26.18 + * Notice in each file and include the License file at
26.19 + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
26.20 + * particular file as subject to the "Classpath" exception as provided
26.21 + * by Sun in the GPL Version 2 section of the License file that
26.22 + * accompanied this code. If applicable, add the following below the
26.23 + * License Header, with the fields enclosed by brackets [] replaced by
26.24 + * your own identifying information:
26.25 + * "Portions Copyrighted [year] [name of copyright owner]"
26.26 + *
26.27 + * If you wish your version of this file to be governed by only the CDDL
26.28 + * or only the GPL Version 2, indicate your decision by adding
26.29 + * "[Contributor] elects to include this software in this distribution
26.30 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
26.31 + * single choice of license, a recipient has the option to distribute
26.32 + * your version of this file under either the CDDL, the GPL Version 2 or
26.33 + * to extend the choice of license to its licensees as provided above.
26.34 + * However, if you add GPL Version 2 code and therefore, elected the GPL
26.35 + * Version 2 license, then the option applies only if the new code is
26.36 + * made subject to such option by the copyright holder.
26.37 + *
26.38 + * Contributor(s):
26.39 + *
26.40 + * Portions Copyrighted 2010 Sun Microsystems, Inc.
26.41 + */
26.42 +
26.43 +package org.netbeans.modules.masterfs.watcher.windows;
26.44 +
26.45 +import org.netbeans.modules.masterfs.providers.Notifier;
26.46 +import com.sun.jna.FromNativeContext;
26.47 +import com.sun.jna.IntegerType;
26.48 +import com.sun.jna.Library;
26.49 +import com.sun.jna.Native;
26.50 +import com.sun.jna.Pointer;
26.51 +import com.sun.jna.PointerType;
26.52 +import com.sun.jna.Structure;
26.53 +import com.sun.jna.ptr.ByReference;
26.54 +import java.io.File;
26.55 +import java.io.IOException;
26.56 +import java.util.HashMap;
26.57 +import java.util.Map;
26.58 +
26.59 +import com.sun.jna.ptr.IntByReference;
26.60 +import com.sun.jna.ptr.PointerByReference;
26.61 +import com.sun.jna.win32.StdCallLibrary;
26.62 +import com.sun.jna.win32.W32APIFunctionMapper;
26.63 +import com.sun.jna.win32.W32APITypeMapper;
26.64 +import java.io.InterruptedIOException;
26.65 +import java.util.concurrent.BlockingQueue;
26.66 +import java.util.concurrent.LinkedBlockingQueue;
26.67 +import java.util.logging.Level;
26.68 +import java.util.logging.Logger;
26.69 +import org.openide.util.lookup.ServiceProvider;
26.70 +
26.71 +/**
26.72 + * A {@link Notifier} implementation using Win32 API ReadDirectoryChangesW.
26.73 + * Based on JNA examples and platform library stubs.
26.74 + *
26.75 + * @author nenik
26.76 + */
26.77 +@ServiceProvider(service=Notifier.class, position=100)
26.78 +public final class WindowsNotifier extends Notifier<Void> {
26.79 + static final Logger LOG = Logger.getLogger(WindowsNotifier.class.getName());
26.80 +
26.81 + public static final class HANDLE extends PointerType {
26.82 + private boolean immutable;
26.83 + public HANDLE() { }
26.84 + public HANDLE(Pointer p) {
26.85 + setPointer(p);
26.86 + immutable = true;
26.87 + }
26.88 +
26.89 + /** Override to the appropriate object for INVALID_HANDLE_VALUE. */
26.90 + @Override
26.91 + public Object fromNative(Object nativeValue, FromNativeContext context) {
26.92 + Object o = super.fromNative(nativeValue, context);
26.93 + if (INVALID_HANDLE_VALUE.equals(o))
26.94 + return INVALID_HANDLE_VALUE;
26.95 + return o;
26.96 + }
26.97 +
26.98 + @Override
26.99 + public void setPointer(Pointer p) {
26.100 + if (immutable) {
26.101 + throw new UnsupportedOperationException("immutable reference");
26.102 + }
26.103 +
26.104 + super.setPointer(p);
26.105 + }
26.106 + }
26.107 +
26.108 + public static class ULONG_PTR extends IntegerType {
26.109 + public ULONG_PTR() {
26.110 + this(0);
26.111 + }
26.112 +
26.113 + public ULONG_PTR(long value) {
26.114 + super(Pointer.SIZE, value);
26.115 + }
26.116 + }
26.117 +
26.118 + public static class OVERLAPPED extends Structure {
26.119 + public ULONG_PTR Internal;
26.120 + public ULONG_PTR InternalHigh;
26.121 + public int Offset;
26.122 + public int OffsetHigh;
26.123 + public HANDLE hEvent;
26.124 + }
26.125 +
26.126 + public static HANDLE INVALID_HANDLE_VALUE = new HANDLE(Pointer.createConstant(
26.127 + Pointer.SIZE == 8 ? -1 : 0xFFFFFFFFL));
26.128 +
26.129 +
26.130 +
26.131 + public static class HANDLEByReference extends ByReference {
26.132 +
26.133 + public HANDLEByReference() {
26.134 + this(null);
26.135 + }
26.136 +
26.137 + public HANDLEByReference(HANDLE h) {
26.138 + super(Pointer.SIZE);
26.139 + setValue(h);
26.140 + }
26.141 +
26.142 + public void setValue(HANDLE h) {
26.143 + getPointer().setPointer(0, h != null ? h.getPointer() : null);
26.144 + }
26.145 +
26.146 + public HANDLE getValue() {
26.147 + Pointer p = getPointer().getPointer(0);
26.148 + if (p == null)
26.149 + return null;
26.150 + if (INVALID_HANDLE_VALUE.getPointer().equals(p))
26.151 + return INVALID_HANDLE_VALUE;
26.152 + HANDLE h = new HANDLE();
26.153 + h.setPointer(p);
26.154 + return h;
26.155 + }
26.156 + }
26.157 +
26.158 + public static class FILE_NOTIFY_INFORMATION extends Structure {
26.159 + public int NextEntryOffset;
26.160 + public int Action;
26.161 + public int FileNameLength;
26.162 + // filename is not nul-terminated, so we can't use a String/WString
26.163 + public char[] FileName = new char[1];
26.164 +
26.165 + private FILE_NOTIFY_INFORMATION() {}
26.166 +
26.167 + public FILE_NOTIFY_INFORMATION(int size) {
26.168 + if (size < size()) {
26.169 + throw new IllegalArgumentException("Size must greater than "
26.170 + + size() + ", requested " + size);
26.171 + }
26.172 + allocateMemory(size);
26.173 + }
26.174 +
26.175 + /** WARNING: this filename may be either the short or long form of the filename. */
26.176 + public String getFilename() {
26.177 + return new String(FileName, 0, FileNameLength/2);
26.178 + }
26.179 +
26.180 + @Override
26.181 + public void read() {
26.182 + // avoid reading filename until we know how long it is
26.183 + FileName = new char[0];
26.184 + super.read();
26.185 + FileName = getPointer().getCharArray(12, FileNameLength/2);
26.186 + }
26.187 +
26.188 + public FILE_NOTIFY_INFORMATION next() {
26.189 + if (NextEntryOffset == 0)
26.190 + return null;
26.191 + FILE_NOTIFY_INFORMATION next = new FILE_NOTIFY_INFORMATION();
26.192 + next.useMemory(getPointer(), NextEntryOffset);
26.193 + next.read();
26.194 + return next;
26.195 + }
26.196 + }
26.197 +
26.198 + public static class SECURITY_ATTRIBUTES extends Structure {
26.199 + public final int nLength = size();
26.200 + public Pointer lpSecurityDescriptor;
26.201 + public boolean bInheritHandle;
26.202 + }
26.203 +
26.204 + interface Kernel32 extends StdCallLibrary {
26.205 + HANDLE CreateFile(String lpFileName, int dwDesiredAccess, int dwShareMode,
26.206 + SECURITY_ATTRIBUTES lpSecurityAttributes, int dwCreationDisposition,
26.207 + int dwFlagsAndAttributes, HANDLE hTemplateFile);
26.208 +
26.209 + HANDLE CreateIoCompletionPort(HANDLE FileHandle, HANDLE ExistingCompletionPort,
26.210 + Pointer CompletionKey, int NumberOfConcurrentThreads);
26.211 +
26.212 + int GetLastError();
26.213 +
26.214 + boolean GetQueuedCompletionStatus(HANDLE CompletionPort,
26.215 + IntByReference lpNumberOfBytes, ByReference lpCompletionKey,
26.216 + PointerByReference lpOverlapped, int dwMilliseconds);
26.217 +
26.218 + boolean PostQueuedCompletionStatus(HANDLE CompletionPort,
26.219 + int dwNumberOfBytesTransferred, Pointer dwCompletionKey,
26.220 + OVERLAPPED lpOverlapped);
26.221 +
26.222 + boolean CloseHandle(HANDLE hObject);
26.223 +
26.224 + interface OVERLAPPED_COMPLETION_ROUTINE extends StdCallCallback {
26.225 + void callback(int errorCode, int nBytesTransferred,
26.226 + OVERLAPPED overlapped);
26.227 + }
26.228 +
26.229 +
26.230 + public boolean ReadDirectoryChangesW(HANDLE directory,
26.231 + FILE_NOTIFY_INFORMATION info, int length, boolean watchSubtree,
26.232 + int notifyFilter, IntByReference bytesReturned, OVERLAPPED overlapped,
26.233 + OVERLAPPED_COMPLETION_ROUTINE completionRoutine);
26.234 +
26.235 +
26.236 + }
26.237 +
26.238 + final static Kernel32 KERNEL32 = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class,
26.239 + new HashMap() {{
26.240 + put(Library.OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
26.241 + put(Library.OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE);
26.242 + }});
26.243 +
26.244 +
26.245 +
26.246 + public WindowsNotifier() { // prepare port, start thread?
26.247 + }
26.248 +
26.249 + public @Override void removeWatch(Void key) throws IOException {}
26.250 +
26.251 +
26.252 + public @Override String nextEvent() throws IOException, InterruptedException {
26.253 + return events.take();
26.254 + }
26.255 +
26.256 + public static final int INFINITE = 0xFFFFFFFF;
26.257 +
26.258 + public static final int FILE_NOTIFY_CHANGE_NAME = 0x00000003;
26.259 + public static final int FILE_NOTIFY_CHANGE_ATTRIBUTES = 0x00000004;
26.260 + public static final int FILE_NOTIFY_CHANGE_SIZE = 0x00000008;
26.261 + public static final int FILE_NOTIFY_CHANGE_LAST_WRITE = 0x00000010;
26.262 + public static final int FILE_NOTIFY_CHANGE_CREATION = 0x00000040;
26.263 + public static final int FILE_NOTIFY_CHANGE_SECURITY = 0x00000100;
26.264 +
26.265 + private static final int NOTIFY_MASK =
26.266 + FILE_NOTIFY_CHANGE_NAME |
26.267 + FILE_NOTIFY_CHANGE_ATTRIBUTES |
26.268 + FILE_NOTIFY_CHANGE_SIZE |
26.269 + FILE_NOTIFY_CHANGE_LAST_WRITE |
26.270 + FILE_NOTIFY_CHANGE_CREATION |
26.271 + FILE_NOTIFY_CHANGE_SECURITY;
26.272 +
26.273 + public static final int FILE_LIST_DIRECTORY = 0x00000001;
26.274 + public static final int OPEN_EXISTING = 3;
26.275 +
26.276 + public static final int FILE_SHARE_READ = 0x00000001;
26.277 + public static final int FILE_SHARE_WRITE = 0x00000002;
26.278 + public static final int FILE_SHARE_DELETE = 0x00000004;
26.279 +
26.280 + public static final int FILE_FLAG_OVERLAPPED = 0x40000000;
26.281 + public static final int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
26.282 +
26.283 + private class FileInfo {
26.284 + public final String path;
26.285 + public final HANDLE handle;
26.286 + public final FILE_NOTIFY_INFORMATION info = new FILE_NOTIFY_INFORMATION(BUFFER_SIZE);
26.287 + public final IntByReference infoLength = new IntByReference();
26.288 + public final OVERLAPPED overlapped = new OVERLAPPED();
26.289 + public FileInfo(String path, HANDLE h) {
26.290 + this.path = path;
26.291 + this.handle = h;
26.292 + }
26.293 + }
26.294 +
26.295 + private static int watcherThreadID;
26.296 + private Thread watcher;
26.297 + private HANDLE port;
26.298 + private final Map<String, FileInfo> rootMap = new HashMap<String, FileInfo>();
26.299 + private final Map<HANDLE, FileInfo> handleMap = new HashMap<HANDLE, FileInfo>();
26.300 + private final BlockingQueue<String> events = new LinkedBlockingQueue<String>();
26.301 +
26.302 + public @Override Void addWatch(String path) throws IOException {
26.303 +
26.304 + if (path.length() < 3 ) throw new IOException("wrong path: " + path);
26.305 +
26.306 + String root = path.substring(0, 3).replace('/', '\\');
26.307 + if (root.charAt(1) != ':' || root.charAt(2) != '\\') {
26.308 + throw new IOException("wrong path: " + path);
26.309 + }
26.310 +
26.311 + if (rootMap.containsKey(root)) return null; // already listening
26.312 + path = root; // listen once on the rootpath instead
26.313 +
26.314 + int mask = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
26.315 + int flags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
26.316 + HANDLE handle = KERNEL32.CreateFile(path,
26.317 + FILE_LIST_DIRECTORY,
26.318 + mask, null, OPEN_EXISTING,
26.319 + flags, null);
26.320 + if (INVALID_HANDLE_VALUE.equals(handle)) {
26.321 + throw new IOException("Unable to open " + path + ": "
26.322 + + KERNEL32.GetLastError());
26.323 + }
26.324 + FileInfo finfo = new FileInfo(path, handle);
26.325 + rootMap.put(path, finfo);
26.326 + handleMap.put(handle, finfo);
26.327 +
26.328 + // Existing port is returned
26.329 + port = KERNEL32.CreateIoCompletionPort(handle, port, handle.getPointer(), 0);
26.330 + if (INVALID_HANDLE_VALUE.equals(port)) {
26.331 + throw new IOException("Unable to create/use I/O Completion port "
26.332 + + "for " + path + ": " + KERNEL32.GetLastError());
26.333 + }
26.334 +
26.335 + if (!KERNEL32.ReadDirectoryChangesW(handle, finfo.info, finfo.info.size(),
26.336 + true, NOTIFY_MASK, finfo.infoLength,
26.337 + finfo.overlapped, null)) {
26.338 + int err = KERNEL32.GetLastError();
26.339 + throw new IOException("ReadDirectoryChangesW failed on "
26.340 + + finfo.path + ", handle " + handle
26.341 + + ": " + err);
26.342 + }
26.343 + if (watcher == null) {
26.344 + Thread t = new Thread("W32 File Monitor") {
26.345 + @Override
26.346 + public void run() {
26.347 + FileInfo finfo;
26.348 + while (watcher != null) {
26.349 + finfo = waitForChange();
26.350 + if (finfo == null) continue;
26.351 +
26.352 + try {
26.353 + handleChanges(finfo);
26.354 + } catch(IOException e) {
26.355 + LOG.log(Level.INFO, "handleChanges", e);
26.356 + }
26.357 + }
26.358 + }
26.359 + };
26.360 + t.setDaemon(true);
26.361 + t.start();
26.362 + watcher = t;
26.363 + }
26.364 +
26.365 + return null;
26.366 + }
26.367 +
26.368 + @Override
26.369 + protected void start() throws IOException {
26.370 + }
26.371 +
26.372 + @Override
26.373 + public void stop() throws IOException {
26.374 + try {
26.375 + Thread w = watcher;
26.376 + if (w == null) {
26.377 + return;
26.378 + }
26.379 + watcher = null;
26.380 + w.interrupt();
26.381 + w.join(2000);
26.382 + } catch (InterruptedException ex) {
26.383 + throw (IOException)new InterruptedIOException().initCause(ex);
26.384 + }
26.385 + }
26.386 +
26.387 + private void notify(File file) {
26.388 + events.add(file.getPath());
26.389 + }
26.390 +
26.391 +
26.392 + private static final int BUFFER_SIZE = 4096;
26.393 +
26.394 + private void handleChanges(FileInfo finfo) throws IOException {
26.395 + FILE_NOTIFY_INFORMATION fni = finfo.info;
26.396 + // Lazily fetch the data from native to java - asynchronous update
26.397 + fni.read();
26.398 + do {
26.399 + File file = new File(finfo.path, fni.getFilename());
26.400 + notify(file);
26.401 +
26.402 + fni = fni.next();
26.403 + } while (fni != null);
26.404 +
26.405 + if (!KERNEL32.ReadDirectoryChangesW(finfo.handle, finfo.info,
26.406 + finfo.info.size(), true, NOTIFY_MASK,
26.407 + finfo.infoLength, finfo.overlapped, null)) {
26.408 + int err = KERNEL32.GetLastError();
26.409 + throw new IOException("ReadDirectoryChangesW failed on "
26.410 + + finfo.path + ": " + err);
26.411 + }
26.412 + }
26.413 +
26.414 + private FileInfo waitForChange() {
26.415 + IntByReference rcount = new IntByReference();
26.416 + HANDLEByReference rkey = new HANDLEByReference();
26.417 + PointerByReference roverlap = new PointerByReference();
26.418 + KERNEL32.GetQueuedCompletionStatus(port, rcount, rkey, roverlap, INFINITE);
26.419 +
26.420 + synchronized (this) {
26.421 + return (FileInfo)handleMap.get(rkey.getValue());
26.422 + }
26.423 + }
26.424 +
26.425 +}
27.1 --- a/masterfs/apichanges.xml Sat Dec 24 00:28:54 2011 +0000
27.2 +++ b/masterfs/apichanges.xml Tue Dec 27 19:31:42 2011 +0100
27.3 @@ -49,6 +49,21 @@
27.4 <apidef name="masterfs">MasterFileSystem API</apidef>
27.5 </apidefs>
27.6 <changes>
27.7 + <change id="notifier">
27.8 + <api name="masterfs"/>
27.9 + <summary>External change notifiers in separate modules</summary>
27.10 + <version major="2" minor="36"/>
27.11 + <date day="31" month="12" year="2011"/>
27.12 + <author login="jtulach"/>
27.13 + <compatibility addition="yes" binary="compatible" source="compatible" semantic="compatible" deprecation="no" deletion="no" modification="no"/>
27.14 + <description>
27.15 + Implementation of <em>native listeners</em> has been moved
27.16 + to separate modules, so the masterfs itself does not depend
27.17 + on JNA library.
27.18 + </description>
27.19 + <class package="org.netbeans.modules.masterfs.providers" name="Notifier"/>
27.20 + <issue number="206434"/>
27.21 + </change>
27.22 <change id="canWrite">
27.23 <api name="masterfs"/>
27.24 <summary>Determine if a ProvidedExtensions instance should be used to get a files canWrite() value</summary>
28.1 --- a/masterfs/manifest.mf Sat Dec 24 00:28:54 2011 +0000
28.2 +++ b/masterfs/manifest.mf Tue Dec 27 19:31:42 2011 +0100
28.3 @@ -2,7 +2,9 @@
28.4 OpenIDE-Module: org.netbeans.modules.masterfs/2
28.5 OpenIDE-Module-Install: org/netbeans/modules/masterfs/Installer.class
28.6 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/masterfs/resources/Bundle.properties
28.7 -OpenIDE-Module-Specification-Version: 2.35
28.8 +OpenIDE-Module-Specification-Version: 2.36
28.9 +OpenIDE-Module-Recommends: org.netbeans.modules.masterfs.providers.Notifier
28.10 +OpenIDE-Module-Provides: org.openide.filesystems.FileUtil.toFileObject
28.11 AutoUpdate-Show-In-Client: false
28.12 AutoUpdate-Essential-Module: true
28.13
29.1 --- a/masterfs/nbproject/project.xml Sat Dec 24 00:28:54 2011 +0000
29.2 +++ b/masterfs/nbproject/project.xml Tue Dec 27 19:31:42 2011 +0100
29.3 @@ -50,15 +50,6 @@
29.4 <code-name-base>org.netbeans.modules.masterfs</code-name-base>
29.5 <module-dependencies>
29.6 <dependency>
29.7 - <code-name-base>org.netbeans.libs.jna</code-name-base>
29.8 - <build-prerequisite/>
29.9 - <compile-dependency/>
29.10 - <run-dependency>
29.11 - <release-version>1</release-version>
29.12 - <specification-version>1.10</specification-version>
29.13 - </run-dependency>
29.14 - </dependency>
29.15 - <dependency>
29.16 <code-name-base>org.netbeans.modules.queries</code-name-base>
29.17 <build-prerequisite/>
29.18 <compile-dependency/>
29.19 @@ -139,6 +130,10 @@
29.20 <friend>org.netbeans.modules.parsing.api</friend>
29.21 <friend>org.netbeans.modules.parsing.lucene</friend>
29.22 <friend>org.netbeans.modules.versioning</friend>
29.23 + <friend>org.netbeans.modules.masterfs.windows</friend>
29.24 + <friend>org.netbeans.modules.masterfs.linux</friend>
29.25 + <friend>org.netbeans.modules.masterfs.solaris</friend>
29.26 + <friend>org.netbeans.modules.masterfs.macosx</friend>
29.27 <package>org.netbeans.modules.masterfs.providers</package>
29.28 </friend-packages>
29.29 </data>
30.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
30.2 +++ b/masterfs/src/org/netbeans/modules/masterfs/providers/Notifier.java Tue Dec 27 19:31:42 2011 +0100
30.3 @@ -0,0 +1,131 @@
30.4 +/*
30.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
30.6 + *
30.7 + * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
30.8 + *
30.9 + * The contents of this file are subject to the terms of either the GNU
30.10 + * General Public License Version 2 only ("GPL") or the Common
30.11 + * Development and Distribution License("CDDL") (collectively, the
30.12 + * "License"). You may not use this file except in compliance with the
30.13 + * License. You can obtain a copy of the License at
30.14 + * http://www.netbeans.org/cddl-gplv2.html
30.15 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
30.16 + * specific language governing permissions and limitations under the
30.17 + * License. When distributing the software, include this License Header
30.18 + * Notice in each file and include the License file at
30.19 + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
30.20 + * particular file as subject to the "Classpath" exception as provided
30.21 + * by Sun in the GPL Version 2 section of the License file that
30.22 + * accompanied this code. If applicable, add the following below the
30.23 + * License Header, with the fields enclosed by brackets [] replaced by
30.24 + * your own identifying information:
30.25 + * "Portions Copyrighted [year] [name of copyright owner]"
30.26 + *
30.27 + * If you wish your version of this file to be governed by only the CDDL
30.28 + * or only the GPL Version 2, indicate your decision by adding
30.29 + * "[Contributor] elects to include this software in this distribution
30.30 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
30.31 + * single choice of license, a recipient has the option to distribute
30.32 + * your version of this file under either the CDDL, the GPL Version 2 or
30.33 + * to extend the choice of license to its licensees as provided above.
30.34 + * However, if you add GPL Version 2 code and therefore, elected the GPL
30.35 + * Version 2 license, then the option applies only if the new code is
30.36 + * made subject to such option by the copyright holder.
30.37 + *
30.38 + * Contributor(s):
30.39 + *
30.40 + * Portions Copyrighted 2010 Sun Microsystems, Inc.
30.41 + */
30.42 +
30.43 +package org.netbeans.modules.masterfs.providers;
30.44 +
30.45 +import java.io.IOException;
30.46 +import org.netbeans.modules.masterfs.watcher.NotifierAccessor;
30.47 +
30.48 +/**
30.49 + * This SPI represents the interface between masterfs and
30.50 + * different implementations of filesystem watches on various systems.
30.51 + *
30.52 + * The SPI is kept very minimal, as the only necessary information is a queue
30.53 + * of modified folders, the filesystems code will evaluate the nature
30.54 + * of the change itself.
30.55 + * The SPI also doesn't distinguish between systems able of hierarchical
30.56 + * listening and systems without such a capability.
30.57 + * The implementation can report more events than registered, the infrastructure
30.58 + * should take care of filtering them.
30.59 + *
30.60 + * @author Petr Nejedly
30.61 + * @since 2.36
30.62 + */
30.63 +public abstract class Notifier<KEY> {
30.64 + /**
30.65 + * Register a path for notifications. Optionally provide a key useful
30.66 + * for unregistering the path. The implementations that need to have every
30.67 + * path registered individually shall return a valid key, and shall
30.68 + * implement the {@link #removeWatch(java.lang.Object)} properly.
30.69 + *
30.70 + * @param path the path to register for notifications
30.71 + * @return a key useful for unregistering the path.
30.72 + * @throws IOException if the path can't be registered. For example if the
30.73 + * OS limit on the number of watched folders is reached. The exception
30.74 + * should be annotated with localized explanation.
30.75 + */
30.76 + protected abstract KEY addWatch(String path) throws IOException;
30.77 +
30.78 + /**
30.79 + * Unregister a path. Implementations that listen recursively on the whole
30.80 + * filesystem may ignore this request. They shall also return
30.81 + * <code>null</code> from the {@link #addWatch(java.lang.String)} call.
30.82 + *
30.83 + * @param key the key obtained during registration.
30.84 + * @throws IOException
30.85 + */
30.86 + protected abstract void removeWatch(KEY key) throws IOException;
30.87 +
30.88 + /**
30.89 + *
30.90 + * @return absolute path of the changed folder or null in case
30.91 + * of overflow or any other reason to cause a full rescan
30.92 + * @throws IOException
30.93 + * @throws InterruptedException
30.94 + */
30.95 + protected abstract String nextEvent() throws IOException, InterruptedException;
30.96 +
30.97 + /** Starts the notifier. If the implementation is not ready to work,
30.98 + * it may throw I/O exception to signal it has not been initialized
30.99 + * properly.
30.100 + * @exception IOException if the initialization cannot be performed
30.101 + */
30.102 + protected abstract void start() throws IOException;
30.103 +
30.104 + /** Get ready for stop. Clean all resources, the system is about to
30.105 + * shutdown the VM. By default this is no-op operation.
30.106 + */
30.107 + protected void stop() throws IOException {
30.108 + }
30.109 +
30.110 + static {
30.111 + NotifierAccessor impl = new NotifierAccessor() {
30.112 + @Override
30.113 + protected <KEY> KEY addWatch(Notifier<KEY> n, String path) throws IOException {
30.114 + return n.addWatch(path);
30.115 + }
30.116 + @Override
30.117 + protected <KEY> void removeWatch(Notifier<KEY> n, KEY key) throws IOException {
30.118 + n.removeWatch(key);
30.119 + }
30.120 + @Override
30.121 + protected String nextEvent(Notifier<?> n) throws IOException, InterruptedException {
30.122 + return n.nextEvent();
30.123 + }
30.124 + @Override
30.125 + protected void start(Notifier<?> n) throws IOException {
30.126 + n.start();
30.127 + }
30.128 + @Override
30.129 + protected void stop(Notifier<?> n) throws IOException {
30.130 + n.stop();
30.131 + }
30.132 + };
30.133 + }
30.134 +}
31.1 --- a/masterfs/src/org/netbeans/modules/masterfs/watcher/FAMNotifier.java Sat Dec 24 00:28:54 2011 +0000
31.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
31.3 @@ -1,231 +0,0 @@
31.4 -/*
31.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
31.6 - *
31.7 - * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
31.8 - *
31.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
31.10 - * Other names may be trademarks of their respective owners.
31.11 - *
31.12 - * The contents of this file are subject to the terms of either the GNU
31.13 - * General Public License Version 2 only ("GPL") or the Common
31.14 - * Development and Distribution License("CDDL") (collectively, the
31.15 - * "License"). You may not use this file except in compliance with the
31.16 - * License. You can obtain a copy of the License at
31.17 - * http://www.netbeans.org/cddl-gplv2.html
31.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
31.19 - * specific language governing permissions and limitations under the
31.20 - * License. When distributing the software, include this License Header
31.21 - * Notice in each file and include the License file at
31.22 - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
31.23 - * particular file as subject to the "Classpath" exception as provided
31.24 - * by Oracle in the GPL Version 2 section of the License file that
31.25 - * accompanied this code. If applicable, add the following below the
31.26 - * License Header, with the fields enclosed by brackets [] replaced by
31.27 - * your own identifying information:
31.28 - * "Portions Copyrighted [year] [name of copyright owner]"
31.29 - *
31.30 - * If you wish your version of this file to be governed by only the CDDL
31.31 - * or only the GPL Version 2, indicate your decision by adding
31.32 - * "[Contributor] elects to include this software in this distribution
31.33 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
31.34 - * single choice of license, a recipient has the option to distribute
31.35 - * your version of this file under either the CDDL, the GPL Version 2 or
31.36 - * to extend the choice of license to its licensees as provided above.
31.37 - * However, if you add GPL Version 2 code and therefore, elected the GPL
31.38 - * Version 2 license, then the option applies only if the new code is
31.39 - * made subject to such option by the copyright holder.
31.40 - *
31.41 - * Contributor(s):
31.42 - *
31.43 - * Portions Copyrighted 2010 Sun Microsystems, Inc.
31.44 - */
31.45 -
31.46 -package org.netbeans.modules.masterfs.watcher;
31.47 -
31.48 -import com.sun.jna.Library;
31.49 -import com.sun.jna.Native;
31.50 -import com.sun.jna.Pointer;
31.51 -import com.sun.jna.Structure;
31.52 -import com.sun.jna.Structure.ByReference;
31.53 -import java.io.IOException;
31.54 -import java.util.Collections;
31.55 -import java.util.HashMap;
31.56 -import java.util.HashSet;
31.57 -import java.util.Map;
31.58 -import java.util.Set;
31.59 -import java.util.concurrent.BlockingQueue;
31.60 -import java.util.concurrent.LinkedBlockingQueue;
31.61 -import java.util.logging.Level;
31.62 -import java.util.logging.Logger;
31.63 -
31.64 -/**
31.65 - * Notifier implementation using fam library
31.66 - *
31.67 - * @author Egor Ushakov
31.68 - */
31.69 -public class FAMNotifier extends Notifier<Integer> {
31.70 - private final FAMLibrary.FAMConnection conn;
31.71 - private final FAMLibrary lib;
31.72 - private final Map<Integer, String> map = Collections.synchronizedMap(new HashMap<Integer, String>());
31.73 -
31.74 - private final BlockingQueue<String> events = new LinkedBlockingQueue<String>();
31.75 - private final Thread eventReader;
31.76 - private volatile boolean stopped = false;
31.77 -
31.78 - // limit unanswered requests to avoid FAM hang, see IZ 199497
31.79 - private static int ACTIVE_REQUESTS_LIMIT = 100;
31.80 - private final Set<Integer> activeRequests = Collections.synchronizedSet(new HashSet<Integer>());
31.81 -
31.82 - private static final Logger LOG = Logger.getLogger(FAMNotifier.class.getName());
31.83 -
31.84 - public FAMNotifier() {
31.85 - FAMLibrary library;
31.86 - try {
31.87 - // first try gamin
31.88 - library = (FAMLibrary) Native.loadLibrary("gamin-1", FAMLibrary.class); //NOI18N
31.89 - } catch (LinkageError x) {
31.90 - // then fam
31.91 - library = (FAMLibrary) Native.loadLibrary("fam", FAMLibrary.class); //NOI18N
31.92 - }
31.93 - this.lib = library;
31.94 - this.conn = new FAMLibrary.FAMConnection();
31.95 - if (lib.FAMOpen(conn) != 0) {
31.96 - throw new IllegalStateException();
31.97 - }
31.98 - eventReader = new Thread(new Runnable() {
31.99 - @Override
31.100 - public void run() {
31.101 - while (!stopped) {
31.102 - while (lib.FAMPending(conn) > 0) {
31.103 - FAMLibrary.FAMEvent evt = new FAMLibrary.FAMEvent();
31.104 - if (lib.FAMNextEvent(conn, evt) != -1) {
31.105 - if (evt.code == FAMLibrary.FAMEndExist || evt.code == FAMLibrary.FAMAcknowledge) {
31.106 - synchronized (activeRequests) {
31.107 - activeRequests.remove(evt.fr.reqnum);
31.108 - activeRequests.notifyAll();
31.109 - }
31.110 - }
31.111 - String path = map.get(evt.fr.reqnum);
31.112 - if (path != null) {
31.113 - events.add(path);
31.114 - }
31.115 - }
31.116 - }
31.117 - // now sleep
31.118 - try {
31.119 - Thread.sleep(1000);
31.120 - } catch (InterruptedException ex) {
31.121 - }
31.122 - }
31.123 - }
31.124 - }, "FAM events reader"); //NOI18N
31.125 - eventReader.start();
31.126 - }
31.127 -
31.128 - @Override
31.129 - public Integer addWatch(String path) throws IOException {
31.130 - startRequest();
31.131 - FAMLibrary.FAMRequest request = new FAMLibrary.FAMRequest();
31.132 - lib.FAMMonitorDirectory(conn, path, request, null);
31.133 - LOG.log(Level.FINEST, "addWatch {0}({1}), queue length: {2}", new Object[]{path, request.reqnum, activeRequests.size()});
31.134 - activeRequests.add(request.reqnum);
31.135 - map.put(request.reqnum, path);
31.136 - return request.reqnum;
31.137 - }
31.138 -
31.139 - private void startRequest() {
31.140 - synchronized (activeRequests) {
31.141 - while (activeRequests.size() > ACTIVE_REQUESTS_LIMIT) {
31.142 - long start = System.currentTimeMillis();
31.143 - try {
31.144 - activeRequests.wait();
31.145 - } catch (InterruptedException ex) {
31.146 - }
31.147 - LOG.warning("Blocking FAM requests for " + (System.currentTimeMillis()-start) + "ms, requests queue is full"); //NOI18N
31.148 - }
31.149 - }
31.150 - }
31.151 -
31.152 - @Override
31.153 - public String nextEvent() throws IOException, InterruptedException {
31.154 - return events.take();
31.155 - }
31.156 -
31.157 - @Override
31.158 - public void removeWatch(Integer key) throws IOException {
31.159 - LOG.log(Level.FINEST, "removeWatch {0}({1}), queue length: {2}", new Object[]{map.get(key), key, activeRequests.size()});
31.160 - startRequest();
31.161 - activeRequests.add(key);
31.162 - lib.FAMCancelMonitor(conn, new FAMLibrary.FAMRequest(key));
31.163 - map.remove(key);
31.164 - }
31.165 -
31.166 - @Override
31.167 - protected void stop() throws IOException {
31.168 - stopped = true;
31.169 - try {
31.170 - eventReader.join(2000);
31.171 - } catch (InterruptedException ex) {
31.172 - }
31.173 - lib.FAMClose(conn);
31.174 - }
31.175 -
31.176 - interface FAMLibrary extends Library {
31.177 - static class FAMConnection extends Structure {
31.178 - public static class Reference extends FAMConnection implements ByReference {}
31.179 - public int fd;
31.180 - public Pointer client;
31.181 - }
31.182 -
31.183 - static class FAMRequest extends Structure {
31.184 -
31.185 - public FAMRequest() {
31.186 - super();
31.187 - }
31.188 -
31.189 - public FAMRequest(int reqnum) {
31.190 - super();
31.191 - this.reqnum = reqnum;
31.192 - }
31.193 -
31.194 - public int reqnum;
31.195 - };
31.196 -
31.197 - public static final int PATH_MAX = 1024;
31.198 -
31.199 - public static final int FAMChanged = 1;
31.200 - public static final int FAMDeleted = 2;
31.201 - public static final int FAMStartExecuting = 3;
31.202 - public static final int FAMStopExecuting = 4;
31.203 - public static final int FAMCreated = 5;
31.204 - public static final int FAMMoved = 6;
31.205 - public static final int FAMAcknowledge = 7;
31.206 - public static final int FAMExists = 8;
31.207 - public static final int FAMEndExist = 9;
31.208 -
31.209 - static class FAMEvent extends Structure {
31.210 - public FAMConnection.Reference fc; /* The fam connection that event occurred on */
31.211 - public FAMRequest fr; /* Corresponds to the FamRequest from monitor */
31.212 - public String hostname; /* host and filename - pointer to which */
31.213 - public byte[] filename = new byte[PATH_MAX]; /* file changed */
31.214 - public Pointer userdata; /* userdata associated with this monitor req. */
31.215 - public int code; /* What happened to file - see above */
31.216 - }
31.217 -
31.218 -
31.219 - int FAMOpen(FAMConnection fc);
31.220 - int FAMClose(FAMConnection fc);
31.221 -
31.222 - int FAMMonitorDirectory(FAMConnection fc,
31.223 - String filename,
31.224 - FAMRequest fr,
31.225 - Pointer userData);
31.226 - int FAMCancelMonitor(FAMConnection fc,
31.227 - FAMRequest fr);
31.228 -
31.229 - int FAMPending(FAMConnection fc);
31.230 - int FAMNextEvent(FAMConnection fc,
31.231 - FAMEvent fe);
31.232 - }
31.233 -
31.234 -}
32.1 --- a/masterfs/src/org/netbeans/modules/masterfs/watcher/LinuxNotifier.java Sat Dec 24 00:28:54 2011 +0000
32.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
32.3 @@ -1,209 +0,0 @@
32.4 -/*
32.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
32.6 - *
32.7 - * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
32.8 - *
32.9 - * The contents of this file are subject to the terms of either the GNU
32.10 - * General Public License Version 2 only ("GPL") or the Common
32.11 - * Development and Distribution License("CDDL") (collectively, the
32.12 - * "License"). You may not use this file except in compliance with the
32.13 - * License. You can obtain a copy of the License at
32.14 - * http://www.netbeans.org/cddl-gplv2.html
32.15 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
32.16 - * specific language governing permissions and limitations under the
32.17 - * License. When distributing the software, include this License Header
32.18 - * Notice in each file and include the License file at
32.19 - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
32.20 - * particular file as subject to the "Classpath" exception as provided
32.21 - * by Sun in the GPL Version 2 section of the License file that
32.22 - * accompanied this code. If applicable, add the following below the
32.23 - * License Header, with the fields enclosed by brackets [] replaced by
32.24 - * your own identifying information:
32.25 - * "Portions Copyrighted [year] [name of copyright owner]"
32.26 - *
32.27 - * If you wish your version of this file to be governed by only the CDDL
32.28 - * or only the GPL Version 2, indicate your decision by adding
32.29 - * "[Contributor] elects to include this software in this distribution
32.30 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
32.31 - * single choice of license, a recipient has the option to distribute
32.32 - * your version of this file under either the CDDL, the GPL Version 2 or
32.33 - * to extend the choice of license to its licensees as provided above.
32.34 - * However, if you add GPL Version 2 code and therefore, elected the GPL
32.35 - * Version 2 license, then the option applies only if the new code is
32.36 - * made subject to such option by the copyright holder.
32.37 - *
32.38 - * Contributor(s):
32.39 - *
32.40 - * Portions Copyrighted 2010 Sun Microsystems, Inc.
32.41 - */
32.42 -
32.43 -package org.netbeans.modules.masterfs.watcher;
32.44 -
32.45 -import com.sun.jna.Library;
32.46 -import com.sun.jna.Native;
32.47 -import com.sun.jna.NativeLibrary;
32.48 -import java.io.IOException;
32.49 -import java.nio.ByteBuffer;
32.50 -import java.nio.ByteOrder;
32.51 -import java.util.HashMap;
32.52 -import java.util.Map;
32.53 -import java.util.logging.Level;
32.54 -import java.util.logging.Logger;
32.55 -
32.56 -/**
32.57 - * A {@link Notifier} implementation based on Linux inotify mechanism.
32.58 - *
32.59 - * @author nenik
32.60 - */
32.61 -final class LinuxNotifier extends Notifier<LinuxNotifier.LKey> {
32.62 - private static final Logger LOG = Logger.getLogger(LinuxNotifier.class.getName());
32.63 -
32.64 - private static interface InotifyImpl extends Library {
32.65 - public int inotify_init();
32.66 - public int inotify_init1(int flags);
32.67 - public int close(int fd);
32.68 - public int read(int fd, ByteBuffer buff, int count);
32.69 - public int inotify_add_watch(int fd, String pathname, int mask);
32.70 - public int inotify_rm_watch(int fd, int wd);
32.71 -
32.72 - public static final int O_CLOEXEC = 02000000; //0x80000
32.73 -
32.74 - // Masks
32.75 - public static final int IN_ACCESS = 0x0001; /* File was accessed */
32.76 - public static final int IN_MODIFY = 0x0002; /* File was modified */
32.77 - public static final int IN_ATTRIB = 0x0004; /* Metadata changed */
32.78 - public static final int IN_CLOSE_WRITE = 0x0008; /* Writtable file was closed */
32.79 - public static final int IN_CLOSE_NOWRITE = 0x0010; /* Unwrittable file closed */
32.80 - public static final int IN_OPEN = 0x0020; /* File was opened */
32.81 - public static final int IN_MOVED_FROM = 0x0040; /* File was moved from X */
32.82 - public static final int IN_MOVED_TO = 0x0080; /* File was moved to Y */
32.83 - public static final int IN_CREATE = 0x0100; /* Subfile was created */
32.84 - public static final int IN_DELETE = 0x0200; /* Subfile was deleted */
32.85 - public static final int IN_DELETE_SELF = 0x0400; /* Self was deleted */
32.86 - public static final int IN_MOVE_SELF = 0x0800; /* Self was moved */
32.87 -
32.88 - // additional event masks
32.89 - public static final int IN_UNMOUNT = 0x2000; /* Backing fs was unmounted */
32.90 - public static final int IN_Q_OVERFLOW = 0x4000; /* Event queued overflowed */
32.91 - public static final int IN_IGNORED = 0x8000; /* File was ignored */
32.92 -
32.93 - }
32.94 -
32.95 - final InotifyImpl IMPL;
32.96 - int fd;
32.97 - private ByteBuffer buff = ByteBuffer.allocateDirect(4096);
32.98 -
32.99 - // An array would serve nearly as well
32.100 - private Map<Integer, LKey> map = new HashMap<Integer, LKey>();
32.101 -
32.102 - public LinuxNotifier() {
32.103 - IMPL = (InotifyImpl) Native.loadLibrary("c", InotifyImpl.class);
32.104 - buff.position(buff.capacity()); // make the buffer empty
32.105 - buff.order(ByteOrder.nativeOrder());
32.106 - fd = IMPL.inotify_init1(InotifyImpl.O_CLOEXEC);
32.107 - if (fd < 0) {
32.108 - LOG.log(
32.109 - Level.INFO, "Linux kernel {0} returned {1} from inotify_init1",
32.110 - new Object[] { System.getProperty("os.version"), fd }
32.111 - );
32.112 - fd = IMPL.inotify_init();
32.113 - LOG.log(Level.INFO, "Trying inotify_init: {0}", fd);
32.114 - }
32.115 - if (fd < 0) {
32.116 - throw new IllegalStateException("inotify_init failed: " + fd);
32.117 - }
32.118 - }
32.119 -
32.120 - private String getString(int maxLen) {
32.121 - if (maxLen < 1) return null; // no name field
32.122 - int stop = maxLen - 1;
32.123 - byte[] temp = new byte[maxLen];
32.124 - buff.get(temp);
32.125 - while (temp[stop] == 0) stop--;
32.126 - return new String(temp, 0, stop+1);
32.127 - }
32.128 -
32.129 - @Override public String nextEvent() throws IOException {
32.130 - /* inotify event structure layout:
32.131 - * int wd; // Watch descriptor
32.132 - * uint32_t mask; // Mask of events
32.133 - * uint32_t cookie;// Unique cookie associating related events (for rename(2))
32.134 - * uint32_t len; // Size of name field
32.135 - * char name[];// Optional null-terminated name
32.136 - */
32.137 - while (buff.remaining() < 16 || buff.remaining() < 16 + buff.getInt(buff.position() + 12)) {
32.138 - buff.compact();
32.139 - int len = IMPL.read(fd, buff, buff.remaining());
32.140 -
32.141 - if (len <= 0) {
32.142 - // lazily get a thread local errno
32.143 - int errno = NativeLibrary.getInstance("c").getFunction("errno").getInt(0);
32.144 - if (errno == 4) { // EINTR
32.145 - buff.flip();
32.146 - continue; // restart the I/O
32.147 - } else {
32.148 - throw new IOException("error reading from inotify: " + errno);
32.149 - }
32.150 - }
32.151 - buff.position(buff.position() + len);
32.152 - buff.flip();
32.153 - }
32.154 -
32.155 - // now we have enough data in the buffer
32.156 - int wd = buff.getInt();
32.157 - int mask = buff.getInt();
32.158 - int cookie = buff.getInt();
32.159 - int len = buff.getInt();
32.160 - String name = getString(len); // ignore
32.161 -
32.162 - LKey key = map.get(wd);
32.163 - if (key == null) { /* wd == -1 -> Queue overflow */
32.164 - return null;
32.165 - }
32.166 -
32.167 - return key.path;
32.168 - }
32.169 -
32.170 -
32.171 - static class LKey {
32.172 - int id;
32.173 - String path;
32.174 -
32.175 - public LKey(int id, String path) {
32.176 - this.id = id;
32.177 - this.path = path;
32.178 - }
32.179 -
32.180 - @Override
32.181 - public String toString() {
32.182 - return "LKey[" + id + " - '" + path + "']";
32.183 - }
32.184 - }
32.185 -
32.186 - @Override public LKey addWatch(String path) throws IOException {
32.187 - // what if the file doesn't exist?
32.188 - int id = IMPL.inotify_add_watch(fd, path,
32.189 - InotifyImpl.IN_CREATE | InotifyImpl.IN_MOVED_TO |
32.190 - InotifyImpl.IN_DELETE | InotifyImpl.IN_MOVED_FROM |
32.191 - InotifyImpl.IN_MODIFY | InotifyImpl.IN_ATTRIB);
32.192 - //XXX handle error return value (-1)
32.193 - LOG.log(Level.FINEST, "addWatch{0} res: {1}", new Object[]{path, id});
32.194 - if (id <= 0) {
32.195 - // 28 == EINOSPC
32.196 - int errno = NativeLibrary.getInstance("c").getFunction("errno").getInt(0); // NOI18N
32.197 - throw new IOException("addWatch on " + path + " errno: " + errno); // NOI18N
32.198 - }
32.199 -
32.200 - LKey newKey = map.get(id);
32.201 - if (newKey == null) {
32.202 - newKey = new LKey(id, path);
32.203 - map.put(id, newKey);
32.204 - }
32.205 - return newKey;
32.206 - }
32.207 -
32.208 - @Override public void removeWatch(LKey lkey) {
32.209 - map.remove(lkey.id);
32.210 - IMPL.inotify_rm_watch(fd, lkey.id);
32.211 - }
32.212 -}
33.1 --- a/masterfs/src/org/netbeans/modules/masterfs/watcher/Notifier.java Sat Dec 24 00:28:54 2011 +0000
33.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
33.3 @@ -1,153 +0,0 @@
33.4 -/*
33.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
33.6 - *
33.7 - * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
33.8 - *
33.9 - * The contents of this file are subject to the terms of either the GNU
33.10 - * General Public License Version 2 only ("GPL") or the Common
33.11 - * Development and Distribution License("CDDL") (collectively, the
33.12 - * "License"). You may not use this file except in compliance with the
33.13 - * License. You can obtain a copy of the License at
33.14 - * http://www.netbeans.org/cddl-gplv2.html
33.15 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
33.16 - * specific language governing permissions and limitations under the
33.17 - * License. When distributing the software, include this License Header
33.18 - * Notice in each file and include the License file at
33.19 - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
33.20 - * particular file as subject to the "Classpath" exception as provided
33.21 - * by Sun in the GPL Version 2 section of the License file that
33.22 - * accompanied this code. If applicable, add the following below the
33.23 - * License Header, with the fields enclosed by brackets [] replaced by
33.24 - * your own identifying information:
33.25 - * "Portions Copyrighted [year] [name of copyright owner]"
33.26 - *
33.27 - * If you wish your version of this file to be governed by only the CDDL
33.28 - * or only the GPL Version 2, indicate your decision by adding
33.29 - * "[Contributor] elects to include this software in this distribution
33.30 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
33.31 - * single choice of license, a recipient has the option to distribute
33.32 - * your version of this file under either the CDDL, the GPL Version 2 or
33.33 - * to extend the choice of license to its licensees as provided above.
33.34 - * However, if you add GPL Version 2 code and therefore, elected the GPL
33.35 - * Version 2 license, then the option applies only if the new code is
33.36 - * made subject to such option by the copyright holder.
33.37 - *
33.38 - * Contributor(s):
33.39 - *
33.40 - * Portions Copyrighted 2010 Sun Microsystems, Inc.
33.41 - */
33.42 -
33.43 -package org.netbeans.modules.masterfs.watcher;
33.44 -
33.45 -import java.io.IOException;
33.46 -import java.lang.ref.ReferenceQueue;
33.47 -import java.lang.ref.WeakReference;
33.48 -import java.util.logging.Level;
33.49 -import org.openide.filesystems.FileObject;
33.50 -import org.openide.util.Exceptions;
33.51 -import org.openide.util.Utilities;
33.52 -
33.53 -/**
33.54 - * This internal SPI represents the interface between masterfs and
33.55 - * different implementations of filesystem watches on various systems.
33.56 - *
33.57 - * The SPI is kept very minimal, as the only necessary information is a queue
33.58 - * of modified folders, the filesystems code will evaluate the nature
33.59 - * of the change itself.
33.60 - * The SPI also doesn't distinguish between systems able of hierarchical
33.61 - * listening and systems without such a capability.
33.62 - * The implementation can report more events than registered, the infrastructure
33.63 - * should take care of filtering them.
33.64 - *
33.65 - * @author nenik
33.66 - */
33.67 -public abstract class Notifier<KEY> {
33.68 -
33.69 - /**
33.70 - * Register a path for notifications. Optionally provide a key useful
33.71 - * for unregistering the path. The implementations that need to have every
33.72 - * path registered individually shall return a valid key, and shall
33.73 - * implement the {@link #removeWatch(java.lang.Object)} properly.
33.74 - *
33.75 - * @param path the path to register for notifications
33.76 - * @return a key useful for unregistering the path.
33.77 - * @throws IOException if the path can't be registered. For example if the
33.78 - * OS limit on the number of watched folders is reached. The exception
33.79 - * should be annotated with localized explanation.
33.80 - */
33.81 - public abstract KEY addWatch(String path) throws IOException;
33.82 -
33.83 - /**
33.84 - * Unregister a path. Implementations that listen recursively on the whole
33.85 - * filesystem may ignore this request. They shall also return
33.86 - * <code>null</code> from the {@link #addWatch(java.lang.String)} call.
33.87 - *
33.88 - * @param key the key obtained during registration.
33.89 - * @throws IOException
33.90 - */
33.91 - public abstract void removeWatch(KEY key) throws IOException;
33.92 -
33.93 - /**
33.94 - *
33.95 - * @return absolute path of the changed folder or null in case
33.96 - * of overflow or any other reason to cause a full rescan
33.97 - * @throws IOException
33.98 - * @throws InterruptedException
33.99 - */
33.100 - public abstract String nextEvent() throws IOException, InterruptedException;
33.101 -
33.102 - /** Get ready for stop. Clean all resources, the system is about to
33.103 - * shutdown the VM. By default this is no-op operation.
33.104 - */
33.105 - protected void stop() throws IOException {
33.106 - }
33.107 -
33.108 - class KeyRef extends WeakReference<FileObject> {
33.109 - private final KEY key;
33.110 - private final int hash;
33.111 -
33.112 - public KeyRef(FileObject fo, KEY key, ReferenceQueue<FileObject> queue) {
33.113 - super(fo, queue);
33.114 - this.key = key;
33.115 - this.hash = fo.hashCode();
33.116 - if (key != null) {
33.117 - Watcher.LOG.log(Level.FINE, "Adding watch for {0}", key);
33.118 - }
33.119 - }
33.120 -
33.121 - @Override
33.122 - public FileObject get() {
33.123 - return super.get();
33.124 - }
33.125 -
33.126 - @Override
33.127 - public boolean equals(Object obj) {
33.128 - if (obj == this) {
33.129 - return true;
33.130 - }
33.131 - try {
33.132 - KeyRef kr = (KeyRef)obj;
33.133 - FileObject mine = get();
33.134 - FileObject theirs = kr.get();
33.135 - if (mine == null) {
33.136 - return theirs == null;
33.137 - } else {
33.138 - return mine.equals(theirs);
33.139 - }
33.140 - } catch (ClassCastException ex) {
33.141 - return false;
33.142 - }
33.143 - }
33.144 -
33.145 - final void removeWatch() throws IOException {
33.146 - Watcher.LOG.log(Level.FINE, "Removing watch for {0}", key);
33.147 - Notifier.this.removeWatch(key);
33.148 - }
33.149 -
33.150 - @Override
33.151 - public int hashCode() {
33.152 - return hash;
33.153 - }
33.154 - } // KeyRef
33.155 -
33.156 -}
34.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
34.2 +++ b/masterfs/src/org/netbeans/modules/masterfs/watcher/NotifierAccessor.java Tue Dec 27 19:31:42 2011 +0100
34.3 @@ -0,0 +1,74 @@
34.4 +/*
34.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
34.6 + *
34.7 + * Copyright 2011 Oracle and/or its affiliates. All rights reserved.
34.8 + *
34.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
34.10 + * Other names may be trademarks of their respective owners.
34.11 + *
34.12 + * The contents of this file are subject to the terms of either the GNU
34.13 + * General Public License Version 2 only ("GPL") or the Common
34.14 + * Development and Distribution License("CDDL") (collectively, the
34.15 + * "License"). You may not use this file except in compliance with the
34.16 + * License. You can obtain a copy of the License at
34.17 + * http://www.netbeans.org/cddl-gplv2.html
34.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
34.19 + * specific language governing permissions and limitations under the
34.20 + * License. When distributing the software, include this License Header
34.21 + * Notice in each file and include the License file at
34.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
34.23 + * particular file as subject to the "Classpath" exception as provided
34.24 + * by Oracle in the GPL Version 2 section of the License file that
34.25 + * accompanied this code. If applicable, add the following below the
34.26 + * License Header, with the fields enclosed by brackets [] replaced by
34.27 + * your own identifying information:
34.28 + * "Portions Copyrighted [year] [name of copyright owner]"
34.29 + *
34.30 + * If you wish your version of this file to be governed by only the CDDL
34.31 + * or only the GPL Version 2, indicate your decision by adding
34.32 + * "[Contributor] elects to include this software in this distribution
34.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
34.34 + * single choice of license, a recipient has the option to distribute
34.35 + * your version of this file under either the CDDL, the GPL Version 2 or
34.36 + * to extend the choice of license to its licensees as provided above.
34.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
34.38 + * Version 2 license, then the option applies only if the new code is
34.39 + * made subject to such option by the copyright holder.
34.40 + *
34.41 + * Contributor(s):
34.42 + *
34.43 + * Portions Copyrighted 2011 Sun Microsystems, Inc.
34.44 + */
34.45 +package org.netbeans.modules.masterfs.watcher;
34.46 +
34.47 +import java.io.IOException;
34.48 +import org.netbeans.modules.masterfs.providers.Notifier;
34.49 +
34.50 +/** Access to protected methods of {@link Notifier}.
34.51 + *
34.52 + * @author Jaroslav Tulach <jtulach@netbeans.org>
34.53 + */
34.54 +public abstract class NotifierAccessor {
34.55 + private static NotifierAccessor DEFAULT;
34.56 + static {
34.57 + try {
34.58 + Class.forName(Notifier.class.getName(), true, Notifier.class.getClassLoader());
34.59 + } catch (ClassNotFoundException ex) {
34.60 + throw new IllegalStateException(ex);
34.61 + }
34.62 + }
34.63 + protected NotifierAccessor() {
34.64 + DEFAULT = this;
34.65 + }
34.66 +
34.67 + public static NotifierAccessor getDefault() {
34.68 + assert DEFAULT != null;
34.69 + return DEFAULT;
34.70 + }
34.71 +
34.72 + protected abstract <KEY> KEY addWatch(Notifier<KEY> n, String path) throws IOException;
34.73 + protected abstract <KEY> void removeWatch(Notifier<KEY> n, KEY key) throws IOException;
34.74 + protected abstract String nextEvent(Notifier<?> n) throws IOException, InterruptedException;
34.75 + protected abstract void start(Notifier<?> n) throws IOException;
34.76 + protected abstract void stop(Notifier<?> n) throws IOException;
34.77 +}
35.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
35.2 +++ b/masterfs/src/org/netbeans/modules/masterfs/watcher/NotifierKeyRef.java Tue Dec 27 19:31:42 2011 +0100
35.3 @@ -0,0 +1,104 @@
35.4 +/*
35.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
35.6 + *
35.7 + * Copyright 2011 Oracle and/or its affiliates. All rights reserved.
35.8 + *
35.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
35.10 + * Other names may be trademarks of their respective owners.
35.11 + *
35.12 + * The contents of this file are subject to the terms of either the GNU
35.13 + * General Public License Version 2 only ("GPL") or the Common
35.14 + * Development and Distribution License("CDDL") (collectively, the
35.15 + * "License"). You may not use this file except in compliance with the
35.16 + * License. You can obtain a copy of the License at
35.17 + * http://www.netbeans.org/cddl-gplv2.html
35.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
35.19 + * specific language governing permissions and limitations under the
35.20 + * License. When distributing the software, include this License Header
35.21 + * Notice in each file and include the License file at
35.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
35.23 + * particular file as subject to the "Classpath" exception as provided
35.24 + * by Oracle in the GPL Version 2 section of the License file that
35.25 + * accompanied this code. If applicable, add the following below the
35.26 + * License Header, with the fields enclosed by brackets [] replaced by
35.27 + * your own identifying information:
35.28 + * "Portions Copyrighted [year] [name of copyright owner]"
35.29 + *
35.30 + * If you wish your version of this file to be governed by only the CDDL
35.31 + * or only the GPL Version 2, indicate your decision by adding
35.32 + * "[Contributor] elects to include this software in this distribution
35.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
35.34 + * single choice of license, a recipient has the option to distribute
35.35 + * your version of this file under either the CDDL, the GPL Version 2 or
35.36 + * to extend the choice of license to its licensees as provided above.
35.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
35.38 + * Version 2 license, then the option applies only if the new code is
35.39 + * made subject to such option by the copyright holder.
35.40 + *
35.41 + * Contributor(s):
35.42 + *
35.43 + * Portions Copyrighted 2011 Sun Microsystems, Inc.
35.44 + */
35.45 +package org.netbeans.modules.masterfs.watcher;
35.46 +
35.47 +import org.netbeans.modules.masterfs.providers.Notifier;
35.48 +import java.io.IOException;
35.49 +import java.lang.ref.ReferenceQueue;
35.50 +import java.lang.ref.WeakReference;
35.51 +import java.util.logging.Level;
35.52 +import org.openide.filesystems.FileObject;
35.53 +
35.54 +/**
35.55 + *
35.56 + * @author Jaroslav Tulach <jtulach@netbeans.org>
35.57 + */
35.58 +class NotifierKeyRef<KEY> extends WeakReference<FileObject> {
35.59 + private final KEY key;
35.60 + private final int hash;
35.61 + private final Notifier<KEY> outer;
35.62 +
35.63 + public NotifierKeyRef(FileObject fo, KEY key, ReferenceQueue<FileObject> queue, final Notifier<KEY> outer) {
35.64 + super(fo, queue);
35.65 + this.outer = outer;
35.66 + this.key = key;
35.67 + this.hash = fo.hashCode();
35.68 + if (key != null) {
35.69 + Watcher.LOG.log(Level.FINE, "Adding watch for {0}", key);
35.70 + }
35.71 + }
35.72 +
35.73 + @Override
35.74 + public FileObject get() {
35.75 + return super.get();
35.76 + }
35.77 +
35.78 + @Override
35.79 + public boolean equals(Object obj) {
35.80 + if (obj == this) {
35.81 + return true;
35.82 + }
35.83 + try {
35.84 + NotifierKeyRef kr = (NotifierKeyRef) obj;
35.85 + FileObject mine = get();
35.86 + FileObject theirs = kr.get();
35.87 + if (mine == null) {
35.88 + return theirs == null;
35.89 + } else {
35.90 + return mine.equals(theirs);
35.91 + }
35.92 + } catch (ClassCastException ex) {
35.93 + return false;
35.94 + }
35.95 + }
35.96 +
35.97 + final void removeWatch() throws IOException {
35.98 + Watcher.LOG.log(Level.FINE, "Removing watch for {0}", key);
35.99 + NotifierAccessor.getDefault().removeWatch(outer, key);
35.100 + }
35.101 +
35.102 + @Override
35.103 + public int hashCode() {
35.104 + return hash;
35.105 + }
35.106 +
35.107 +}
36.1 --- a/masterfs/src/org/netbeans/modules/masterfs/watcher/OSXNotifier.java Sat Dec 24 00:28:54 2011 +0000
36.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
36.3 @@ -1,290 +0,0 @@
36.4 -/*
36.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
36.6 - *
36.7 - * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
36.8 - *
36.9 - * The contents of this file are subject to the terms of either the GNU
36.10 - * General Public License Version 2 only ("GPL") or the Common
36.11 - * Development and Distribution License("CDDL") (collectively, the
36.12 - * "License"). You may not use this file except in compliance with the
36.13 - * License. You can obtain a copy of the License at
36.14 - * http://www.netbeans.org/cddl-gplv2.html
36.15 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
36.16 - * specific language governing permissions and limitations under the
36.17 - * License. When distributing the software, include this License Header
36.18 - * Notice in each file and include the License file at
36.19 - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
36.20 - * particular file as subject to the "Classpath" exception as provided
36.21 - * by Sun in the GPL Version 2 section of the License file that
36.22 - * accompanied this code. If applicable, add the following below the
36.23 - * License Header, with the fields enclosed by brackets [] replaced by
36.24 - * your own identifying information:
36.25 - * "Portions Copyrighted [year] [name of copyright owner]"
36.26 - *
36.27 - * If you wish your version of this file to be governed by only the CDDL
36.28 - * or only the GPL Version 2, indicate your decision by adding
36.29 - * "[Contributor] elects to include this software in this distribution
36.30 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
36.31 - * single choice of license, a recipient has the option to distribute
36.32 - * your version of this file under either the CDDL, the GPL Version 2 or
36.33 - * to extend the choice of license to its licensees as provided above.
36.34 - * However, if you add GPL Version 2 code and therefore, elected the GPL
36.35 - * Version 2 license, then the option applies only if the new code is
36.36 - * made subject to such option by the copyright holder.
36.37 - *
36.38 - * Contributor(s):
36.39 - *
36.40 - * Portions Copyrighted 2010 Sun Microsystems, Inc.
36.41 - */
36.42 -
36.43 -package org.netbeans.modules.masterfs.watcher;
36.44 -
36.45 -import com.sun.jna.Callback;
36.46 -import com.sun.jna.Library;
36.47 -import com.sun.jna.Native;
36.48 -import com.sun.jna.NativeLong;
36.49 -import com.sun.jna.Pointer;
36.50 -import java.io.IOException;
36.51 -import java.io.InterruptedIOException;
36.52 -import java.util.concurrent.BlockingQueue;
36.53 -import java.util.concurrent.Exchanger;
36.54 -import java.util.concurrent.ExecutorService;
36.55 -import java.util.concurrent.Executors;
36.56 -import java.util.concurrent.LinkedBlockingQueue;
36.57 -import java.util.concurrent.ThreadFactory;
36.58 -import java.util.logging.Level;
36.59 -import java.util.logging.Logger;
36.60 -
36.61 -/**
36.62 - *
36.63 - * @author Tomas Zezula
36.64 - */
36.65 -public class OSXNotifier extends Notifier<Void> {
36.66 - private static final Level DEBUG_LOG_LEVEL = Level.FINE;
36.67 - private static final Level PERF_LOG_LEVEL = Level.FINE;
36.68 - private static final long kFSEventStreamEventIdSinceNow = 0xFFFFFFFFFFFFFFFFL;
36.69 - private static final int kFSEventStreamCreateFlagNoDefer = 0x00000002;
36.70 - private static final int kFSEventStreamEventFlagMustScanSubDirs = 0x00000001;
36.71 - private static final int kFSEventStreamEventFlagMount = 0x00000040;
36.72 - private static final int kFSEventStreamEventFlagUnmount = 0x00000080;
36.73 - private static final double LATENCY = 1.0f;
36.74 - private static final int ENC_MAC_ROMAN = 0;
36.75 - private static final String DEFAULT_RUN_LOOP_MODE = "kCFRunLoopDefaultMode"; //NOI18N
36.76 - private static final Logger LOG = Logger.getLogger(OSXNotifier.class.getName());
36.77 - private final CoreFoundation cf;
36.78 - private final CoreServices cs;
36.79 - private final EventCallback callback;
36.80 - private final BlockingQueue<String> events;
36.81 - //@GuardedBy("this")
36.82 - private ExecutorService worker;
36.83 - //@GuardedBy("this")
36.84 - private Pointer[] rtData;
36.85 -
36.86 - private static final String ALL_CHANGE = "ALL-CHANGE"; //xxx - shouldn't be global in Notifier rather than using null?
36.87 -
36.88 - public OSXNotifier() {
36.89 - cf = (CoreFoundation) Native.loadLibrary("CoreFoundation",CoreFoundation.class); //NOI18N
36.90 - cs = (CoreServices) Native.loadLibrary("CoreServices",CoreServices.class); //NOI18N
36.91 - callback = new EventCallbackImpl();
36.92 - events = new LinkedBlockingQueue<String>();
36.93 - }
36.94 -
36.95 -
36.96 - public @Override Void addWatch(String path) throws IOException {
36.97 - return null;
36.98 - }
36.99 -
36.100 - public @Override void removeWatch(Void key) throws IOException {
36.101 - // ignore
36.102 - }
36.103 -
36.104 - public @Override String nextEvent() throws IOException, InterruptedException {
36.105 - final String event = events.take();
36.106 - return event == ALL_CHANGE ? null : event;
36.107 - }
36.108 -
36.109 - public synchronized void start() throws IOException {
36.110 - if (worker != null) {
36.111 - throw new IllegalStateException("FileSystemWatcher already started."); //NOI18N
36.112 - }
36.113 - worker = Executors.newSingleThreadExecutor(new DaemonThreadFactory());
36.114 - final Exchanger<Object> exchanger = new Exchanger<Object>();
36.115 - worker.execute(new Runnable() {
36.116 - public @Override void run () {
36.117 - try {
36.118 - Pointer[] _rtData = null;
36.119 - try {
36.120 - _rtData = createFSEventStream();
36.121 - } catch (Throwable ex) {
36.122 - exchanger.exchange(ex);
36.123 - } finally {
36.124 - if (_rtData != null) {
36.125 - exchanger.exchange(_rtData);
36.126 - cf.CFRunLoopRun();
36.127 - }
36.128 - }
36.129 - } catch (InterruptedException ie) {
36.130 - LOG.log(Level.WARNING, "Watcher interruped during start", ie); //NOI18N
36.131 - }
36.132 - }
36.133 - });
36.134 - final Object _data;
36.135 - try {
36.136 - _data = exchanger.exchange(null);
36.137 - } catch (InterruptedException ex) {
36.138 - throw (InterruptedIOException)new InterruptedIOException().initCause(ex);
36.139 - }
36.140 - assert _data != null;
36.141 - if (_data instanceof Throwable) {
36.142 - worker.shutdown();
36.143 - worker = null;
36.144 - throw new IOException((Throwable)_data);
36.145 - } else {
36.146 - rtData = (Pointer[]) _data;
36.147 - }
36.148 - }
36.149 -
36.150 - @Override
36.151 - public synchronized void stop() throws IOException {
36.152 - if (worker == null) {
36.153 - throw new IllegalStateException("FileSystemWatcher is not started."); //NOI18N
36.154 - }
36.155 - assert rtData != null;
36.156 - assert rtData.length == 2;
36.157 - assert rtData[0] != null;
36.158 - assert rtData[1] != null;
36.159 - cs.FSEventStreamStop(rtData[0]);
36.160 - cs.FSEventStreamInvalidate(rtData[0]);
36.161 - cs.FSEventStreamRelease(rtData[0]);
36.162 - cf.CFRunLoopStop(rtData[1]);
36.163 - worker.shutdown();
36.164 - worker = null;
36.165 - rtData = null;
36.166 - }
36.167 -
36.168 - private Pointer[] createFSEventStream() throws IOException {
36.169 - final Pointer root = cf.CFStringCreateWithCString(Pointer.NULL,"/",ENC_MAC_ROMAN); //NOI18N
36.170 - if (root == Pointer.NULL) {
36.171 - throw new IOException("Path creation failed."); //NOI18N
36.172 - }
36.173 - final Pointer arr = cf.CFArrayCreateMutable(Pointer.NULL, new NativeLong(1), Pointer.NULL);
36.174 - if (arr == Pointer.NULL) {
36.175 - throw new IOException("Path list creation failed."); //NOI18N
36.176 - }
36.177 - cf.CFArrayAppendValue(arr, root);
36.178 -
36.179 - final Pointer eventStream = cs.FSEventStreamCreate(Pointer.NULL, callback, Pointer.NULL, arr, kFSEventStreamEventIdSinceNow, LATENCY, kFSEventStreamCreateFlagNoDefer);
36.180 - if (eventStream == Pointer.NULL) {
36.181 - throw new IOException("Creation of FSEventStream failed."); //NOI18N
36.182 - }
36.183 - final Pointer loop = cf.CFRunLoopGetCurrent();
36.184 - if (eventStream == Pointer.NULL) {
36.185 - throw new IOException("Cannot find run loop for caller."); //NOI18N
36.186 - }
36.187 - final Pointer kCFRunLoopDefaultMode = findDefaultMode(loop);
36.188 - if (kCFRunLoopDefaultMode == null) {
36.189 - throw new IOException("Caller has no defaul run loop mode."); //NOI18N
36.190 - }
36.191 - cs.FSEventStreamScheduleWithRunLoop(eventStream, loop, kCFRunLoopDefaultMode);
36.192 - if (LOG.isLoggable(DEBUG_LOG_LEVEL)) {
36.193 - LOG.log(DEBUG_LOG_LEVEL, getStreamDescription(eventStream));
36.194 - }
36.195 - cs.FSEventStreamStart(eventStream);
36.196 - return new Pointer[] {eventStream, loop};
36.197 - }
36.198 -
36.199 - private Pointer findDefaultMode(final Pointer runLoop) {
36.200 - final Pointer modes = cf.CFRunLoopCopyAllModes(runLoop);
36.201 - if (modes != Pointer.NULL) {
36.202 - final int modesCount = cf.CFArrayGetCount(modes).intValue();
36.203 - for (int i=0; i< modesCount; i++) {
36.204 - final Pointer mode = cf.CFArrayGetValueAtIndex(modes, new NativeLong(i));
36.205 - if (mode != Pointer.NULL && DEFAULT_RUN_LOOP_MODE.equals(cf.CFStringGetCStringPtr(mode, ENC_MAC_ROMAN))) {
36.206 - return mode;
36.207 - }
36.208 - }
36.209 - }
36.210 - return null;
36.211 - }
36.212 -
36.213 - private String getStreamDescription(final Pointer eventStream) {
36.214 - final Pointer desc = cs.FSEventStreamCopyDescription(eventStream);
36.215 - return desc == Pointer.NULL ? "" : cf.CFStringGetCStringPtr(desc, ENC_MAC_ROMAN); //NOI18N
36.216 - }
36.217 -
36.218 - public static interface EventCallback extends Callback {
36.219 - void invoke(Pointer streamRef,
36.220 - Pointer clientCallBackInfo,
36.221 - NativeLong numEvents,
36.222 - Pointer eventPaths,
36.223 - Pointer eventFlags,
36.224 - Pointer eventIds);
36.225 - }
36.226 -
36.227 - public static interface CoreFoundation extends Library {
36.228 - Pointer CFRunLoopGetCurrent();
36.229 - void CFRunLoopRun();
36.230 - void CFRunLoopStop(Pointer loop);
36.231 - Pointer CFRunLoopCopyAllModes(Pointer loop);
36.232 -
36.233 - Pointer CFArrayCreateMutable(Pointer allocator, NativeLong size, Pointer callback);
36.234 - void CFArrayAppendValue(Pointer theArray, Pointer value);
36.235 - Pointer CFArrayGetValueAtIndex(Pointer theArray, NativeLong index);
36.236 - NativeLong CFArrayGetCount(Pointer theArray);
36.237 -
36.238 - Pointer CFStringCreateWithCString(Pointer allocator, String string, int encoding);
36.239 - String CFStringGetCStringPtr(Pointer theString, int encoding);
36.240 - }
36.241 -
36.242 - public static interface CoreServices extends Library {
36.243 - Pointer FSEventStreamCreate(Pointer allocator, EventCallback callback, Pointer ctx, Pointer pathsToWatch, long sinceWhen, double latency, int flags);
36.244 - Pointer FSEventStreamCopyDescription(Pointer stream);
36.245 - void FSEventStreamScheduleWithRunLoop(Pointer stream, Pointer loop, Pointer mode);
36.246 - void FSEventStreamUnscheduleFromRunLoop(Pointer stream, Pointer loop, Pointer mode);
36.247 - void FSEventStreamStart(Pointer stream);
36.248 - void FSEventStreamStop(Pointer stream);
36.249 - void FSEventStreamInvalidate(Pointer stream);
36.250 - void FSEventStreamRelease(Pointer stream);
36.251 - }
36.252 -
36.253 - private static class DaemonThreadFactory implements ThreadFactory {
36.254 - @Override
36.255 - public Thread newThread(Runnable r) {
36.256 - final Thread t = new Thread(r);
36.257 - t.setDaemon(true);
36.258 - return t;
36.259 - }
36.260 -
36.261 - }
36.262 -
36.263 - private class EventCallbackImpl implements EventCallback {
36.264 - @Override
36.265 - public void invoke(Pointer streamRef, Pointer clientCallBackInfo, NativeLong numEvents, Pointer eventPaths, Pointer eventFlags, Pointer eventIds) {
36.266 - final long st = System.currentTimeMillis();
36.267 - final int length = numEvents.intValue();
36.268 - final Pointer[] pointers = eventPaths.getPointerArray(0, length);
36.269 - int flags[];
36.270 - if (eventFlags == null) {
36.271 - flags = new int[length];
36.272 - LOG.log(DEBUG_LOG_LEVEL, "FSEventStreamCallback eventFlags == null, expected int[] of size {0}", length); //NOI18N
36.273 - } else {
36.274 - flags = eventFlags.getIntArray(0, length);
36.275 - }
36.276 - for (int i=0; i<length; i++) {
36.277 - final Pointer p = pointers[i];
36.278 - int flag = flags[i];
36.279 - final String path = p.getString(0);
36.280 -
36.281 - if ((flag & kFSEventStreamEventFlagMustScanSubDirs) == kFSEventStreamEventFlagMustScanSubDirs ||
36.282 - (flag & kFSEventStreamEventFlagMount) == kFSEventStreamEventFlagMount ||
36.283 - (flag & kFSEventStreamEventFlagUnmount) == kFSEventStreamEventFlagUnmount) {
36.284 - events.add(ALL_CHANGE);
36.285 - } else {
36.286 - events.add(path);
36.287 - }
36.288 - LOG.log(DEBUG_LOG_LEVEL, "Event on {0}", new Object[]{path});
36.289 - }
36.290 - LOG.log(PERF_LOG_LEVEL, "Callback time: {0}", (System.currentTimeMillis() - st));
36.291 - }
36.292 - }
36.293 -}
37.1 --- a/masterfs/src/org/netbeans/modules/masterfs/watcher/Watcher.java Sat Dec 24 00:28:54 2011 +0000
37.2 +++ b/masterfs/src/org/netbeans/modules/masterfs/watcher/Watcher.java Tue Dec 27 19:31:42 2011 +0100
37.3 @@ -39,11 +39,11 @@
37.4
37.5 package org.netbeans.modules.masterfs.watcher;
37.6
37.7 +import org.netbeans.modules.masterfs.providers.Notifier;
37.8 import java.awt.Image;
37.9 import java.io.File;
37.10 import java.io.IOException;
37.11 import java.lang.ref.ReferenceQueue;
37.12 -import java.util.HashMap;
37.13 import java.util.HashSet;
37.14 import java.util.List;
37.15 import java.util.Map;
37.16 @@ -57,10 +57,9 @@
37.17 import org.netbeans.modules.masterfs.providers.ProvidedExtensions;
37.18 import org.openide.filesystems.FileObject;
37.19 import org.netbeans.modules.masterfs.providers.AnnotationProvider;
37.20 -import org.netbeans.modules.masterfs.watcher.Notifier.KeyRef;
37.21 import org.openide.util.Lookup;
37.22 +import org.openide.util.Lookup.Item;
37.23 import org.openide.util.RequestProcessor;
37.24 -import org.openide.util.Utilities;
37.25 import org.openide.util.lookup.ServiceProvider;
37.26 import org.openide.util.lookup.ServiceProviders;
37.27
37.28 @@ -75,8 +74,7 @@
37.29 public final class Watcher extends AnnotationProvider {
37.30 static final Logger LOG = Logger.getLogger(Watcher.class.getName());
37.31 private static final Map<FileObject,int[]> MODIFIED = new WeakHashMap<FileObject, int[]>();
37.32 -
37.33 - private Ext<?> ext;
37.34 + private final Ext<?> ext;
37.35
37.36 public Watcher() {
37.37 // Watcher disabled manually or for some tests
37.38 @@ -84,14 +82,9 @@
37.39 ext = null;
37.40 return;
37.41 }
37.42 -
37.43 ext = make(getNotifierForPlatform());
37.44 }
37.45
37.46 - final void installNotifier(Notifier<?> n) {
37.47 - ext = make(n);
37.48 - }
37.49 -
37.50 private static Ext<?> ext() {
37.51 final Watcher w = Lookup.getDefault().lookup(Watcher.class);
37.52 return w == null ? null : w.ext;
37.53 @@ -182,7 +175,7 @@
37.54 private final ReferenceQueue<FileObject> REF = new ReferenceQueue<FileObject>();
37.55 private final Notifier<KEY> impl;
37.56 private final Object LOCK = new Object();
37.57 - private final Set<KeyRef> references = new HashSet<KeyRef>();
37.58 + private final Set<NotifierKeyRef> references = new HashSet<NotifierKeyRef>();
37.59 private final Thread watcher;
37.60 private volatile boolean shutdown;
37.61
37.62 @@ -221,7 +214,7 @@
37.63 LOG.log(Level.INFO, "Exception while clearing the queue", ex);
37.64 }
37.65 synchronized (LOCK) {
37.66 - KeyRef kr = impl.new KeyRef(fo, null, null);
37.67 + NotifierKeyRef<KEY> kr = new NotifierKeyRef<KEY>(fo, null, null, impl);
37.68 return getReferences().contains(kr);
37.69 }
37.70 }
37.71 @@ -236,13 +229,13 @@
37.72 LOG.log(Level.INFO, "Exception while clearing the queue", ex);
37.73 }
37.74 synchronized (LOCK) {
37.75 - KeyRef kr = impl.new KeyRef(fo, null, null);
37.76 + NotifierKeyRef<KEY> kr = new NotifierKeyRef<KEY>(fo, null, null, impl);
37.77 if (getReferences().contains(kr)) {
37.78 return;
37.79 }
37.80
37.81 try {
37.82 - getReferences().add(impl.new KeyRef(fo, impl.addWatch(fo.getPath()), REF));
37.83 + getReferences().add(new NotifierKeyRef<KEY>(fo, NotifierAccessor.getDefault().addWatch(impl, fo.getPath()), REF, impl));
37.84 } catch (IOException ex) {
37.85 // XXX: handle resource overflow gracefully
37.86 LOG.log(Level.WARNING, "Cannot add filesystem watch for {0}: {1}", new Object[] {fo.getPath(), ex});
37.87 @@ -254,12 +247,12 @@
37.88 final void unregister(FileObject fo) {
37.89 assert fo.isFolder() : "Should be a folder: " + fo;
37.90 synchronized (LOCK) {
37.91 - final KeyRef[] equalOne = new KeyRef[1];
37.92 - KeyRef kr = impl.new KeyRef(fo, null, null) {
37.93 + final NotifierKeyRef[] equalOne = new NotifierKeyRef[1];
37.94 + NotifierKeyRef<KEY> kr = new NotifierKeyRef<KEY>(fo, null, null, impl) {
37.95 @Override
37.96 public boolean equals(Object obj) {
37.97 if (super.equals(obj)) {
37.98 - equalOne[0] = (KeyRef)obj;
37.99 + equalOne[0] = (NotifierKeyRef)obj;
37.100 return true;
37.101 } else {
37.102 return false;
37.103 @@ -287,7 +280,7 @@
37.104
37.105 final void clearQueue() throws IOException {
37.106 for (;;) {
37.107 - KeyRef kr = (KeyRef)REF.poll();
37.108 + NotifierKeyRef kr = (NotifierKeyRef)REF.poll();
37.109 if (kr == null) {
37.110 break;
37.111 }
37.112 @@ -302,12 +295,12 @@
37.113 while (!shutdown) {
37.114 try {
37.115 clearQueue();
37.116 - String path = impl.nextEvent();
37.117 + String path = NotifierAccessor.getDefault().nextEvent(impl);
37.118 LOG.log(Level.FINEST, "nextEvent: {0}", path);
37.119 if (path == null) { // all dirty
37.120 Set<FileObject> set = new HashSet<FileObject>();
37.121 synchronized (LOCK) {
37.122 - for (KeyRef kr : getReferences()) {
37.123 + for (NotifierKeyRef kr : getReferences()) {
37.124 final FileObject ref = kr.get();
37.125 if (ref != null) {
37.126 set.add(ref);
37.127 @@ -325,7 +318,7 @@
37.128 }
37.129 if (fo != null) {
37.130 synchronized (LOCK) {
37.131 - KeyRef kr = impl.new KeyRef(fo, null, null);
37.132 + NotifierKeyRef<KEY> kr = new NotifierKeyRef<KEY>(fo, null, null, impl);
37.133 if (getReferences().contains(kr)) {
37.134 enqueue(fo);
37.135 }
37.136 @@ -347,11 +340,11 @@
37.137 final void shutdown() throws IOException, InterruptedException {
37.138 shutdown = true;
37.139 watcher.interrupt();
37.140 - impl.stop();
37.141 + NotifierAccessor.getDefault().stop(impl);
37.142 watcher.join(1000);
37.143 }
37.144
37.145 - private Set<KeyRef> getReferences() {
37.146 + private Set<NotifierKeyRef> getReferences() {
37.147 assert Thread.holdsLock(LOCK);
37.148 return references;
37.149 }
37.150 @@ -416,35 +409,19 @@
37.151 * @return a suitable {@link Notifier} implementation or <code>null</code>.
37.152 */
37.153 private static Notifier<?> getNotifierForPlatform() {
37.154 - try {
37.155 - if (Utilities.isWindows()) {
37.156 - return new WindowsNotifier();
37.157 + for (Item<Notifier> item : Lookup.getDefault().lookupResult(Notifier.class).allItems()) {
37.158 + try {
37.159 + final Notifier notifier = item.getInstance();
37.160 + NotifierAccessor.getDefault().start(notifier);
37.161 + return notifier;
37.162 + } catch (IOException ex) {
37.163 + LOG.log(Level.INFO, "Notifier {0} refused to be initialized", item.getType()); // NOI18N
37.164 + LOG.log(Level.FINE, null, ex);
37.165 + } catch (Exception ex) {
37.166 + LOG.log(Level.INFO, "Exception while instantiating " + item, ex);
37.167 + } catch (LinkageError ex) {
37.168 + LOG.log(Level.INFO, "Linkage error for " + item, ex);
37.169 }
37.170 - if (Utilities.getOperatingSystem() == Utilities.OS_LINUX) {
37.171 - return new LinuxNotifier();
37.172 - }
37.173 - if (Utilities.getOperatingSystem() == Utilities.OS_MAC) {
37.174 - try {
37.175 - final OSXNotifier notifier = new OSXNotifier();
37.176 - notifier.start();
37.177 - return notifier;
37.178 - } catch (IOException ioe) {
37.179 - LOG.log(Level.INFO, null, ioe);
37.180 - }
37.181 - }
37.182 - if (Utilities.getOperatingSystem() == Utilities.OS_SOLARIS &&
37.183 - Boolean.getBoolean("org.netbeans.modules.masterfs.watcher.FAM")) {
37.184 - // disabled by default, see IZ 206670
37.185 - try {
37.186 - return new FAMNotifier();
37.187 - } catch (Exception e) {
37.188 - LOG.log(Level.INFO, null, e);
37.189 - } catch (LinkageError x) {
37.190 - //this is normal not to have fam in the system, do not report
37.191 - }
37.192 - }
37.193 - } catch (LinkageError x) {
37.194 - LOG.warning(x.toString());
37.195 }
37.196 return null;
37.197 }
38.1 --- a/masterfs/src/org/netbeans/modules/masterfs/watcher/WindowsNotifier.java Sat Dec 24 00:28:54 2011 +0000
38.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
38.3 @@ -1,413 +0,0 @@
38.4 -/*
38.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
38.6 - *
38.7 - * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
38.8 - *
38.9 - * The contents of this file are subject to the terms of either the GNU
38.10 - * General Public License Version 2 only ("GPL") or the Common
38.11 - * Development and Distribution License("CDDL") (collectively, the
38.12 - * "License"). You may not use this file except in compliance with the
38.13 - * License. You can obtain a copy of the License at
38.14 - * http://www.netbeans.org/cddl-gplv2.html
38.15 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
38.16 - * specific language governing permissions and limitations under the
38.17 - * License. When distributing the software, include this License Header
38.18 - * Notice in each file and include the License file at
38.19 - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
38.20 - * particular file as subject to the "Classpath" exception as provided
38.21 - * by Sun in the GPL Version 2 section of the License file that
38.22 - * accompanied this code. If applicable, add the following below the
38.23 - * License Header, with the fields enclosed by brackets [] replaced by
38.24 - * your own identifying information:
38.25 - * "Portions Copyrighted [year] [name of copyright owner]"
38.26 - *
38.27 - * If you wish your version of this file to be governed by only the CDDL
38.28 - * or only the GPL Version 2, indicate your decision by adding
38.29 - * "[Contributor] elects to include this software in this distribution
38.30 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
38.31 - * single choice of license, a recipient has the option to distribute
38.32 - * your version of this file under either the CDDL, the GPL Version 2 or
38.33 - * to extend the choice of license to its licensees as provided above.
38.34 - * However, if you add GPL Version 2 code and therefore, elected the GPL
38.35 - * Version 2 license, then the option applies only if the new code is
38.36 - * made subject to such option by the copyright holder.
38.37 - *
38.38 - * Contributor(s):
38.39 - *
38.40 - * Portions Copyrighted 2010 Sun Microsystems, Inc.
38.41 - */
38.42 -
38.43 -package org.netbeans.modules.masterfs.watcher;
38.44 -
38.45 -import com.sun.jna.FromNativeContext;
38.46 -import com.sun.jna.IntegerType;
38.47 -import com.sun.jna.Library;
38.48 -import com.sun.jna.Native;
38.49 -import com.sun.jna.Pointer;
38.50 -import com.sun.jna.PointerType;
38.51 -import com.sun.jna.Structure;
38.52 -import com.sun.jna.ptr.ByReference;
38.53 -import java.io.File;
38.54 -import java.io.IOException;
38.55 -import java.util.HashMap;
38.56 -import java.util.Map;
38.57 -
38.58 -import com.sun.jna.ptr.IntByReference;
38.59 -import com.sun.jna.ptr.PointerByReference;
38.60 -import com.sun.jna.win32.StdCallLibrary;
38.61 -import com.sun.jna.win32.W32APIFunctionMapper;
38.62 -import com.sun.jna.win32.W32APITypeMapper;
38.63 -import java.io.InterruptedIOException;
38.64 -import java.util.concurrent.BlockingQueue;
38.65 -import java.util.concurrent.LinkedBlockingQueue;
38.66 -import java.util.logging.Level;
38.67 -
38.68 -/**
38.69 - * A {@link Notifier} implementation using Win32 API ReadDirectoryChangesW.
38.70 - * Based on JNA examples and platform library stubs.
38.71 - *
38.72 - * @author nenik
38.73 - */
38.74 -public class WindowsNotifier extends Notifier<Void> {
38.75 -
38.76 - public static final class HANDLE extends PointerType {
38.77 - private boolean immutable;
38.78 - public HANDLE() { }
38.79 - public HANDLE(Pointer p) {
38.80 - setPointer(p);
38.81 - immutable = true;
38.82 - }
38.83 -
38.84 - /** Override to the appropriate object for INVALID_HANDLE_VALUE. */
38.85 - @Override
38.86 - public Object fromNative(Object nativeValue, FromNativeContext context) {
38.87 - Object o = super.fromNative(nativeValue, context);
38.88 - if (INVALID_HANDLE_VALUE.equals(o))
38.89 - return INVALID_HANDLE_VALUE;
38.90 - return o;
38.91 - }
38.92 -
38.93 - @Override
38.94 - public void setPointer(Pointer p) {
38.95 - if (immutable) {
38.96 - throw new UnsupportedOperationException("immutable reference");
38.97 - }
38.98 -
38.99 - super.setPointer(p);
38.100 - }
38.101 - }
38.102 -
38.103 - public static class ULONG_PTR extends IntegerType {
38.104 - public ULONG_PTR() {
38.105 - this(0);
38.106 - }
38.107 -
38.108 - public ULONG_PTR(long value) {
38.109 - super(Pointer.SIZE, value);
38.110 - }
38.111 - }
38.112 -
38.113 - public static class OVERLAPPED extends Structure {
38.114 - public ULONG_PTR Internal;
38.115 - public ULONG_PTR InternalHigh;
38.116 - public int Offset;
38.117 - public int OffsetHigh;
38.118 - public HANDLE hEvent;
38.119 - }
38.120 -
38.121 - public static HANDLE INVALID_HANDLE_VALUE = new HANDLE(Pointer.createConstant(
38.122 - Pointer.SIZE == 8 ? -1 : 0xFFFFFFFFL));
38.123 -
38.124 -
38.125 -
38.126 - public static class HANDLEByReference extends ByReference {
38.127 -
38.128 - public HANDLEByReference() {
38.129 - this(null);
38.130 - }
38.131 -
38.132 - public HANDLEByReference(HANDLE h) {
38.133 - super(Pointer.SIZE);
38.134 - setValue(h);
38.135 - }
38.136 -
38.137 - public void setValue(HANDLE h) {
38.138 - getPointer().setPointer(0, h != null ? h.getPointer() : null);
38.139 - }
38.140 -
38.141 - public HANDLE getValue() {
38.142 - Pointer p = getPointer().getPointer(0);
38.143 - if (p == null)
38.144 - return null;
38.145 - if (INVALID_HANDLE_VALUE.getPointer().equals(p))
38.146 - return INVALID_HANDLE_VALUE;
38.147 - HANDLE h = new HANDLE();
38.148 - h.setPointer(p);
38.149 - return h;
38.150 - }
38.151 - }
38.152 -
38.153 - public static class FILE_NOTIFY_INFORMATION extends Structure {
38.154 - public int NextEntryOffset;
38.155 - public int Action;
38.156 - public int FileNameLength;
38.157 - // filename is not nul-terminated, so we can't use a String/WString
38.158 - public char[] FileName = new char[1];
38.159 -
38.160 - private FILE_NOTIFY_INFORMATION() {}
38.161 -
38.162 - public FILE_NOTIFY_INFORMATION(int size) {
38.163 - if (size < size()) {
38.164 - throw new IllegalArgumentException("Size must greater than "
38.165 - + size() + ", requested " + size);
38.166 - }
38.167 - allocateMemory(size);
38.168 - }
38.169 -
38.170 - /** WARNING: this filename may be either the short or long form of the filename. */
38.171 - public String getFilename() {
38.172 - return new String(FileName, 0, FileNameLength/2);
38.173 - }
38.174 -
38.175 - @Override
38.176 - public void read() {
38.177 - // avoid reading filename until we know how long it is
38.178 - FileName = new char[0];
38.179 - super.read();
38.180 - FileName = getPointer().getCharArray(12, FileNameLength/2);
38.181 - }
38.182 -
38.183 - public FILE_NOTIFY_INFORMATION next() {
38.184 - if (NextEntryOffset == 0)
38.185 - return null;
38.186 - FILE_NOTIFY_INFORMATION next = new FILE_NOTIFY_INFORMATION();
38.187 - next.useMemory(getPointer(), NextEntryOffset);
38.188 - next.read();
38.189 - return next;
38.190 - }
38.191 - }
38.192 -
38.193 - public static class SECURITY_ATTRIBUTES extends Structure {
38.194 - public final int nLength = size();
38.195 - public Pointer lpSecurityDescriptor;
38.196 - public boolean bInheritHandle;
38.197 - }
38.198 -
38.199 - interface Kernel32 extends StdCallLibrary {
38.200 - HANDLE CreateFile(String lpFileName, int dwDesiredAccess, int dwShareMode,
38.201 - SECURITY_ATTRIBUTES lpSecurityAttributes, int dwCreationDisposition,
38.202 - int dwFlagsAndAttributes, HANDLE hTemplateFile);
38.203 -
38.204 - HANDLE CreateIoCompletionPort(HANDLE FileHandle, HANDLE ExistingCompletionPort,
38.205 - Pointer CompletionKey, int NumberOfConcurrentThreads);
38.206 -
38.207 - int GetLastError();
38.208 -
38.209 - boolean GetQueuedCompletionStatus(HANDLE CompletionPort,
38.210 - IntByReference lpNumberOfBytes, ByReference lpCompletionKey,
38.211 - PointerByReference lpOverlapped, int dwMilliseconds);
38.212 -
38.213 - boolean PostQueuedCompletionStatus(HANDLE CompletionPort,
38.214 - int dwNumberOfBytesTransferred, Pointer dwCompletionKey,
38.215 - OVERLAPPED lpOverlapped);
38.216 -
38.217 - boolean CloseHandle(HANDLE hObject);
38.218 -
38.219 - interface OVERLAPPED_COMPLETION_ROUTINE extends StdCallCallback {
38.220 - void callback(int errorCode, int nBytesTransferred,
38.221 - OVERLAPPED overlapped);
38.222 - }
38.223 -
38.224 -
38.225 - public boolean ReadDirectoryChangesW(HANDLE directory,
38.226 - FILE_NOTIFY_INFORMATION info, int length, boolean watchSubtree,
38.227 - int notifyFilter, IntByReference bytesReturned, OVERLAPPED overlapped,
38.228 - OVERLAPPED_COMPLETION_ROUTINE completionRoutine);
38.229 -
38.230 -
38.231 - }
38.232 -
38.233 - final static Kernel32 KERNEL32 = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class,
38.234 - new HashMap() {{
38.235 - put(Library.OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
38.236 - put(Library.OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE);
38.237 - }});
38.238 -
38.239 -
38.240 -
38.241 - public WindowsNotifier() { // prepare port, start thread?
38.242 - }
38.243 -
38.244 - public @Override void removeWatch(Void key) throws IOException {}
38.245 -
38.246 -
38.247 - public @Override String nextEvent() throws IOException, InterruptedException {
38.248 - return events.take();
38.249 - }
38.250 -
38.251 - public static final int INFINITE = 0xFFFFFFFF;
38.252 -
38.253 - public static final int FILE_NOTIFY_CHANGE_NAME = 0x00000003;
38.254 - public static final int FILE_NOTIFY_CHANGE_ATTRIBUTES = 0x00000004;
38.255 - public static final int FILE_NOTIFY_CHANGE_SIZE = 0x00000008;
38.256 - public static final int FILE_NOTIFY_CHANGE_LAST_WRITE = 0x00000010;
38.257 - public static final int FILE_NOTIFY_CHANGE_CREATION = 0x00000040;
38.258 - public static final int FILE_NOTIFY_CHANGE_SECURITY = 0x00000100;
38.259 -
38.260 - private static final int NOTIFY_MASK =
38.261 - FILE_NOTIFY_CHANGE_NAME |
38.262 - FILE_NOTIFY_CHANGE_ATTRIBUTES |
38.263 - FILE_NOTIFY_CHANGE_SIZE |
38.264 - FILE_NOTIFY_CHANGE_LAST_WRITE |
38.265 - FILE_NOTIFY_CHANGE_CREATION |
38.266 - FILE_NOTIFY_CHANGE_SECURITY;
38.267 -
38.268 - public static final int FILE_LIST_DIRECTORY = 0x00000001;
38.269 - public static final int OPEN_EXISTING = 3;
38.270 -
38.271 - public static final int FILE_SHARE_READ = 0x00000001;
38.272 - public static final int FILE_SHARE_WRITE = 0x00000002;
38.273 - public static final int FILE_SHARE_DELETE = 0x00000004;
38.274 -
38.275 - public static final int FILE_FLAG_OVERLAPPED = 0x40000000;
38.276 - public static final int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
38.277 -
38.278 - private class FileInfo {
38.279 - public final String path;
38.280 - public final HANDLE handle;
38.281 - public final FILE_NOTIFY_INFORMATION info = new FILE_NOTIFY_INFORMATION(BUFFER_SIZE);
38.282 - public final IntByReference infoLength = new IntByReference();
38.283 - public final OVERLAPPED overlapped = new OVERLAPPED();
38.284 - public FileInfo(String path, HANDLE h) {
38.285 - this.path = path;
38.286 - this.handle = h;
38.287 - }
38.288 - }
38.289 -
38.290 - private static int watcherThreadID;
38.291 - private Thread watcher;
38.292 - private HANDLE port;
38.293 - private final Map<String, FileInfo> rootMap = new HashMap<String, FileInfo>();
38.294 - private final Map<HANDLE, FileInfo> handleMap = new HashMap<HANDLE, FileInfo>();
38.295 - private final BlockingQueue<String> events = new LinkedBlockingQueue<String>();
38.296 -
38.297 - public @Override Void addWatch(String path) throws IOException {
38.298 -
38.299 - if (path.length() < 3 ) throw new IOException("wrong path: " + path);
38.300 -
38.301 - String root = path.substring(0, 3).replace('/', '\\');
38.302 - if (root.charAt(1) != ':' || root.charAt(2) != '\\') {
38.303 - throw new IOException("wrong path: " + path);
38.304 - }
38.305 -
38.306 - if (rootMap.containsKey(root)) return null; // already listening
38.307 - path = root; // listen once on the rootpath instead
38.308 -
38.309 - int mask = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
38.310 - int flags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
38.311 - HANDLE handle = KERNEL32.CreateFile(path,
38.312 - FILE_LIST_DIRECTORY,
38.313 - mask, null, OPEN_EXISTING,
38.314 - flags, null);
38.315 - if (INVALID_HANDLE_VALUE.equals(handle)) {
38.316 - throw new IOException("Unable to open " + path + ": "
38.317 - + KERNEL32.GetLastError());
38.318 - }
38.319 - FileInfo finfo = new FileInfo(path, handle);
38.320 - rootMap.put(path, finfo);
38.321 - handleMap.put(handle, finfo);
38.322 -
38.323 - // Existing port is returned
38.324 - port = KERNEL32.CreateIoCompletionPort(handle, port, handle.getPointer(), 0);
38.325 - if (INVALID_HANDLE_VALUE.equals(port)) {
38.326 - throw new IOException("Unable to create/use I/O Completion port "
38.327 - + "for " + path + ": " + KERNEL32.GetLastError());
38.328 - }
38.329 -
38.330 - if (!KERNEL32.ReadDirectoryChangesW(handle, finfo.info, finfo.info.size(),
38.331 - true, NOTIFY_MASK, finfo.infoLength,
38.332 - finfo.overlapped, null)) {
38.333 - int err = KERNEL32.GetLastError();
38.334 - throw new IOException("ReadDirectoryChangesW failed on "
38.335 - + finfo.path + ", handle " + handle
38.336 - + ": " + err);
38.337 - }
38.338 - if (watcher == null) {
38.339 - Thread t = new Thread("W32 File Monitor") {
38.340 - @Override
38.341 - public void run() {
38.342 - FileInfo finfo;
38.343 - while (watcher != null) {
38.344 - finfo = waitForChange();
38.345 - if (finfo == null) continue;
38.346 -
38.347 - try {
38.348 - handleChanges(finfo);
38.349 - } catch(IOException e) {
38.350 - Watcher.LOG.log(Level.INFO, "handleChanges", e);
38.351 - }
38.352 - }
38.353 - }
38.354 - };
38.355 - t.setDaemon(true);
38.356 - t.start();
38.357 - watcher = t;
38.358 - }
38.359 -
38.360 - return null;
38.361 - }
38.362 -
38.363 - @Override
38.364 - public void stop() throws IOException {
38.365 - try {
38.366 - Thread w = watcher;
38.367 - if (w == null) {
38.368 - return;
38.369 - }
38.370 - watcher = null;
38.371 - w.interrupt();
38.372 - w.join(2000);
38.373 - } catch (InterruptedException ex) {
38.374 - throw (IOException)new InterruptedIOException().initCause(ex);
38.375 - }
38.376 - }
38.377 -
38.378 - private void notify(File file) {
38.379 - events.add(file.getPath());
38.380 - }
38.381 -
38.382 -
38.383 - private static final int BUFFER_SIZE = 4096;
38.384 -
38.385 - private void handleChanges(FileInfo finfo) throws IOException {
38.386 - FILE_NOTIFY_INFORMATION fni = finfo.info;
38.387 - // Lazily fetch the data from native to java - asynchronous update
38.388 - fni.read();
38.389 - do {
38.390 - File file = new File(finfo.path, fni.getFilename());
38.391 - notify(file);
38.392 -
38.393 - fni = fni.next();
38.394 - } while (fni != null);
38.395 -
38.396 - if (!KERNEL32.ReadDirectoryChangesW(finfo.handle, finfo.info,
38.397 - finfo.info.size(), true, NOTIFY_MASK,
38.398 - finfo.infoLength, finfo.overlapped, null)) {
38.399 - int err = KERNEL32.GetLastError();
38.400 - throw new IOException("ReadDirectoryChangesW failed on "
38.401 - + finfo.path + ": " + err);
38.402 - }
38.403 - }
38.404 -
38.405 - private FileInfo waitForChange() {
38.406 - IntByReference rcount = new IntByReference();
38.407 - HANDLEByReference rkey = new HANDLEByReference();
38.408 - PointerByReference roverlap = new PointerByReference();
38.409 - KERNEL32.GetQueuedCompletionStatus(port, rcount, rkey, roverlap, INFINITE);
38.410 -
38.411 - synchronized (this) {
38.412 - return (FileInfo)handleMap.get(rkey.getValue());
38.413 - }
38.414 - }
38.415 -
38.416 -}
39.1 --- a/masterfs/test/unit/src/org/netbeans/modules/masterfs/watcher/WatcherTest.java Sat Dec 24 00:28:54 2011 +0000
39.2 +++ b/masterfs/test/unit/src/org/netbeans/modules/masterfs/watcher/WatcherTest.java Tue Dec 27 19:31:42 2011 +0100
39.3 @@ -41,11 +41,13 @@
39.4 */
39.5 package org.netbeans.modules.masterfs.watcher;
39.6
39.7 +import org.netbeans.modules.masterfs.providers.Notifier;
39.8 import java.io.IOException;
39.9 import java.lang.ref.Reference;
39.10 import java.lang.ref.WeakReference;
39.11 import java.util.LinkedList;
39.12 import java.util.List;
39.13 +import org.netbeans.junit.MockServices;
39.14 import org.netbeans.junit.NbTestCase;
39.15 import org.openide.filesystems.FileChangeAdapter;
39.16 import org.openide.filesystems.FileObject;
39.17 @@ -68,10 +70,11 @@
39.18 @Override
39.19 protected void setUp() throws Exception {
39.20 clearWorkDir();
39.21 + MockServices.setServices(TestNotifier.class);
39.22 listener = new L();
39.23 watcher = Lookup.getDefault().lookup(Watcher.class);
39.24 - notify = new TestNotifier();
39.25 - watcher.installNotifier(notify);
39.26 + notify = Lookup.getDefault().lookup(TestNotifier.class);
39.27 + notify.start();
39.28 }
39.29
39.30 @Override
39.31 @@ -158,7 +161,7 @@
39.32 private static final class L extends FileChangeAdapter {
39.33 }
39.34
39.35 - private static final class TestNotifier extends Notifier<Integer> {
39.36 + public static final class TestNotifier extends Notifier<Integer> {
39.37 final List<String> registered = new LinkedList<String>();
39.38
39.39 @Override
39.40 @@ -189,6 +192,10 @@
39.41 assertEquals(i + ". " + msg, expected[i], registered.get(i));
39.42 }
39.43 }
39.44 -
39.45 +
39.46 + @Override
39.47 + protected void start() throws IOException {
39.48 + registered.clear();
39.49 + }
39.50 }
39.51 }
40.1 --- a/nbbuild/cluster.properties Sat Dec 24 00:28:54 2011 +0000
40.2 +++ b/nbbuild/cluster.properties Tue Dec 27 19:31:42 2011 +0100
40.3 @@ -176,6 +176,10 @@
40.4 libs.jsr223,\
40.5 libs.osgi,\
40.6 masterfs,\
40.7 + masterfs.linux,\
40.8 + masterfs.macosx,\
40.9 + masterfs.solaris,\
40.10 + masterfs.windows,\
40.11 netbinox,\
40.12 o.jdesktop.layout,\
40.13 o.n.bootstrap,\
41.1 --- a/openide.filesystems/src/org/openide/filesystems/FileUtil.java Sat Dec 24 00:28:54 2011 +0000
41.2 +++ b/openide.filesystems/src/org/openide/filesystems/FileUtil.java Tue Dec 27 19:31:42 2011 +0100
41.3 @@ -1003,7 +1003,13 @@
41.4 * For example, to make this method work in unit tests in an Ant-based module project,
41.5 * right-click Unit Test Libraries, Add Unit Test Dependency, check Show Non-API Modules, select Master Filesystem.
41.6 * (Also right-click the new Master Filesystem node, Edit, uncheck Include in Compile Classpath.)
41.7 + * To ensure masterfs (or some other module that can handle the conversion)
41.8 + * is present put following line into your module manifest:
41.9 * </p>
41.10 + * <pre>
41.11 + * OpenIDE-Module-Needs: org.openide.filesystems.FileUtil.toFileObject
41.12 + * </pre>
41.13 + *
41.14 * @param file a disk file (may or may not exist). This file
41.15 * must be {@linkplain #normalizeFile normalized}.
41.16 * @return a corresponding file object, or null if the file does not exist
42.1 --- a/openide.filesystems/src/org/openide/filesystems/StreamPool.java Sat Dec 24 00:28:54 2011 +0000
42.2 +++ b/openide.filesystems/src/org/openide/filesystems/StreamPool.java Tue Dec 27 19:31:42 2011 +0100
42.3 @@ -453,9 +453,9 @@
42.4 }
42.5 String annotateProp = System.getProperty("org.openide.filesystems.annotateUnclosedStreams"); // NOI18N;
42.6 annotateUnclosedStreams = Boolean.parseBoolean(annotateProp);
42.7 - try {
42.8 - assert false;
42.9 - } catch (AssertionError ex) {
42.10 + boolean assertsOn = false;
42.11 + assert assertsOn = true;
42.12 + if (assertsOn) {
42.13 annotateUnclosedStreams = annotateProp == null ? true : ! Boolean.FALSE.toString().equals(annotateProp);
42.14 }
42.15 return annotateUnclosedStreams;