|
Java example source code file (SolarisWatchService.java)
The SolarisWatchService.java Java example source code/* * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.nio.fs; import java.nio.file.*; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.*; import java.io.IOException; import sun.misc.Unsafe; import static sun.nio.fs.UnixConstants.*; /** * Solaris implementation of WatchService based on file events notification * facility. */ class SolarisWatchService extends AbstractWatchService { private static final Unsafe unsafe = Unsafe.getUnsafe(); private static int addressSize = unsafe.addressSize(); private static int dependsArch(int value32, int value64) { return (addressSize == 4) ? value32 : value64; } /* * typedef struct port_event { * int portev_events; * ushort_t portev_source; * ushort_t portev_pad; * uintptr_t portev_object; * void *portev_user; * } port_event_t; */ private static final int SIZEOF_PORT_EVENT = dependsArch(16, 24); private static final int OFFSETOF_EVENTS = 0; private static final int OFFSETOF_SOURCE = 4; private static final int OFFSETOF_OBJECT = 8; /* * typedef struct file_obj { * timestruc_t fo_atime; * timestruc_t fo_mtime; * timestruc_t fo_ctime; * uintptr_t fo_pad[3]; * char *fo_name; * } file_obj_t; */ private static final int SIZEOF_FILEOBJ = dependsArch(40, 80); private static final int OFFSET_FO_NAME = dependsArch(36, 72); // port sources private static final short PORT_SOURCE_USER = 3; private static final short PORT_SOURCE_FILE = 7; // user-watchable events private static final int FILE_MODIFIED = 0x00000002; private static final int FILE_ATTRIB = 0x00000004; private static final int FILE_NOFOLLOW = 0x10000000; // exception events private static final int FILE_DELETE = 0x00000010; private static final int FILE_RENAME_TO = 0x00000020; private static final int FILE_RENAME_FROM = 0x00000040; private static final int UNMOUNTED = 0x20000000; private static final int MOUNTEDOVER = 0x40000000; // background thread to read change events private final Poller poller; SolarisWatchService(UnixFileSystem fs) throws IOException { int port = -1; try { port = portCreate(); } catch (UnixException x) { throw new IOException(x.errorString()); } this.poller = new Poller(fs, this, port); this.poller.start(); } @Override WatchKey register(Path dir, WatchEvent.Kind<?>[] events, WatchEvent.Modifier... modifiers) throws IOException { // delegate to poller return poller.register(dir, events, modifiers); } @Override void implClose() throws IOException { // delegate to poller poller.close(); } /** * WatchKey implementation */ private class SolarisWatchKey extends AbstractWatchKey implements DirectoryNode { private final UnixFileKey fileKey; // pointer to native file_obj object private final long object; // events (may be changed). set to null when watch key is invalid private volatile Set<? extends WatchEvent.Kind>> events; // map of entries in directory; created lazily; accessed only by // poller thread. private Map<Path,EntryNode> children = new HashMap<>(); SolarisWatchKey(SolarisWatchService watcher, UnixPath dir, UnixFileKey fileKey, long object, Set<? extends WatchEvent.Kind>> events) { super(dir, watcher); this.fileKey = fileKey; this.object = object; this.events = events; } UnixPath getDirectory() { return (UnixPath)watchable(); } UnixFileKey getFileKey() { return fileKey; } @Override public long object() { return object; } void invalidate() { events = null; } Set<? extends WatchEvent.Kind>> events() { return events; } void setEvents(Set<? extends WatchEvent.Kind>> events) { this.events = events; } Map<Path,EntryNode> children() { return children; } @Override public boolean isValid() { return events != null; } @Override public void cancel() { if (isValid()) { // delegate to poller poller.cancel(this); } } @Override public void addChild(Path name, EntryNode node) { children.put(name, node); } @Override public void removeChild(Path name) { children.remove(name); } @Override public EntryNode getChild(Path name) { return children.get(name); } } /** * Background thread to read from port */ private class Poller extends AbstractPoller { // maximum number of events to read per call to port_getn private static final int MAX_EVENT_COUNT = 128; // events that map to ENTRY_DELETE private static final int FILE_REMOVED = (FILE_DELETE|FILE_RENAME_TO|FILE_RENAME_FROM); // events that tell us not to re-associate the object private static final int FILE_EXCEPTION = (FILE_REMOVED|UNMOUNTED|MOUNTEDOVER); // address of event buffers (used to receive events with port_getn) private final long bufferAddress; private final SolarisWatchService watcher; // the I/O port private final int port; // maps file key (dev/inode) to WatchKey private final Map<UnixFileKey,SolarisWatchKey> fileKey2WatchKey; // maps file_obj object to Node private final Map<Long,Node> object2Node; /** * Create a new instance */ Poller(UnixFileSystem fs, SolarisWatchService watcher, int port) { this.watcher = watcher; this.port = port; this.bufferAddress = unsafe.allocateMemory(SIZEOF_PORT_EVENT * MAX_EVENT_COUNT); this.fileKey2WatchKey = new HashMap<UnixFileKey,SolarisWatchKey>(); this.object2Node = new HashMap<Long,Node>(); } @Override void wakeup() throws IOException { // write to port to wakeup polling thread try { portSend(port, 0); } catch (UnixException x) { throw new IOException(x.errorString()); } } @Override Object implRegister(Path obj, Set<? extends WatchEvent.Kind>> events, WatchEvent.Modifier... modifiers) { // no modifiers supported at this time if (modifiers.length > 0) { for (WatchEvent.Modifier modifier: modifiers) { if (modifier == null) return new NullPointerException(); if (modifier instanceof com.sun.nio.file.SensitivityWatchEventModifier) continue; // ignore return new UnsupportedOperationException("Modifier not supported"); } } UnixPath dir = (UnixPath)obj; // check file is directory UnixFileAttributes attrs = null; try { attrs = UnixFileAttributes.get(dir, true); } catch (UnixException x) { return x.asIOException(dir); } if (!attrs.isDirectory()) { return new NotDirectoryException(dir.getPathForExceptionMessage()); } // if already registered then update the events and return existing key UnixFileKey fileKey = attrs.fileKey(); SolarisWatchKey watchKey = fileKey2WatchKey.get(fileKey); if (watchKey != null) { try { updateEvents(watchKey, events); } catch (UnixException x) { return x.asIOException(dir); } return watchKey; } // register directory long object = 0L; try { object = registerImpl(dir, (FILE_MODIFIED | FILE_ATTRIB)); } catch (UnixException x) { return x.asIOException(dir); } // create watch key and insert it into maps watchKey = new SolarisWatchKey(watcher, dir, fileKey, object, events); object2Node.put(object, watchKey); fileKey2WatchKey.put(fileKey, watchKey); // register all entries in directory registerChildren(dir, watchKey, false, false); return watchKey; } // release resources for single entry void releaseChild(EntryNode node) { long object = node.object(); if (object != 0L) { object2Node.remove(object); releaseObject(object, true); node.setObject(0L); } } // release resources for entries in directory void releaseChildren(SolarisWatchKey key) { for (EntryNode node: key.children().values()) { releaseChild(node); } } // cancel single key @Override void implCancelKey(WatchKey obj) { SolarisWatchKey key = (SolarisWatchKey)obj; if (key.isValid()) { fileKey2WatchKey.remove(key.getFileKey()); // release resources for entries releaseChildren(key); // release resources for directory long object = key.object(); object2Node.remove(object); releaseObject(object, true); // and finally invalidate the key key.invalidate(); } } // close watch service @Override void implCloseAll() { // release all native resources for (Long object: object2Node.keySet()) { releaseObject(object, true); } // invalidate all keys for (Map.Entry<UnixFileKey,SolarisWatchKey> entry: fileKey2WatchKey.entrySet()) { entry.getValue().invalidate(); } // clean-up object2Node.clear(); fileKey2WatchKey.clear(); // free global resources unsafe.freeMemory(bufferAddress); UnixNativeDispatcher.close(port); } /** * Poller main loop. Blocks on port_getn waiting for events and then * processes them. */ @Override public void run() { try { for (;;) { int n = portGetn(port, bufferAddress, MAX_EVENT_COUNT); assert n > 0; long address = bufferAddress; for (int i=0; i<n; i++) { boolean shutdown = processEvent(address); if (shutdown) return; address += SIZEOF_PORT_EVENT; } } } catch (UnixException x) { x.printStackTrace(); } } /** * Process a single port_event * * Returns true if poller thread is requested to shutdown. */ boolean processEvent(long address) { // pe->portev_source short source = unsafe.getShort(address + OFFSETOF_SOURCE); // pe->portev_object long object = unsafe.getAddress(address + OFFSETOF_OBJECT); // pe->portev_events int events = unsafe.getInt(address + OFFSETOF_EVENTS); // user event is trigger to process pending requests if (source != PORT_SOURCE_FILE) { if (source == PORT_SOURCE_USER) { // process any pending requests boolean shutdown = processRequests(); if (shutdown) return true; } return false; } // lookup object to get Node Node node = object2Node.get(object); if (node == null) { // should not happen return false; } // As a workaround for 6642290 and 6636438/6636412 we don't use // FILE_EXCEPTION events to tell use not to register the file. // boolean reregister = (events & FILE_EXCEPTION) == 0; boolean reregister = true; // If node is EntryNode then event relates to entry in directory // If node is a SolarisWatchKey (DirectoryNode) then event relates // to a watched directory. boolean isDirectory = (node instanceof SolarisWatchKey); if (isDirectory) { processDirectoryEvents((SolarisWatchKey)node, events); } else { boolean ignore = processEntryEvents((EntryNode)node, events); if (ignore) reregister = false; } // need to re-associate to get further events if (reregister) { try { events = FILE_MODIFIED | FILE_ATTRIB; if (!isDirectory) events |= FILE_NOFOLLOW; portAssociate(port, PORT_SOURCE_FILE, object, events); } catch (UnixException x) { // unable to re-register reregister = false; } } // object is not re-registered so release resources. If // object is a watched directory then signal key if (!reregister) { // release resources object2Node.remove(object); releaseObject(object, false); // if watch key then signal it if (isDirectory) { SolarisWatchKey key = (SolarisWatchKey)node; fileKey2WatchKey.remove( key.getFileKey() ); key.invalidate(); key.signal(); } else { // if entry then remove it from parent EntryNode entry = (EntryNode)node; SolarisWatchKey key = (SolarisWatchKey)entry.parent(); key.removeChild(entry.name()); } } return false; } /** * Process directory events. If directory is modified then re-scan * directory to register any new entries */ void processDirectoryEvents(SolarisWatchKey key, int mask) { if ((mask & (FILE_MODIFIED | FILE_ATTRIB)) != 0) { registerChildren(key.getDirectory(), key, key.events().contains(StandardWatchEventKinds.ENTRY_CREATE), key.events().contains(StandardWatchEventKinds.ENTRY_DELETE)); } } /** * Process events for entries in registered directories. Returns {@code * true} if events are ignored because the watch key has been cancelled. */ boolean processEntryEvents(EntryNode node, int mask) { SolarisWatchKey key = (SolarisWatchKey)node.parent(); Set<? extends WatchEvent.Kind>> events = key.events(); if (events == null) { // key has been cancelled so ignore event return true; } // entry modified if (((mask & (FILE_MODIFIED | FILE_ATTRIB)) != 0) && events.contains(StandardWatchEventKinds.ENTRY_MODIFY)) { key.signalEvent(StandardWatchEventKinds.ENTRY_MODIFY, node.name()); } return false; } /** * Registers all entries in the given directory * * The {@code sendCreateEvents} and {@code sendDeleteEvents} parameters * indicates if ENTRY_CREATE and ENTRY_DELETE events should be queued * when new entries are found. When initially registering a directory * they will always be false. When re-scanning a directory then it * depends on if the events are enabled or not. */ void registerChildren(UnixPath dir, SolarisWatchKey parent, boolean sendCreateEvents, boolean sendDeleteEvents) { boolean isModifyEnabled = parent.events().contains(StandardWatchEventKinds.ENTRY_MODIFY) ; // reset visited flag on entries so that we can detect file deletes for (EntryNode node: parent.children().values()) { node.setVisited(false); } try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) { for (Path entry: stream) { Path name = entry.getFileName(); // skip entry if already registered EntryNode node = parent.getChild(name); if (node != null) { node.setVisited(true); continue; } // new entry found long object = 0L; int errno = 0; boolean addNode = false; // if ENTRY_MODIFY enabled then we register the entry for events if (isModifyEnabled) { try { UnixPath path = (UnixPath)entry; int events = (FILE_NOFOLLOW | FILE_MODIFIED | FILE_ATTRIB); object = registerImpl(path, events); addNode = true; } catch (UnixException x) { errno = x.errno(); } } else { addNode = true; } if (addNode) { // create node node = new EntryNode(object, (UnixPath)entry.getFileName(), parent); node.setVisited(true); // tell the parent about it parent.addChild(entry.getFileName(), node); if (object != 0L) object2Node.put(object, node); } // send ENTRY_CREATE event for the new file // send ENTRY_DELETE event for files that were deleted immediately boolean deleted = (errno == ENOENT); if (sendCreateEvents && (addNode || deleted)) parent.signalEvent(StandardWatchEventKinds.ENTRY_CREATE, name); if (sendDeleteEvents && deleted) parent.signalEvent(StandardWatchEventKinds.ENTRY_DELETE, name); } } catch (DirectoryIteratorException | IOException x) { // queue OVERFLOW event so that user knows to re-scan directory parent.signalEvent(StandardWatchEventKinds.OVERFLOW, null); return; } // clean-up and send ENTRY_DELETE events for any entries that were // not found Iterator<Map.Entry Other Java examples (source code examples)Here is a short list of links related to this Java SolarisWatchService.java source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.