/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*/
/**
* Solaris implementation of WatchService based on file events notification
* facility.
*/
class SolarisWatchService
extends AbstractWatchService
{
}
/*
* 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;
*/
/*
* 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;
*/
// port sources
// user-watchable events
// exception events
// background thread to read change events
int port = -1;
try {
port = portCreate();
} catch (UnixException x) {
throw new IOException(x.errorString());
}
}
throws IOException
{
// delegate to poller
}
// delegate to poller
}
/**
* WatchKey implementation
*/
implements DirectoryNode
{
// pointer to native file_obj object
private final long object;
// events (may be changed). set to null when watch key is invalid
// map of entries in directory; created lazily; accessed only by
// poller thread.
long object,
{
}
}
return fileKey;
}
public long object() {
return object;
}
void invalidate() {
}
return events;
}
}
public boolean isValid() {
}
public void cancel() {
if (isValid()) {
// delegate to poller
}
}
}
}
return null;
}
}
/**
* Background thread to read from port
*/
// maximum number of events to read per call to port_getn
// events that map to ENTRY_DELETE
private static final int FILE_REMOVED =
// events that tell us not to re-associate the object
private static final int FILE_EXCEPTION =
// address of event buffers (used to receive events with port_getn)
private final long bufferAddress;
// the I/O port
private final int port;
// maps file_obj object to Node
/**
* Create a new instance
*/
this.bufferAddress =
}
// write to port to wakeup polling thread
try {
} catch (UnixException x) {
throw new IOException(x.errorString());
}
}
{
// no modifiers supported at this time
return new NullPointerException();
continue; // ignore
return new UnsupportedOperationException("Modifier not supported");
}
}
// check file is directory
try {
} catch (UnixException x) {
return x.asIOException(dir);
}
if (!attrs.isDirectory()) {
}
// return existing watch key after updating events if already
// registered
return watchKey;
}
// register directory
long object = 0L;
try {
} catch (UnixException x) {
return x.asIOException(dir);
}
// create watch key and insert it into maps
// register all entries in directory
return watchKey;
}
// cancel single key
// release resources for entries in directory
releaseObject(object, true);
}
}
// release resources for directory
releaseObject(object, true);
// and finally invalidate the key
key.invalidate();
}
}
// close watch service
void implCloseAll() {
// release all native resources
releaseObject(object, true);
}
// invalidate all keys
}
// clean-up
object2Node.clear();
// free global resources
}
/**
* Poller main loop. Blocks on port_getn waiting for events and then
* processes them.
*/
public void run() {
try {
for (;;) {
assert n > 0;
long address = bufferAddress;
for (int i=0; i<n; i++) {
if (shutdown)
return;
}
}
} catch (UnixException x) {
x.printStackTrace();
}
}
/**
* Process a single port_event
*
* Returns true if poller thread is requested to shutdown.
*/
// pe->portev_source
// pe->portev_object
// pe->portev_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
// 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.
if (isDirectory) {
} else {
if (ignore)
reregister = false;
}
// need to re-associate to get further events
if (reregister) {
try {
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
releaseObject(object, false);
// if watch key then signal it
if (isDirectory) {
key.invalidate();
} else {
// if entry then remove it from parent
}
}
return false;
}
/**
* Process directory events. If directory is modified then re-scan
* directory to register any new entries
*/
}
}
/**
* Process events for entries in registered directories. Returns {@code
* true} if events are ignored because the watch key has been cancelled.
*/
// key has been cancelled so ignore event
return true;
}
// entry modified
{
}
// entry removed
{
// Due to 6636438/6636412 we may get a remove event for cases
// this issue is resolved we re-lstat the file to check if it
// exists. If it exists then we ignore the event. To keep the
// workaround simple we don't check the st_ino so it isn't
// effective when the file is replaced.
boolean removed = true;
try {
removed = false;
} catch (UnixException x) { }
if (removed)
}
return false;
}
/**
* Registers all entries in the given directory
*
* The {@code sendEvents} parameter indicates if ENTRY_CREATE events
* should be queued when new entries are found. When initially
* registering a directory then will always be false. When re-scanning
* a directory then it depends on if the event is enabled or not.
*/
boolean sendEvents)
{
// if the ENTRY_MODIFY event is not enabled then we don't need
// modification events for entries in the directory
int events = FILE_NOFOLLOW;
try {
} catch (IOException x) {
// nothing we can do
return;
}
try {
// skip entry if already registered
continue;
// send ENTRY_CREATE if enabled
if (sendEvents) {
}
// register it
long object = 0L;
try {
} catch (UnixException x) {
// can't register so ignore for now.
continue;
}
// create node
// tell the parent about it
}
} catch (ConcurrentModificationException x) {
// error during iteration which we ignore for now
} finally {
try {
} catch (IOException x) { }
}
}
/**
* Update watch key's events. Where the ENTRY_MODIFY changes then we
* need to update the events of registered children.
*/
// update events, rembering if ENTRY_MODIFY was previously
// enabled or disabled.
// check if ENTRY_MODIFY has changed
boolean isModifyEnabled = events
if (wasModifyEnabled == isModifyEnabled) {
return;
}
// if changed then update events of children
int ev = FILE_NOFOLLOW;
if (isModifyEnabled)
try {
ev);
} catch (UnixException x) {
// nothing we can do.
}
}
}
}
/**
* Calls port_associate to register the given path.
* Returns pointer to fileobj structure that is allocated for
* the registration.
*/
throws UnixException
{
// allocate memory for the path (file_obj->fo_name field)
// allocate memory for filedatanode structure - this is the object
// to port_associate
// associate the object with the port
try {
events);
} catch (UnixException x) {
// debugging
"with the port has been reached");
}
throw x;
}
return object;
}
/**
* Frees all resources for an file_obj object; optionally remove
* association from port
*/
// remove association
if (dissociate) {
try {
} catch (UnixException x) {
// ignore
}
}
// free native memory
}
}
/**
* A node with native (file_obj) resources
*/
private static interface Node {
long object();
}
/**
* A directory node with a map of the entries in the directory
*/
}
/**
* An implementation of a node that is an entry in a directory.
*/
private final long object;
}
public long object() {
return object;
}
return name;
}
return parent;
}
}
// -- native methods --
private static native void init();
throws UnixException;
throws UnixException;
throws UnixException;
throws UnixException;
static {
return null;
}});
init();
}
}