You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@river.apache.org by pe...@apache.org on 2020/07/05 11:41:42 UTC

svn commit: r1879521 [26/37] - in /river/jtsk/modules/modularize/apache-river: ./ browser/ browser/src/main/java/org/apache/river/example/browser/ extra/ groovy-config/ river-activation/ river-collections/ river-collections/src/main/java/org/apache/riv...

Modified: river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/AvailabilityRegistrationWatcher.java
URL: http://svn.apache.org/viewvc/river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/AvailabilityRegistrationWatcher.java?rev=1879521&r1=1879520&r2=1879521&view=diff
==============================================================================
--- river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/AvailabilityRegistrationWatcher.java (original)
+++ river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/AvailabilityRegistrationWatcher.java Sun Jul  5 11:41:39 2020
@@ -1,382 +1,385 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- * 
- *      http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.river.outrigger;
-
-import java.io.IOException;
-import java.rmi.RemoteException;
-import java.rmi.MarshalledObject;
-import java.util.Set;
-import java.util.Iterator;
-import net.jini.core.event.RemoteEventListener;
-import net.jini.core.event.RemoteEvent;
-import net.jini.core.event.UnknownEventException;
-import net.jini.id.Uuid;
-import net.jini.security.ProxyPreparer;
-import net.jini.space.JavaSpace;
-import org.apache.river.landlord.LeasedResource;
-
-/**
- * Subclass of <code>TransitionWatcher</code> for availability event
- * registrations. Also represents the event registration itself.
- */
-abstract class AvailabilityRegistrationWatcher extends TransitionWatcher 
-    implements EventRegistrationRecord
-{
-    /** 
-     * The current expiration time of the registration
-     * Protected, but only for use by subclasses.
-     */
-    long expiration;
-
-    /** 
-     * The UUID that identifies this registration 
-     * Only for use by subclasses.
-     * Should not be changed.
-     */
-    volatile Uuid cookie;
-
-    /**
-     * The handback associated with this registration.
-     * Only for use by subclasses.
-     * Should not be changed.
-     */
-    MarshalledObject handback;
-
-    /**
-     * <code>true</code> if client is interested
-     * in only visibility events, <code>false</code>
-     * otherwise.
-     * Only for use by subclasses.
-     * Should not be changed.
-     */
-    volatile boolean visibilityOnly;
-
-    /** 
-     * The event ID associated with this registration
-     * Protected, but only for use by subclasses.
-     * Should not be changed.
-     */
-    long eventID;
-
-    /** 
-     * The current sequence number. 
-     */
-    private long currentSeqNum = 0;
-
-    /**
-     * The <code>TemplateHandle</code>s associated with this
-     * watcher.
-     */
-    private final Set<TemplateHandle> owners = new java.util.HashSet<TemplateHandle>();
-    private boolean removed = false;
-
-    /**
-     * The OutriggerServerImpl we are part of.
-     */
-    private OutriggerServerImpl server;
-
-    /**
-     * Used during log recovery to create a mostly empty
-     * <code>AvailabilityRegistrationWatcher</code>.  
-     * <p> 
-     * Note, we set the time stamp and tie-breaker here instead of
-     * getting them from the log. This means they will be inconstant
-     * with their value from the last VM we were in, but since they
-     * never leak out and events are read-only anyway this should not
-     * be a problem (this also allows us to keep the tie-breaker and
-     * time stamp in final fields).
-     *
-     * @param timestamp the value that is used
-     *        to sort <code>TransitionWatcher</code>s.
-     * @param startOrdinal the highest ordinal associated
-     *        with operations that are considered to have occurred 
-     *        before the operation associated with this watcher.
-     * @param currentSeqNum Sequence number to start with.
-     */
-    AvailabilityRegistrationWatcher(long timestamp, long startOrdinal, 
-				    long currentSeqNum) 
-    {
-	super(timestamp, startOrdinal);
-	this.currentSeqNum = currentSeqNum;
-    }
-
-    /**
-     * Create a new <code>AvailabilityRegistrationWatcher</code>.
-     * @param timestamp the value that is used
-     *        to sort <code>TransitionWatcher</code>s.
-     * @param startOrdinal the highest ordinal associated
-     *        with operations that are considered to have occurred 
-     *        before the operation associated with this watcher.
-     * @param cookie The unique identifier associated
-     *        with this watcher. Must not be <code>null</code>.
-     * @param visibilityOnly pass <code>true</code> if client
-     *        only wants visibility events
-     * @param handback The handback object that
-     *        should be sent along with event
-     *        notifications to the the listener.
-     * @param eventID The event ID for event type
-     *        represented by this object.
-     * @throws NullPointerException if the <code>cookie</code>
-     *        argument is <code>null</code>.
-     */
-    AvailabilityRegistrationWatcher(long timestamp, long startOrdinal, 
-	Uuid cookie, boolean visibilityOnly, MarshalledObject handback, 
-        long eventID)
-    {
-	super(timestamp, startOrdinal);
-
-	if (cookie == null)
-	    throw new NullPointerException("cookie must be non-null");
-
-	this.cookie = cookie;
-	this.handback = handback;
-	this.eventID = eventID;
-	this.visibilityOnly = visibilityOnly;
-    }
-
-    /**
-     * Process the given transition by queuing up a task with the
-     * notifier for event delivery. Assumes the passed entry matches
-     * the template in the <code>TemplateHandle</code> associated with
-     * this watcher and that <code>isInterested</code> returned
-     * <code>true</code>. If <code>remove</code> has been called or
-     * the expiration time of this watcher has passed, this call
-     * should have no effect. This call may cause the watcher to be
-     * removed.
-     * @param transition A <code>EntryTransition</code> that
-     *              describes the transition and what
-     *              entry is transitioning. This method
-     *              will assume that <code>transition.getHandle</code>
-     *              returns a non-null value.
-     * @param now   An estimate of the current time (not the time
-     *              when the event occurred).
-     * @throws NullPointerException if <code>transition</code> is 
-     *         <code>null</code>.  
-     */
-    void process(EntryTransition transition, long now) {
-	boolean doneFor = false;
-
-	// lock before checking the time and so we can update currentSeqNum
-	synchronized (this) {
-	    if (removed) return; // Must have been removed
-
-	    if (now > expiration) {
-		doneFor = true;
-	    } else {
-		server.enqueueDelivery(
-		    new VisibilityEventSender(
-                        transition.getHandle().rep(),
-			transition.isVisible(), 
-			currentSeqNum++));
-	    }
-	}
-
-	if (doneFor) cancel();
-    }
-
-    /**
-     * Return the remote listener associated with this 
-     * <code>EventRegistrationWatcher</code>. Optionally
-     * prepare the listener if it has been recovered from
-     * the store and not yet re-prepared.
-     * @return the remote listener associated with this 
-     * <code>EventRegistrationWatcher</code>
-     * @throws IOException if the listener can not
-     *         be unmarshalled. May throw {@link RemoteException}
-     *         if the call to the preparer does
-     * @throws ClassNotFoundException if the listener
-     *         needs to be unmarshalled and a necessary
-     *         class can not be found
-     * @throws SecurityException if the <code>prepareProxy</code>
-     *         call does.
-     */
-    abstract RemoteEventListener getListener(ProxyPreparer preparer)
-	throws ClassNotFoundException, IOException;
-
-    /**
-     * Associate a <code>TemplateHandle</code> with this object.  May
-     * call more than once.
-     *
-     * @param h The <code>TemplateHandle</code> associate
-     *          with this watcher
-     * @return <code>true</code> if the handle was successfully added,
-     *         and <code>false</code> if the watcher has already
-     *         been removed
-     * @throws NullPointerException if <code>h</code> is 
-     *        <code>null</code> 
-     */
-    synchronized boolean addTemplateHandle(TemplateHandle h) {
-	if (removed)
-	    return false; // Already removed!
-
-	owners.add(h);
-
-	if (server == null)
-	    server = h.getServer();
-
-	return true;
-    }
-
-    /**
-     * Set the expiration time of this object.  This method may be
-     * called before <code>setTemplateHandle</code> is called.
-     * @param newExpiration The expiration time.
-     */
-    public final synchronized void setExpiration(long newExpiration) {
-	expiration = newExpiration;
-    }
-
-    public final synchronized long getExpiration() {
-	return expiration;
-    }
-
-    /**
-     * Get the unique identifier associated with this object.  This
-     * method may be called before <code>setTemplateHandle</code> is
-     * called. 
-     * @return The unique identifier associated with this
-     * watcher.
-     */
-    public Uuid getCookie() {
-	return cookie;
-    }
-
-    /**
-     * Overridden by subclasses if there is any cleanup work they need
-     * to do as part of <code>cancel</code> or
-     * <code>removeIfExpired</code>. Called after releasing the lock
-     * on <code>this</code>.  Will be called at most once.  
-     * @param server A reference to the top level server object.
-     * @param expired <code>true</code> if being called from 
-     *        <code>removeIfExpired</code> and false otherwise. 
-     */
-    void cleanup(OutriggerServerImpl server, boolean expired) 
-    {}
-
-    /**
-     * The heavy lifting of removing ourselves.
-     * @param now The current time (or a bit earlier).
-     * @param doIt If <code>true</code> ignore
-     *             <code>now</code> and just remove <code>this</code>
-     *             object.
-     * @return <code>true</code> if this call removed
-     *         <code>this</code> object, <code>false</code> if
-     *         it had already been done. Should be ignored if 
-     *         <code>doIt</code> is <code>false</code>.
-     */
-    private boolean doRemove(long now, boolean doIt) {
-	final Set<TemplateHandle> owners;
-        OutriggerServerImpl serv;
-	synchronized (this) {
-	    if (removed) return false; // already removed
-
-	    // Is this a force, or past our expiration?
-	    if (!doIt && (now < expiration)) return false; // don't remove, not our time
-
-	    owners = new java.util.HashSet<TemplateHandle>(this.owners); // Don't need to clone
-            this.owners.clear(); 
-            removed = true;
-	    expiration = Long.MIN_VALUE; //Make sure no one tries to renew us
-            serv = server;
-	}
-
-	cleanup(serv, !doIt);
-        for (Iterator<TemplateHandle> i=owners.iterator(); i.hasNext(); ) {
-            final TemplateHandle h = i.next();
-            h.removeTransitionWatcher(this);
-        }
-        
-	serv.removeEventRegistration(this);	    	
-	return true; // we did the deed
-    }
-
-    void removeIfExpired(long now) {
-	doRemove(now, false);
-    }
-
-    public boolean cancel() {
-	return doRemove(0, true);
-    }
-
-    /**
-     * Common implementation of <code>EventSender</code> for visibility events
-     */
-    private class VisibilityEventSender implements EventSender {
-	/** the <code>EntryRep</code> for the entry that became visible */
-	final private EntryRep rep;
-	/** the sequence number this event should have */
-	final private long     ourSeqNumber;
-	/** <code>true</code> if this is a visibility event */
-	final private boolean isVisible;
-
-	/**
-	 * Create a new <code>VisibilityEventSender</code> that will send
-	 * a new <code>OutriggerAvailabilityEvent</code>.
-	 * @param rep the <code>EntryRep</code> for the entry
-	 *            that became visible/available
-	 * @param isVisible <code>true</code> if this is a visibility event
-	 * @param ourSeqNumber the sequence number this event should have
-	 */
-	private VisibilityEventSender(EntryRep rep, boolean isVisible,
-				      long ourSeqNumber) 
-	{
-	    this.rep = rep;
-	    this.ourSeqNumber = ourSeqNumber;
-	    this.isVisible = isVisible;
-	}
-
-	public void sendEvent(JavaSpace source, long now, ProxyPreparer preparer)
-	    throws UnknownEventException, IOException, ClassNotFoundException
-	{
-	    boolean doneFor = false;
-            RemoteEvent event;
-	    synchronized (AvailabilityRegistrationWatcher.this) {
-		if (removed)
-		    return; // Our registration must been 
-		            // canceled/expired, don't send event
-
-		if (getExpiration() < now) {
-		    doneFor = true; // Our registration has expired, remove it
-		}
-	    
-
-                // Now that we are outside the lock kill our watcher if doneFor.
-                if (doneFor) {
-                    cancel();
-                    return;
-                }
-                event = new OutriggerAvailabilityEvent(source, eventID, ourSeqNumber, 
-					       handback, isVisible, rep);
-            }// end sync(AvailabilityRegistrationWatcher.this)
-            // Overridden keep out of sync block.
-	    getListener(preparer).notify( event );
-	}
-	
-	public void cancelRegistration() {
-	    cancel();
-	}
-
-//	/**
-//	 * Since we try to send every event that occurs, don't
-//	 * care which order they run.
-//	 */
-//	public boolean runAfter(EventSender other) {
-//	    return false;
-//	}
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.river.outrigger;
+
+import java.io.IOException;
+import java.rmi.RemoteException;
+import java.rmi.MarshalledObject;
+import java.util.Set;
+import java.util.Iterator;
+import net.jini.core.event.RemoteEventListener;
+import net.jini.core.event.RemoteEvent;
+import net.jini.core.event.UnknownEventException;
+import net.jini.id.Uuid;
+import net.jini.security.ProxyPreparer;
+import net.jini.space.JavaSpace;
+import org.apache.river.outrigger.proxy.EntryRep;
+import org.apache.river.landlord.LeasedResource;
+import  org.apache.river.outrigger.proxy.OutriggerAvailabilityEvent;
+
+
+/**
+ * Subclass of <code>TransitionWatcher</code> for availability event
+ * registrations. Also represents the event registration itself.
+ */
+abstract class AvailabilityRegistrationWatcher extends TransitionWatcher 
+    implements EventRegistrationRecord
+{
+    /** 
+     * The current expiration time of the registration
+     * Protected, but only for use by subclasses.
+     */
+    long expiration;
+
+    /** 
+     * The UUID that identifies this registration 
+     * Only for use by subclasses.
+     * Should not be changed.
+     */
+    volatile Uuid cookie;
+
+    /**
+     * The handback associated with this registration.
+     * Only for use by subclasses.
+     * Should not be changed.
+     */
+    MarshalledObject handback;
+
+    /**
+     * <code>true</code> if client is interested
+     * in only visibility events, <code>false</code>
+     * otherwise.
+     * Only for use by subclasses.
+     * Should not be changed.
+     */
+    volatile boolean visibilityOnly;
+
+    /** 
+     * The event ID associated with this registration
+     * Protected, but only for use by subclasses.
+     * Should not be changed.
+     */
+    long eventID;
+
+    /** 
+     * The current sequence number. 
+     */
+    private long currentSeqNum = 0;
+
+    /**
+     * The <code>TemplateHandle</code>s associated with this
+     * watcher.
+     */
+    private final Set<TemplateHandle> owners = new java.util.HashSet<TemplateHandle>();
+    private boolean removed = false;
+
+    /**
+     * The OutriggerServerImpl we are part of.
+     */
+    private OutriggerServerImpl server;
+
+    /**
+     * Used during log recovery to create a mostly empty
+     * <code>AvailabilityRegistrationWatcher</code>.  
+     * <p> 
+     * Note, we set the time stamp and tie-breaker here instead of
+     * getting them from the log. This means they will be inconstant
+     * with their value from the last VM we were in, but since they
+     * never leak out and events are read-only anyway this should not
+     * be a problem (this also allows us to keep the tie-breaker and
+     * time stamp in final fields).
+     *
+     * @param timestamp the value that is used
+     *        to sort <code>TransitionWatcher</code>s.
+     * @param startOrdinal the highest ordinal associated
+     *        with operations that are considered to have occurred 
+     *        before the operation associated with this watcher.
+     * @param currentSeqNum Sequence number to start with.
+     */
+    AvailabilityRegistrationWatcher(long timestamp, long startOrdinal, 
+				    long currentSeqNum) 
+    {
+	super(timestamp, startOrdinal);
+	this.currentSeqNum = currentSeqNum;
+    }
+
+    /**
+     * Create a new <code>AvailabilityRegistrationWatcher</code>.
+     * @param timestamp the value that is used
+     *        to sort <code>TransitionWatcher</code>s.
+     * @param startOrdinal the highest ordinal associated
+     *        with operations that are considered to have occurred 
+     *        before the operation associated with this watcher.
+     * @param cookie The unique identifier associated
+     *        with this watcher. Must not be <code>null</code>.
+     * @param visibilityOnly pass <code>true</code> if client
+     *        only wants visibility events
+     * @param handback The handback object that
+     *        should be sent along with event
+     *        notifications to the the listener.
+     * @param eventID The event ID for event type
+     *        represented by this object.
+     * @throws NullPointerException if the <code>cookie</code>
+     *        argument is <code>null</code>.
+     */
+    AvailabilityRegistrationWatcher(long timestamp, long startOrdinal, 
+	Uuid cookie, boolean visibilityOnly, MarshalledObject handback, 
+        long eventID)
+    {
+	super(timestamp, startOrdinal);
+
+	if (cookie == null)
+	    throw new NullPointerException("cookie must be non-null");
+
+	this.cookie = cookie;
+	this.handback = handback;
+	this.eventID = eventID;
+	this.visibilityOnly = visibilityOnly;
+    }
+
+    /**
+     * Process the given transition by queuing up a task with the
+     * notifier for event delivery. Assumes the passed entry matches
+     * the template in the <code>TemplateHandle</code> associated with
+     * this watcher and that <code>isInterested</code> returned
+     * <code>true</code>. If <code>remove</code> has been called or
+     * the expiration time of this watcher has passed, this call
+     * should have no effect. This call may cause the watcher to be
+     * removed.
+     * @param transition A <code>EntryTransition</code> that
+     *              describes the transition and what
+     *              entry is transitioning. This method
+     *              will assume that <code>transition.getHandle</code>
+     *              returns a non-null value.
+     * @param now   An estimate of the current time (not the time
+     *              when the event occurred).
+     * @throws NullPointerException if <code>transition</code> is 
+     *         <code>null</code>.  
+     */
+    void process(EntryTransition transition, long now) {
+	boolean doneFor = false;
+
+	// lock before checking the time and so we can update currentSeqNum
+	synchronized (this) {
+	    if (removed) return; // Must have been removed
+
+	    if (now > expiration) {
+		doneFor = true;
+	    } else {
+		server.enqueueDelivery(
+		    new VisibilityEventSender(
+                        transition.getHandle().rep(),
+			transition.isVisible(), 
+			currentSeqNum++));
+	    }
+	}
+
+	if (doneFor) cancel();
+    }
+
+    /**
+     * Return the remote listener associated with this 
+     * <code>EventRegistrationWatcher</code>. Optionally
+     * prepare the listener if it has been recovered from
+     * the store and not yet re-prepared.
+     * @return the remote listener associated with this 
+     * <code>EventRegistrationWatcher</code>
+     * @throws IOException if the listener can not
+     *         be unmarshalled. May throw {@link RemoteException}
+     *         if the call to the preparer does
+     * @throws ClassNotFoundException if the listener
+     *         needs to be unmarshalled and a necessary
+     *         class can not be found
+     * @throws SecurityException if the <code>prepareProxy</code>
+     *         call does.
+     */
+    abstract RemoteEventListener getListener(ProxyPreparer preparer)
+	throws ClassNotFoundException, IOException;
+
+    /**
+     * Associate a <code>TemplateHandle</code> with this object.  May
+     * call more than once.
+     *
+     * @param h The <code>TemplateHandle</code> associate
+     *          with this watcher
+     * @return <code>true</code> if the handle was successfully added,
+     *         and <code>false</code> if the watcher has already
+     *         been removed
+     * @throws NullPointerException if <code>h</code> is 
+     *        <code>null</code> 
+     */
+    synchronized boolean addTemplateHandle(TemplateHandle h) {
+	if (removed)
+	    return false; // Already removed!
+
+	owners.add(h);
+
+	if (server == null)
+	    server = h.getServer();
+
+	return true;
+    }
+
+    /**
+     * Set the expiration time of this object.  This method may be
+     * called before <code>setTemplateHandle</code> is called.
+     * @param newExpiration The expiration time.
+     */
+    public final synchronized void setExpiration(long newExpiration) {
+	expiration = newExpiration;
+    }
+
+    public final synchronized long getExpiration() {
+	return expiration;
+    }
+
+    /**
+     * Get the unique identifier associated with this object.  This
+     * method may be called before <code>setTemplateHandle</code> is
+     * called. 
+     * @return The unique identifier associated with this
+     * watcher.
+     */
+    public Uuid getCookie() {
+	return cookie;
+    }
+
+    /**
+     * Overridden by subclasses if there is any cleanup work they need
+     * to do as part of <code>cancel</code> or
+     * <code>removeIfExpired</code>. Called after releasing the lock
+     * on <code>this</code>.  Will be called at most once.  
+     * @param server A reference to the top level server object.
+     * @param expired <code>true</code> if being called from 
+     *        <code>removeIfExpired</code> and false otherwise. 
+     */
+    void cleanup(OutriggerServerImpl server, boolean expired) 
+    {}
+
+    /**
+     * The heavy lifting of removing ourselves.
+     * @param now The current time (or a bit earlier).
+     * @param doIt If <code>true</code> ignore
+     *             <code>now</code> and just remove <code>this</code>
+     *             object.
+     * @return <code>true</code> if this call removed
+     *         <code>this</code> object, <code>false</code> if
+     *         it had already been done. Should be ignored if 
+     *         <code>doIt</code> is <code>false</code>.
+     */
+    private boolean doRemove(long now, boolean doIt) {
+	final Set<TemplateHandle> owners;
+        OutriggerServerImpl serv;
+	synchronized (this) {
+	    if (removed) return false; // already removed
+
+	    // Is this a force, or past our expiration?
+	    if (!doIt && (now < expiration)) return false; // don't remove, not our time
+
+	    owners = new java.util.HashSet<TemplateHandle>(this.owners); // Don't need to clone
+            this.owners.clear(); 
+            removed = true;
+	    expiration = Long.MIN_VALUE; //Make sure no one tries to renew us
+            serv = server;
+	}
+
+	cleanup(serv, !doIt);
+        for (Iterator<TemplateHandle> i=owners.iterator(); i.hasNext(); ) {
+            final TemplateHandle h = i.next();
+            h.removeTransitionWatcher(this);
+        }
+        
+	serv.removeEventRegistration(this);	    	
+	return true; // we did the deed
+    }
+
+    void removeIfExpired(long now) {
+	doRemove(now, false);
+    }
+
+    public boolean cancel() {
+	return doRemove(0, true);
+    }
+
+    /**
+     * Common implementation of <code>EventSender</code> for visibility events
+     */
+    private class VisibilityEventSender implements EventSender {
+	/** the <code>EntryRep</code> for the entry that became visible */
+	final private EntryRep rep;
+	/** the sequence number this event should have */
+	final private long     ourSeqNumber;
+	/** <code>true</code> if this is a visibility event */
+	final private boolean isVisible;
+
+	/**
+	 * Create a new <code>VisibilityEventSender</code> that will send
+	 * a new <code>OutriggerAvailabilityEvent</code>.
+	 * @param rep the <code>EntryRep</code> for the entry
+	 *            that became visible/available
+	 * @param isVisible <code>true</code> if this is a visibility event
+	 * @param ourSeqNumber the sequence number this event should have
+	 */
+	private VisibilityEventSender(EntryRep rep, boolean isVisible,
+				      long ourSeqNumber) 
+	{
+	    this.rep = rep;
+	    this.ourSeqNumber = ourSeqNumber;
+	    this.isVisible = isVisible;
+	}
+
+	public void sendEvent(JavaSpace source, long now, ProxyPreparer preparer)
+	    throws UnknownEventException, IOException, ClassNotFoundException
+	{
+	    boolean doneFor = false;
+            RemoteEvent event;
+	    synchronized (AvailabilityRegistrationWatcher.this) {
+		if (removed)
+		    return; // Our registration must been 
+		            // canceled/expired, don't send event
+
+		if (getExpiration() < now) {
+		    doneFor = true; // Our registration has expired, remove it
+		}
+	    
+
+                // Now that we are outside the lock kill our watcher if doneFor.
+                if (doneFor) {
+                    cancel();
+                    return;
+                }
+                event = new OutriggerAvailabilityEvent(source, eventID, ourSeqNumber, 
+					       handback, isVisible, rep);
+            }// end sync(AvailabilityRegistrationWatcher.this)
+            // Overridden keep out of sync block.
+	    getListener(preparer).notify( event );
+	}
+	
+	public void cancelRegistration() {
+	    cancel();
+	}
+
+//	/**
+//	 * Since we try to send every event that occurs, don't
+//	 * care which order they run.
+//	 */
+//	public boolean runAfter(EventSender other) {
+//	    return false;
+//	}
+    }
+}

Modified: river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/BaseHandle.java
URL: http://svn.apache.org/viewvc/river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/BaseHandle.java?rev=1879521&r1=1879520&r2=1879521&view=diff
==============================================================================
--- river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/BaseHandle.java (original)
+++ river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/BaseHandle.java Sun Jul  5 11:41:39 2020
@@ -1,70 +1,71 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- * 
- *      http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.river.outrigger;
-
-import java.util.Queue;
-
-/**
- * Base class for handles to Entries and templates.
- *
- * @author Sun Microsystems, Inc.
- *
- */
-abstract class BaseHandle  {
-    private final EntryRep rep;		// the rep this handle manages
-    private final Queue<? extends BaseHandle> content;
-
-    /**
-     * Create a new handle
-     * 
-     * @param content thread safe Queue from which this BaseHandle will be removed
-     * atomically, BaseHandle is not added to content during construction,
-     * as it would allow this to escape.
-     * @param rep EntryRep managed by this BaseHandle.
-     */
-    protected BaseHandle(EntryRep rep, Queue<? extends BaseHandle> content) {
-	this.rep = rep;
-        this.content = content;
-    }
-
-    /**
-     * Return the handle's <code>EntryRep</code> object.
-     */
-    EntryRep rep() {
-	return rep;
-    }
-
-    // inherit doc comment
-    public String classFor() {
-	return rep.classFor();
-    }
-    
-    public abstract boolean removed();
-    
-    /**
-     * Overridden and called from subclass.
-     * 
-     * @return true if removed.
-     */
-    public boolean remove(){
-        return content.remove(this);
-    }
-}
-
-
-
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.river.outrigger;
+
+import java.util.Queue;
+import org.apache.river.outrigger.proxy.EntryRep;
+
+/**
+ * Base class for handles to Entries and templates.
+ *
+ * @author Sun Microsystems, Inc.
+ *
+ */
+abstract class BaseHandle  {
+    private final EntryRep rep;		// the rep this handle manages
+    private final Queue<? extends BaseHandle> content;
+
+    /**
+     * Create a new handle
+     * 
+     * @param content thread safe Queue from which this BaseHandle will be removed
+     * atomically, BaseHandle is not added to content during construction,
+     * as it would allow this to escape.
+     * @param rep EntryRep managed by this BaseHandle.
+     */
+    protected BaseHandle(EntryRep rep, Queue<? extends BaseHandle> content) {
+	this.rep = rep;
+        this.content = content;
+    }
+
+    /**
+     * Return the handle's <code>EntryRep</code> object.
+     */
+    EntryRep rep() {
+	return rep;
+    }
+
+    // inherit doc comment
+    public String classFor() {
+	return rep.classFor();
+    }
+    
+    public abstract boolean removed();
+    
+    /**
+     * Overridden and called from subclass.
+     * 
+     * @return true if removed.
+     */
+    public boolean remove(){
+        return content.remove(this);
+    }
+}
+
+
+

Modified: river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/EntryHandle.java
URL: http://svn.apache.org/viewvc/river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/EntryHandle.java?rev=1879521&r1=1879520&r2=1879521&view=diff
==============================================================================
--- river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/EntryHandle.java (original)
+++ river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/EntryHandle.java Sun Jul  5 11:41:39 2020
@@ -1,456 +1,458 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- * 
- *      http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.river.outrigger;
-
-import net.jini.io.MarshalledInstance;
-import org.apache.river.landlord.LeasedResource;
-import java.util.Queue;
-
-/**
- * This object holds an annotated reference to an
- * <code>EntryRep</code> object.  Currently there is one annotation,
- * which is a hash code for the object that can be used as a
- * quick-reject comparison when scanning through the list.  The handle
- * holds a hash code that is based on the bytes that encode the first
- * <i>N</i> fields, where <i>N</i> is the number of fields in the
- * entry up to a maximum (currently this maximum is 16 (64 bits
- * divided by 4 bits/field), so that 4 is the minimum number of bits
- * per field in the hash).
- * <p>
- * When comparing, the template's own hash is calculated, and also a
- * mask that masks out the hash codes of wildcard fields.  A template
- * will match an entry only if the entry's EntryHandle hash masked with
- * the template's wildcard mask is the same as the template's hash.
- * <p>
- * Care must be taken since the template may be a supertype of the type
- * being searched.  This is why the number of fields in the static
- * methods is passed as an argument, not simply taken from the entry in
- * question.  When a template's hash is being created, its hash value
- * is calculated as if it were of the class being searched, with the
- * subclass's field count.  Any extra fields are assumed to be
- * wildcards.  This means that the template's hash must be recalculated
- * for each subclass it is compared against, but this only happens once
- * per known subclass, and so is probably not onerous.  <p>
- *
- * There is a particular risk with the removal of entries outside of
- * transactions. Ideally marking an entry as removed and making the
- * removal durable would be atomic with respect to other
- * operations. But this would require holding a lock across disk I/O
- * which we try to avoid. In particular it would hold up the progress
- * of searches that match the entry in question, even though the very
- * next entry might be a suitable match. One alternative would be to
- * make the removal durable, then while holding the entry's lock mark
- * the entry as removed, but this would allow competing takes to both
- * get the same entry (this could be corrected by making the 2nd take
- * lose when it goes back to try and complete the removal, and then
- * continues its query, but since logging happens up in
- * OutriggerServerImpl restarting the query would be inconvenient, it
- * would probably also result in a number of unnecessary log
- * records). We could mark the entry as removed, release the entry's
- * lock, and then make the removal durable. However, this allows for
- * the possibility of a 2nd query that matches the entry coming in
- * after the entry has been removed, but before the removal has been
- * made durable, finding no matches and returning null, and then the
- * server crashing before the removal is made durable. When the server
- * came back up the entry would be available again, and if the 2nd
- * query was repeated it could then return the entry that had been
- * marked as removed. Effectively an entry would have disappeared and
- * then reappeared. <p>
- * 
- * Our solution is to introduce the <i>removePending</i> flag. When an
- * entry is to be removed outside of a transaction the removePending
- * flag is set by calling <code>provisionallyRemove</code>, the
- * removal is made durable, the entry is removed internally and the
- * removePending flag cleared (generally by calling
- * <code>remove</code> on the appropriate <code>EntryHolder</code> or
- * on the <code>EntryHolderSet</code> - either will remove the entry
- * from all the internal tables and clear the removePending flag). <p>
- *
- * Any operation that will definitively indicate that a given entry
- * has been removed must not only check to see if the entry has been
- * removed but also that removePending is not set (the
- * <code>isProvisionallyRemoved</code> method returns the state of the
- * removePending flag). If removePending is set the operation must
- * either block until removePending is cleared (this can be
- * accomplished using the <code>waitOnCompleteRemoval</code> method),
- * indicating that the removal has been made durable, or return in
- * such a way that the entry's state is left ambiguous. Note, because
- * any I/O failure while logging will result in the space crashing a
- * set removePending flag will only transition to cleared after a
- * removal has been made durable, thus an operation blocked on the
- * removePending flag should never need to go back and see if the
- * entry has become available. <p>
- *
- * Note some of the method of this class are synchronized internally,
- * while other are synchronized externally.  Methods which need to be
- * synchronized externally are called out in their comments.
- *
- * @author Sun Microsystems, Inc.  
- */
-// We do not store this data on the EntryRep object itself because it
-// is not really part of the client<->JavaSpaces service protocol -- 
-// some implementations of EntryHolder may not choose to use this
-// mechanism.  It does add an extra object per EntryRep object in
-// those that *do* use it, and so we may want to re-examine this in the
-// future.
-
-class EntryHandle extends BaseHandle implements LeaseDesc, Transactable {
-    /** the content hash for the rep */
-    private final long     hash; // Made final for toString() and hash().
-
-    /** 
-     * If this entry is locked by one or more transaction the info
-     * on those transactions, otherwise <code>null</code>.
-     */
-    private TxnState txnState;
-
-    /**
-     * <code>true</code> if this entry has to been seen as removed,
-     * but the removal has not yet been committed to disk
-     */
-    private boolean removePending = false;
-    private boolean removed = false;
-
-    /**
-     * Create a new handle, calculating the hash for the object.
-     * If <code>mgr</code> is non-<code>null</code> start the entry
-     * as write locked under the given transaction.
-     * @param rep The rep of the entry this is a handle for
-     * @param mgr If this entry is being written under a transaction the
-     *            manager for that transaction, otherwise <code>null</code>
-     * @param holder If mgr is non-<code>null</code> this must be
-     *            the holder holding this handle.  Otherwise it may be
-     *            <code>null</code> 
-     * @param content Queue this EntryHandle will be removed from.
-     */
-    EntryHandle(EntryRep rep, TransactableMgr mgr, EntryHolder holder, Queue<EntryHandle> content) {
-	super(rep, content);
-	hash = (rep != null ? hashFor(rep, rep.numFields())[0] : -1);
-	if (mgr == null) {
-	    txnState = null;
-	} else {
-	    if (holder == null) 
-		throw new NullPointerException("EntryHandle:If mgr is " +
- 	            "non-null holder must be non-null");
-	    txnState = new TxnState(mgr, TransactableMgr.WRITE, holder);
-	}
-    }
-
-    // inherit doc comment
-    public LeasedResource getLeasedResource() {
-	return rep();
-    }
-
-    /**
-     * Return this handle's content hash.
-     */
-    long hash() {
-	return hash;
-    }
-
-    /**
-     * Calculate the hash for a particular entry, assuming the given
-     * number of fields, filling in the fields of <code>desc</code>
-     * with the relevant values.  <code>desc</code> may be
-     * <code>null</code>.  <code>numFields</code> must be >= the number
-     * of fields in the <code>rep</code> object (this is not
-     * checked).
-     *
-     * @see #hashFor(EntryRep,int)
-     * @see #descFor(EntryRep,int)
-     * @return long[4] containing the hash, bitsPerField, fieldsInHash and mask
-     * in that order.
-     */
-    private static long []
-	hashFor(EntryRep rep, int numFields)
-    {
-        long [] result = new long [4];
-	if (rep == null || numFields == 0) return result;
-        
-        /** Number of bits allocated in the hash for each field */
-	int bitsPerField = Math.max(64 / numFields, 4);	// at least 4 bits
-        /** How many fields are used in the hash? */
-	int fieldsInHash = 64 / bitsPerField;		// max fields used
-        /** A mask with the lower <code>bitsPerField</code> bits set */
-	long mask =					// per-field bit mask
-		    0xffffffffffffffffL >>> (64 - bitsPerField);
-	long hash = 0;					// current hash value
-
-	// field counts will be different if rep is a template of a superclass
-	long endField = Math.min(fieldsInHash, rep.numFields());
-
-	// set the appropriate rep of the overall hash for the field's hash
-	for (int i = 0; i < endField; i++)
-	    hash |= (hashForField(rep, i) & mask) << (i * bitsPerField);
-        
-        result [0] = hash;
-        result [1] = bitsPerField;
-        result [2] = fieldsInHash;
-        result [3] = mask;
-	return result;
-    }
-
-    /**
-     * Return the template description -- mask and hash.
-     *
-     * @see EntryHandleTmplDesc
-     */
-    static EntryHandleTmplDesc descFor(EntryRep tmpl, int numFields) {
-	EntryHandleTmplDesc tmplDesc;
-        
-	// Get the hash and the related useful information
-        long [] hashDesc = hashFor(tmpl, numFields);
-	long hash = hashDesc[0];
-        long bitsPerField = hashDesc [1];
-        long fieldsInHash = hashDesc [2];
-        long mask = hashDesc [3];
-        
-        long tmplMask = 0;
-
-	// Create the mask to mask away wildcard fields
-	for (int i = 0; i < fieldsInHash; i++) {
-	    // If this field is one we have a value for, set bits in the mask
-	    if (i < tmpl.numFields() && tmpl.value(i) != null)
-		tmplMask |= (mask << (i * bitsPerField));
-	}
-        
-	// Ensure that the non-value fields are masked out
-	hash &= tmplMask;
-        
-        tmplDesc = new EntryHandleTmplDesc(hash, tmplMask);
-
-	return tmplDesc;
-    }
-
-    /**
-     * Return the hash value for a given field, which is then merged in
-     * as part of the overall hash for the entry.  The last 32 bytes of
-     * the field value are used (or fewer if there are fewer).
-     *
-     * @see #hashFor(EntryRep,int)
-     */
-    static long hashForField(EntryRep rep, int field) {
-	MarshalledInstance v = rep.value(field);
-	if (v == null)	  // for templates, it's just zero
-	    return 0;
-	else
-	    return v.hashCode();
-    }
-
-    public String toString() {
-	return "0x" + Long.toHexString(hash) + " [" + rep() + "]";
-    }
-
-    /**
-     * Return <code>true</code> if the operation <code>op</code> under
-     * the given transaction (represented by the transaction's manager)
-     * can be performed on the object represented by this handle.  The 
-     * thread calling this method should own this object's lock.
-     */
-    // $$$ Calling this method when we don't own the lock on this
-    // object seems a bit dicey, but that is exactly what we do in 
-    // EntryHolder.SimpleRepEnum.nextRep().  Working it through
-    // it seems to work in that particular case, but it seems fragile.
-    boolean canPerform(TransactableMgr mgr, int op) {
-        synchronized (this){ // Audit revealed calling thread didn't always own lock see comment above.
-            if (txnState == null) 
-                return true; // all operations are legal on a non-transacted entry
-
-            return txnState.canPerform(mgr, op);
-        }
-    }
-
-    /**
-     * Return <code>true</code> if the given transaction is already
-     * known to the entry this handle represents.  The 
-     * thread calling this method should own this object's lock.
-     */
-    boolean knownMgr(TransactableMgr mgr) {
-        synchronized (this){  // Audit revealed caller methods didn't always hold lock.
-            if (txnState == null) 
-                return (mgr == null); // The only mgr we know about is the null mgr
-
-            return txnState.knownMgr(mgr);
-        }
-    }
-
-    /**
-     * Return <code>true</code> if we are being managed the given
-     * manager is the only one we know about.  The thread calling this
-     * method should own this object's lock.
-     */
-    boolean onlyMgr(TransactableMgr mgr) {
-	if (txnState == null) // Audit revealed caller always held lock.
-	    return false;
-
-	return txnState.onlyMgr(mgr);
-    }
-
-    /**
-     * Return <code>true</code> if the entry this handle represents is
-     * being managed within any transaction.  The thread calling this
-     * method should own this object's lock.
-     */
-    boolean managed() {
-        synchronized (this){  // Audit revealed caller didn't always have lock.
-            return txnState != null;
-        }
-    }
-
-    /**
-     * Add into the collection any transactions that are known to this
-     * handle. The thread calling this method should own this object's
-     * lock.
-     */
-    void addTxns(java.util.Collection collection) {
-	if (txnState == null) // Confirmed that calling method owns lock.
-	    return; // nothing to add
-
-	txnState.addTxns(collection);
-    }
-
-    /**
-     * Add <code>mgr</code> to the list of known managers, setting the
-     * the type of lock on this entry to <code>op</code>. The thread
-     * calling this method should own this object's lock.  Assumes
-     * that <code>op</code> is compatible with any lock currently
-     * associated with this entry.  <code>holder</code> is the the
-     * <code>EntryHolder</code> holding this handle.
-     */
-    void add(TransactableMgr mgr, int op, EntryHolder holder) {
-        synchronized (this){
-            if (txnState == null) {
-                txnState = new TxnState(mgr, op, holder);
-            } else {
-                txnState.add(mgr, op);
-            }
-        }
-    }
-
-    /**
-     * It this entry is read locked promote to take locked and return
-     * true, otherwise return false.  Assumes that the object is
-     * locked and the take is being performed under the one
-     * transaction that owns a lock on the entry.
-     */
-    boolean promoteToTakeIfNeeded() {
-        synchronized (this){
-            return txnState.promoteToTakeIfNeeded();
-        }
-    }
-
-    /**
-     * Returns <code>true</code> it this entry has been removed
-     * outside of a transaction, but that removal has not yet been
-     * committed to disk.The thread calling this method should own this
-     * object's lock.
-     */
-    boolean isProvisionallyRemoved() {
-	assert Thread.holdsLock(this);
-	return removePending;
-    }
-
-    /**
-     * Marks this entry as being removed outside of a transaction but
-     * not yet committed to disk. The thread calling this method should
-     * own this object's lock.
-     */
-    void provisionallyRemove() {
-	assert Thread.holdsLock(this);
-	assert !removePending;
-	removePending = true;
-    }
-
-    /**
-     * Called after the removal of a provisionally removed entry has
-     * been committed to disk and the handle has been removed from its
-     * holder. The thread calling this method should own this object's
-     * lock.
-     */
-    void removalComplete() {
-	assert Thread.holdsLock(this);
-
-	if (removePending) {
-	    removePending = false;
-	    notifyAll();
-	}
-    }
-
-    /**
-     * If this entry has been marked for removal by a
-     * non-transactional operation, but that operation has not be
-     * yet been committed to disk, block until the operation has been
-     * committed to disk, otherwise return immediately. The
-     * thread calling this method should own this object's lock.
-     */
-    void waitOnCompleteRemoval() throws InterruptedException {
-	assert Thread.holdsLock(this);
-	while (removePending) {
-	    wait();
-	}
-    }
-
-    /**************************************************
-     * Methods required by the Transactable interface 
-     **************************************************/
-    public synchronized int prepare(TransactableMgr mgr, 
-				    OutriggerServerImpl space) 
-    {
-	if (txnState == null)
-	    throw new IllegalStateException("Can't prepare an entry not " +
-					    "involved in a transaction");
-	final int rslt = txnState.prepare(mgr, space, this);
-	if (txnState.empty())
-	    txnState = null;
-
-	return rslt;
-    }
-
-    public synchronized void abort(TransactableMgr mgr, 
-				   OutriggerServerImpl space) 
-    {
-	if (txnState == null)
-	    throw new IllegalStateException("Can't abort an entry not " +
-					    "involved in a transaction");
-	final boolean last = txnState.abort(mgr, space, this);
-	if (last)
-	    txnState = null;
-    }
-
-    public synchronized void commit(TransactableMgr mgr, 
-				    OutriggerServerImpl space) 
-    {
-	if (txnState == null)
-	    throw new IllegalStateException("Can't commit an entry not " +
-					    "involved in a transaction");
-
-	final boolean last = txnState.commit(mgr, space, this);
-	if (last)
-	    txnState = null;
-    }
-
-    @Override
-    public synchronized boolean removed() {
-        return removed;
-    }
-    
-    public synchronized boolean remove() {
-        if (!removed) removed = super.remove();
-        return removed;
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.river.outrigger;
+
+import net.jini.io.MarshalledInstance;
+import org.apache.river.landlord.LeasedResource;
+import java.util.Queue;
+import org.apache.river.outrigger.proxy.EntryRep;
+
+
+/**
+ * This object holds an annotated reference to an
+ * <code>EntryRep</code> object.  Currently there is one annotation,
+ * which is a hash code for the object that can be used as a
+ * quick-reject comparison when scanning through the list.  The handle
+ * holds a hash code that is based on the bytes that encode the first
+ * <i>N</i> fields, where <i>N</i> is the number of fields in the
+ * entry up to a maximum (currently this maximum is 16 (64 bits
+ * divided by 4 bits/field), so that 4 is the minimum number of bits
+ * per field in the hash).
+ * <p>
+ * When comparing, the template's own hash is calculated, and also a
+ * mask that masks out the hash codes of wildcard fields.  A template
+ * will match an entry only if the entry's EntryHandle hash masked with
+ * the template's wildcard mask is the same as the template's hash.
+ * <p>
+ * Care must be taken since the template may be a supertype of the type
+ * being searched.  This is why the number of fields in the static
+ * methods is passed as an argument, not simply taken from the entry in
+ * question.  When a template's hash is being created, its hash value
+ * is calculated as if it were of the class being searched, with the
+ * subclass's field count.  Any extra fields are assumed to be
+ * wildcards.  This means that the template's hash must be recalculated
+ * for each subclass it is compared against, but this only happens once
+ * per known subclass, and so is probably not onerous.  <p>
+ *
+ * There is a particular risk with the removal of entries outside of
+ * transactions. Ideally marking an entry as removed and making the
+ * removal durable would be atomic with respect to other
+ * operations. But this would require holding a lock across disk I/O
+ * which we try to avoid. In particular it would hold up the progress
+ * of searches that match the entry in question, even though the very
+ * next entry might be a suitable match. One alternative would be to
+ * make the removal durable, then while holding the entry's lock mark
+ * the entry as removed, but this would allow competing takes to both
+ * get the same entry (this could be corrected by making the 2nd take
+ * lose when it goes back to try and complete the removal, and then
+ * continues its query, but since logging happens up in
+ * OutriggerServerImpl restarting the query would be inconvenient, it
+ * would probably also result in a number of unnecessary log
+ * records). We could mark the entry as removed, release the entry's
+ * lock, and then make the removal durable. However, this allows for
+ * the possibility of a 2nd query that matches the entry coming in
+ * after the entry has been removed, but before the removal has been
+ * made durable, finding no matches and returning null, and then the
+ * server crashing before the removal is made durable. When the server
+ * came back up the entry would be available again, and if the 2nd
+ * query was repeated it could then return the entry that had been
+ * marked as removed. Effectively an entry would have disappeared and
+ * then reappeared. <p>
+ * 
+ * Our solution is to introduce the <i>removePending</i> flag. When an
+ * entry is to be removed outside of a transaction the removePending
+ * flag is set by calling <code>provisionallyRemove</code>, the
+ * removal is made durable, the entry is removed internally and the
+ * removePending flag cleared (generally by calling
+ * <code>remove</code> on the appropriate <code>EntryHolder</code> or
+ * on the <code>EntryHolderSet</code> - either will remove the entry
+ * from all the internal tables and clear the removePending flag). <p>
+ *
+ * Any operation that will definitively indicate that a given entry
+ * has been removed must not only check to see if the entry has been
+ * removed but also that removePending is not set (the
+ * <code>isProvisionallyRemoved</code> method returns the state of the
+ * removePending flag). If removePending is set the operation must
+ * either block until removePending is cleared (this can be
+ * accomplished using the <code>waitOnCompleteRemoval</code> method),
+ * indicating that the removal has been made durable, or return in
+ * such a way that the entry's state is left ambiguous. Note, because
+ * any I/O failure while logging will result in the space crashing a
+ * set removePending flag will only transition to cleared after a
+ * removal has been made durable, thus an operation blocked on the
+ * removePending flag should never need to go back and see if the
+ * entry has become available. <p>
+ *
+ * Note some of the method of this class are synchronized internally,
+ * while other are synchronized externally.  Methods which need to be
+ * synchronized externally are called out in their comments.
+ *
+ * @author Sun Microsystems, Inc.  
+ */
+// We do not store this data on the EntryRep object itself because it
+// is not really part of the client<->JavaSpaces service protocol -- 
+// some implementations of EntryHolder may not choose to use this
+// mechanism.  It does add an extra object per EntryRep object in
+// those that *do* use it, and so we may want to re-examine this in the
+// future.
+
+class EntryHandle extends BaseHandle implements LeaseDesc, Transactable {
+    /** the content hash for the rep */
+    private final long     hash; // Made final for toString() and hash().
+
+    /** 
+     * If this entry is locked by one or more transaction the info
+     * on those transactions, otherwise <code>null</code>.
+     */
+    private TxnState txnState;
+
+    /**
+     * <code>true</code> if this entry has to been seen as removed,
+     * but the removal has not yet been committed to disk
+     */
+    private boolean removePending = false;
+    private boolean removed = false;
+
+    /**
+     * Create a new handle, calculating the hash for the object.
+     * If <code>mgr</code> is non-<code>null</code> start the entry
+     * as write locked under the given transaction.
+     * @param rep The rep of the entry this is a handle for
+     * @param mgr If this entry is being written under a transaction the
+     *            manager for that transaction, otherwise <code>null</code>
+     * @param holder If mgr is non-<code>null</code> this must be
+     *            the holder holding this handle.  Otherwise it may be
+     *            <code>null</code> 
+     * @param content Queue this EntryHandle will be removed from.
+     */
+    EntryHandle(EntryRep rep, TransactableMgr mgr, EntryHolder holder, Queue<EntryHandle> content) {
+	super(rep, content);
+	hash = (rep != null ? hashFor(rep, rep.numFields())[0] : -1);
+	if (mgr == null) {
+	    txnState = null;
+	} else {
+	    if (holder == null) 
+		throw new NullPointerException("EntryHandle:If mgr is " +
+ 	            "non-null holder must be non-null");
+	    txnState = new TxnState(mgr, TransactableMgr.WRITE, holder);
+	}
+    }
+
+    // inherit doc comment
+    public LeasedResource getLeasedResource() {
+	return rep();
+    }
+
+    /**
+     * Return this handle's content hash.
+     */
+    long hash() {
+	return hash;
+    }
+
+    /**
+     * Calculate the hash for a particular entry, assuming the given
+     * number of fields, filling in the fields of <code>desc</code>
+     * with the relevant values.  <code>desc</code> may be
+     * <code>null</code>.  <code>numFields</code> must be >= the number
+     * of fields in the <code>rep</code> object (this is not
+     * checked).
+     *
+     * @see #hashFor(EntryRep,int)
+     * @see #descFor(EntryRep,int)
+     * @return long[4] containing the hash, bitsPerField, fieldsInHash and mask
+     * in that order.
+     */
+    private static long []
+	hashFor(EntryRep rep, int numFields)
+    {
+        long [] result = new long [4];
+	if (rep == null || numFields == 0) return result;
+        
+        /** Number of bits allocated in the hash for each field */
+	int bitsPerField = Math.max(64 / numFields, 4);	// at least 4 bits
+        /** How many fields are used in the hash? */
+	int fieldsInHash = 64 / bitsPerField;		// max fields used
+        /** A mask with the lower <code>bitsPerField</code> bits set */
+	long mask =					// per-field bit mask
+		    0xffffffffffffffffL >>> (64 - bitsPerField);
+	long hash = 0;					// current hash value
+
+	// field counts will be different if rep is a template of a superclass
+	long endField = Math.min(fieldsInHash, rep.numFields());
+
+	// set the appropriate rep of the overall hash for the field's hash
+	for (int i = 0; i < endField; i++)
+	    hash |= (hashForField(rep, i) & mask) << (i * bitsPerField);
+        
+        result [0] = hash;
+        result [1] = bitsPerField;
+        result [2] = fieldsInHash;
+        result [3] = mask;
+	return result;
+    }
+
+    /**
+     * Return the template description -- mask and hash.
+     *
+     * @see EntryHandleTmplDesc
+     */
+    static EntryHandleTmplDesc descFor(EntryRep tmpl, int numFields) {
+	EntryHandleTmplDesc tmplDesc;
+        
+	// Get the hash and the related useful information
+        long [] hashDesc = hashFor(tmpl, numFields);
+	long hash = hashDesc[0];
+        long bitsPerField = hashDesc [1];
+        long fieldsInHash = hashDesc [2];
+        long mask = hashDesc [3];
+        
+        long tmplMask = 0;
+
+	// Create the mask to mask away wildcard fields
+	for (int i = 0; i < fieldsInHash; i++) {
+	    // If this field is one we have a value for, set bits in the mask
+	    if (i < tmpl.numFields() && tmpl.value(i) != null)
+		tmplMask |= (mask << (i * bitsPerField));
+	}
+        
+	// Ensure that the non-value fields are masked out
+	hash &= tmplMask;
+        
+        tmplDesc = new EntryHandleTmplDesc(hash, tmplMask);
+
+	return tmplDesc;
+    }
+
+    /**
+     * Return the hash value for a given field, which is then merged in
+     * as part of the overall hash for the entry.  The last 32 bytes of
+     * the field value are used (or fewer if there are fewer).
+     *
+     * @see #hashFor(EntryRep,int)
+     */
+    static long hashForField(EntryRep rep, int field) {
+	MarshalledInstance v = rep.value(field);
+	if (v == null)	  // for templates, it's just zero
+	    return 0;
+	else
+	    return v.hashCode();
+    }
+
+    public String toString() {
+	return "0x" + Long.toHexString(hash) + " [" + rep() + "]";
+    }
+
+    /**
+     * Return <code>true</code> if the operation <code>op</code> under
+     * the given transaction (represented by the transaction's manager)
+     * can be performed on the object represented by this handle.  The 
+     * thread calling this method should own this object's lock.
+     */
+    // $$$ Calling this method when we don't own the lock on this
+    // object seems a bit dicey, but that is exactly what we do in 
+    // EntryHolder.SimpleRepEnum.nextRep().  Working it through
+    // it seems to work in that particular case, but it seems fragile.
+    boolean canPerform(TransactableMgr mgr, int op) {
+        synchronized (this){ // Audit revealed calling thread didn't always own lock see comment above.
+            if (txnState == null) 
+                return true; // all operations are legal on a non-transacted entry
+
+            return txnState.canPerform(mgr, op);
+        }
+    }
+
+    /**
+     * Return <code>true</code> if the given transaction is already
+     * known to the entry this handle represents.  The 
+     * thread calling this method should own this object's lock.
+     */
+    boolean knownMgr(TransactableMgr mgr) {
+        synchronized (this){  // Audit revealed caller methods didn't always hold lock.
+            if (txnState == null) 
+                return (mgr == null); // The only mgr we know about is the null mgr
+
+            return txnState.knownMgr(mgr);
+        }
+    }
+
+    /**
+     * Return <code>true</code> if we are being managed the given
+     * manager is the only one we know about.  The thread calling this
+     * method should own this object's lock.
+     */
+    boolean onlyMgr(TransactableMgr mgr) {
+	if (txnState == null) // Audit revealed caller always held lock.
+	    return false;
+
+	return txnState.onlyMgr(mgr);
+    }
+
+    /**
+     * Return <code>true</code> if the entry this handle represents is
+     * being managed within any transaction.  The thread calling this
+     * method should own this object's lock.
+     */
+    boolean managed() {
+        synchronized (this){  // Audit revealed caller didn't always have lock.
+            return txnState != null;
+        }
+    }
+
+    /**
+     * Add into the collection any transactions that are known to this
+     * handle. The thread calling this method should own this object's
+     * lock.
+     */
+    void addTxns(java.util.Collection collection) {
+	if (txnState == null) // Confirmed that calling method owns lock.
+	    return; // nothing to add
+
+	txnState.addTxns(collection);
+    }
+
+    /**
+     * Add <code>mgr</code> to the list of known managers, setting the
+     * the type of lock on this entry to <code>op</code>. The thread
+     * calling this method should own this object's lock.  Assumes
+     * that <code>op</code> is compatible with any lock currently
+     * associated with this entry.  <code>holder</code> is the the
+     * <code>EntryHolder</code> holding this handle.
+     */
+    void add(TransactableMgr mgr, int op, EntryHolder holder) {
+        synchronized (this){
+            if (txnState == null) {
+                txnState = new TxnState(mgr, op, holder);
+            } else {
+                txnState.add(mgr, op);
+            }
+        }
+    }
+
+    /**
+     * It this entry is read locked promote to take locked and return
+     * true, otherwise return false.  Assumes that the object is
+     * locked and the take is being performed under the one
+     * transaction that owns a lock on the entry.
+     */
+    boolean promoteToTakeIfNeeded() {
+        synchronized (this){
+            return txnState.promoteToTakeIfNeeded();
+        }
+    }
+
+    /**
+     * Returns <code>true</code> it this entry has been removed
+     * outside of a transaction, but that removal has not yet been
+     * committed to disk.The thread calling this method should own this
+     * object's lock.
+     */
+    boolean isProvisionallyRemoved() {
+	assert Thread.holdsLock(this);
+	return removePending;
+    }
+
+    /**
+     * Marks this entry as being removed outside of a transaction but
+     * not yet committed to disk. The thread calling this method should
+     * own this object's lock.
+     */
+    void provisionallyRemove() {
+	assert Thread.holdsLock(this);
+	assert !removePending;
+	removePending = true;
+    }
+
+    /**
+     * Called after the removal of a provisionally removed entry has
+     * been committed to disk and the handle has been removed from its
+     * holder. The thread calling this method should own this object's
+     * lock.
+     */
+    void removalComplete() {
+	assert Thread.holdsLock(this);
+
+	if (removePending) {
+	    removePending = false;
+	    notifyAll();
+	}
+    }
+
+    /**
+     * If this entry has been marked for removal by a
+     * non-transactional operation, but that operation has not be
+     * yet been committed to disk, block until the operation has been
+     * committed to disk, otherwise return immediately. The
+     * thread calling this method should own this object's lock.
+     */
+    void waitOnCompleteRemoval() throws InterruptedException {
+	assert Thread.holdsLock(this);
+	while (removePending) {
+	    wait();
+	}
+    }
+
+    /**************************************************
+     * Methods required by the Transactable interface 
+     **************************************************/
+    public synchronized int prepare(TransactableMgr mgr, 
+				    OutriggerServerImpl space) 
+    {
+	if (txnState == null)
+	    throw new IllegalStateException("Can't prepare an entry not " +
+					    "involved in a transaction");
+	final int rslt = txnState.prepare(mgr, space, this);
+	if (txnState.empty())
+	    txnState = null;
+
+	return rslt;
+    }
+
+    public synchronized void abort(TransactableMgr mgr, 
+				   OutriggerServerImpl space) 
+    {
+	if (txnState == null)
+	    throw new IllegalStateException("Can't abort an entry not " +
+					    "involved in a transaction");
+	final boolean last = txnState.abort(mgr, space, this);
+	if (last)
+	    txnState = null;
+    }
+
+    public synchronized void commit(TransactableMgr mgr, 
+				    OutriggerServerImpl space) 
+    {
+	if (txnState == null)
+	    throw new IllegalStateException("Can't commit an entry not " +
+					    "involved in a transaction");
+
+	final boolean last = txnState.commit(mgr, space, this);
+	if (last)
+	    txnState = null;
+    }
+
+    @Override
+    public synchronized boolean removed() {
+        return removed;
+    }
+    
+    public synchronized boolean remove() {
+        if (!removed) removed = super.remove();
+        return removed;
+    }
+}