summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAudrius Meskauskas <audriusa@Bioinformatics.org>2006-03-06 23:42:19 +0000
committerAudrius Meskauskas <audriusa@Bioinformatics.org>2006-03-06 23:42:19 +0000
commit487217005ac9f00277c857261a7e15bb36830d13 (patch)
tree3cf01ed01d18ced6e1956416c93fa5db33a271a4
parent752dd5cba10d3c1b010505efc48586a37097a9c9 (diff)
downloadclasspath-487217005ac9f00277c857261a7e15bb36830d13.tar.gz
2006-03-07 Audrius Meskauskas <AudriusA@Bioinformatics.org>
PR 25526 * gnu/java/rmi/dgc/DGCImpl.java (LeaseRecord, leaseCache): Removed. (RefProtector): new inner class. (dirty): Rewritten. * gnu/java/rmi/server/UnicastRef.java (dgcId, dgcInterfaceHash, dgcSequence, DIRTY, this_id): New fields. (equals, hashCode, notifyDGC): new methods. (readExternal): Create LeaseRenewingTask if non local. * gnu/java/rmi/server/UnicastServer.java (objects): Use WeakHashMap. (refcache): Use WeakIdentityHashMap. (getExported): New method. * gnu/java/rmi/dgc/LeaseRenewingTask.java: New file.
-rw-r--r--ChangeLog13
-rw-r--r--gnu/java/rmi/dgc/DGCImpl.java152
-rw-r--r--gnu/java/rmi/dgc/LeaseRenewingTask.java226
-rw-r--r--gnu/java/rmi/server/UnicastRef.java165
-rw-r--r--gnu/java/rmi/server/UnicastServer.java46
5 files changed, 515 insertions, 87 deletions
diff --git a/ChangeLog b/ChangeLog
index b21d08e69..730642b8c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2006-03-07 Audrius Meskauskas <AudriusA@Bioinformatics.org>
+
+ PR 25526
+ * gnu/java/rmi/dgc/DGCImpl.java (LeaseRecord, leaseCache): Removed.
+ (RefProtector): new inner class. (dirty): Rewritten.
+ * gnu/java/rmi/server/UnicastRef.java (dgcId, dgcInterfaceHash,
+ dgcSequence, DIRTY, this_id): New fields. (equals, hashCode, notifyDGC):
+ new methods. (readExternal): Create LeaseRenewingTask if non local.
+ * gnu/java/rmi/server/UnicastServer.java (objects):
+ Use WeakHashMap. (refcache): Use WeakIdentityHashMap.
+ (getExported): New method.
+ * gnu/java/rmi/dgc/LeaseRenewingTask.java: New file.
+
2006-03-06 Mark Wielaard <mark@klomp.org>
* doc/www.gnu.org/announce/20060306.wml: New file.
diff --git a/gnu/java/rmi/dgc/DGCImpl.java b/gnu/java/rmi/dgc/DGCImpl.java
index a7bc0940e..a5c036e7b 100644
--- a/gnu/java/rmi/dgc/DGCImpl.java
+++ b/gnu/java/rmi/dgc/DGCImpl.java
@@ -38,6 +38,7 @@ exception statement from your version. */
package gnu.java.rmi.dgc;
+import gnu.java.rmi.server.UnicastServer;
import gnu.java.rmi.server.UnicastServerRef;
import java.rmi.RemoteException;
@@ -46,7 +47,8 @@ import java.rmi.dgc.Lease;
import java.rmi.dgc.VMID;
import java.rmi.server.ObjID;
import java.rmi.server.RMISocketFactory;
-import java.util.Hashtable;
+import java.util.Collection;
+import java.util.TimerTask;
/**
* The DGC implementation is used for the server side during the distributed
@@ -68,14 +70,67 @@ public class DGCImpl
*/
/**
+ * Use the serial version UID for interoperability.
+ */
+ private static final long serialVersionUID = 1;
+
+ /**
+ * Protects the array of object Id's for the scheduled period of time
+ * (lease). After the time expires, the protector is automatically discarded,
+ * making the references unprotected and hence applicable for the garbage
+ * collection.
+ */
+ class RefProtector extends TimerTask
+ {
+ /**
+ * The corresponding server references to protect. Each Id may contain
+ * multiple references that are stored to collection.
+ */
+ Collection[] references;
+
+ /**
+ * Create the new instance of the reference protector that protects the
+ * given array of ids and exists for the given period of time.
+ *
+ * @param ids the ids to protect.
+ */
+ RefProtector(ObjID[] ids, long timeToLive)
+ {
+ references = new Collection[ids.length];
+ for (int i = 0; i < ids.length; i++)
+ {
+ references[i] = UnicastServer.getExported(ids[i]);
+ }
+
+ // Schedule the existence.
+ LeaseRenewingTask.timer.schedule(this, timeToLive);
+ }
+
+ /**
+ * Break all links, ensuring easy collection of the references by the gc.
+ */
+ public void run()
+ {
+ for (int i = 0; i < references.length; i++)
+ {
+ references[i].clear();
+ references[i] = null;
+ }
+ }
+ }
+
+ /**
* This defauld lease value is used if the lease value, passed to the
* {@link #dirty} is equal to zero.
*/
static final long LEASE_VALUE = 600000L;
-
- // leaseCache caches a LeaseRecord associated with a vmid
- Hashtable leaseCache = new Hashtable();
-
+
+ /**
+ * Create the new DGC implementation.
+ *
+ * @throws RemoteException if the super constructor throws or the
+ * socket factory fails.
+ */
public DGCImpl() throws RemoteException
{
super(new ObjID(ObjID.DGC_ID), 0, RMISocketFactory.getSocketFactory());
@@ -92,26 +147,20 @@ public class DGCImpl
*/
public Lease dirty(ObjID[] ids, long sequenceNum, Lease lease)
throws RemoteException
- {
- VMID vmid = lease.getVMID();
- if (vmid == null)
- vmid = new VMID();
-
+ {
+ // We do not fill in VMID because in this implementation it is not used.
long leaseValue = lease.getValue();
+
+ // Grant the maximal default lease time if the passed value is zero.
if (leaseValue <= 0)
leaseValue = LEASE_VALUE;
-
- lease = new Lease(vmid, leaseValue);
- LeaseRecord lr = (LeaseRecord) leaseCache.get(vmid);
- if (lr != null)
- lr.reset(leaseValue);
- else
- {
- lr = new LeaseRecord(vmid, leaseValue, ids);
- leaseCache.put(vmid, lr);
- }
- return (lease);
+ // Create (and shedule of the given existence) the new reference
+ // protector.
+ new RefProtector(ids, leaseValue);
+
+ lease = new Lease(lease.getVMID(), leaseValue);
+ return lease;
}
/**
@@ -130,65 +179,4 @@ public class DGCImpl
// TODO implement
}
- /**
- * LeaseRecord associates a vmid to expireTime.
- */
- static class LeaseRecord
- {
- /**
- * The lease id.
- */
- final VMID vmid;
-
- /**
- * The lease expiration time.
- */
- long expireTime;
-
- /**
- * The array of ObjeID's that must be protected from being garbage
- * collected.
- */
- final ObjID [] objects;
-
- /**
- * Create the new lease record.
- *
- * @param vmid lease id.
- * @param leaseValue lease value
- */
- LeaseRecord(VMID vmid, long leaseValue, ObjID [] an_objects)
- {
- this.vmid = vmid;
- reset(leaseValue);
- objects = an_objects;
- }
-
- /**
- * Prolong the expiration time till current time + passed value
- *
- * @param leaseValue the value after that (since the current moment)
- * the lease should expire in the future.
- */
- void reset(long leaseValue)
- {
- long l = System.currentTimeMillis();
- expireTime = l + leaseValue;
- }
-
- /**
- * Check if the lease has been expired.
- *
- * @return true if the lease has been expired, false if it is still valid.
- */
- boolean isExpired()
- {
- long l = System.currentTimeMillis();
- if (l > expireTime)
- return true;
- return false;
- }
-
- } // End of LeaseRecord
-
} // End of DGCImpl
diff --git a/gnu/java/rmi/dgc/LeaseRenewingTask.java b/gnu/java/rmi/dgc/LeaseRenewingTask.java
new file mode 100644
index 000000000..7e3bb258b
--- /dev/null
+++ b/gnu/java/rmi/dgc/LeaseRenewingTask.java
@@ -0,0 +1,226 @@
+/* LeaseRenewingTask.java -- The task to renew the lease.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.rmi.dgc;
+
+import gnu.java.rmi.server.UnicastRef;
+
+import java.lang.ref.WeakReference;
+import java.rmi.dgc.Lease;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.WeakHashMap;
+
+/**
+ * The task to renew the lease to some object reference. The UnicastRef
+ * being renewed is stored as a weak reference. So the presence of the
+ * sheduled task does not prevent it from being garbage collected. If the
+ * reference has not been garbage collected, the task is resheduled after
+ * the lease is renewed.
+ *
+ * @author Audrius Meskauskas (Audriusa@Bioinformatics.org)
+ */
+public class LeaseRenewingTask
+{
+ /**
+ * The sheduled timer task to call the renew() method.
+ */
+ class LeaseTimerTask extends TimerTask
+ {
+ public void run()
+ {
+ renew();
+ }
+ }
+
+ /**
+ * The default requested lease duration time (one minute by default).
+ */
+ public static long REQUEST_LEASE_DURATION = 60000;
+
+ /**
+ * The reference to the UnicastRef that must renew its lease until not
+ * garbage collected. The different members of this list may point to the
+ * different instances of the UnicastRef's, but these references are
+ * pointing to the same remote object (they .equals() returns
+ * true when comparing with each other). Using LinkedList to make
+ * frequent deletions from the middle easy.
+ */
+ LinkedList ref = new LinkedList();
+
+ /**
+ * The granted (or supposed) lease.
+ */
+ Lease lease = new Lease(null, REQUEST_LEASE_DURATION);
+
+ /**
+ * The timer, shared by all lease renewing tasks. The same instance is also
+ * used for the reference protector discarding in DGCImpl.
+ */
+ static Timer timer = new Timer(true);
+
+ /**
+ * Maps the UnicastRef to its renewing task.
+ */
+ static WeakHashMap existingTasks = new WeakHashMap();
+
+ /**
+ * Creates the lease renewing task that renews the lease of the given
+ * UnicastRef until it is not collected. This constructor requests the lease
+ * value from the server and schedules the lease renewal action.
+ *
+ * @param renewIt the reference that must be renewed.
+ */
+ public LeaseRenewingTask(UnicastRef renewIt)
+ {
+ ref.add(new WeakReference(renewIt));
+ lease = notifyDGC(renewIt);
+ schedule(lease);
+ }
+
+ /**
+ * Schedule periodic leases for the given UnicastRef reference.
+ *
+ * @param renewIt the reference, for that the leases must be scheduled.
+ */
+ public static void scheduleLeases(UnicastRef renewIt)
+ {
+ try {
+ synchronized (existingTasks)
+ {
+ // Check maybe the task for refreshing this remote object already
+ // exists.
+ LeaseRenewingTask task = (LeaseRenewingTask) existingTasks.get(renewIt);
+
+ if (task != null)
+ {
+ // Extend the reference list only. The scheduling must be
+ // alredy done with the previous lease.
+ synchronized (task.ref)
+ {
+ task.ref.add(new WeakReference(renewIt));
+ }
+ }
+ else
+ existingTasks.put(renewIt, new LeaseRenewingTask(renewIt));
+ }
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * Shedule the renewing call, taking into consideration that the following
+ * lease was granted.
+ *
+ * @param lease the lease that was granted.
+ */
+ public void schedule(Lease lease)
+ {
+ long value = lease.getValue();
+
+ // Shedule a 10 % earlier because some time is needed for the message
+ // to reach the server.
+ long reduced = (value * 90)/100;
+ if (reduced == 0)
+ reduced = value;
+
+ timer.schedule(new LeaseTimerTask(), reduced);
+ }
+
+ /**
+ * Renew the lease.
+ */
+ public void renew()
+ {
+ Object renewIt = null;
+ // Iterate throw the list of associated references. If all are
+ // discarded, there is no need to renew.
+ synchronized (ref)
+ {
+ Iterator iter = ref.iterator();
+ WeakReference w;
+ while (iter.hasNext() && renewIt == null)
+ {
+ w = (WeakReference) iter.next();
+ renewIt = w.get();
+ if (renewIt == null)
+ // Discard the weak reference if its target has been garbage
+ // collected.
+ iter.remove();
+ }
+ }
+
+ if (renewIt!=null)
+ {
+ Lease lease = notifyDGC( (UnicastRef) renewIt);
+
+ // Schedule the next renewing session.
+ if (lease!=null)
+ schedule(lease);
+ }
+ {
+ // All references collected - discard this entry.
+ }
+ }
+
+ /**
+ * Notify DGC that we still hold this reference.
+ *
+ * @param renewIt the reference we still have (must not be null).
+ */
+ public Lease notifyDGC(UnicastRef renewIt)
+ {
+ try
+ {
+ return renewIt.notifyDGC(lease);
+ }
+ catch (Exception e)
+ {
+ // Failed to notify.
+ // TODO Take some relevant action in the case if we failed
+ // to notify the remote object owner.
+ return null;
+ }
+ }
+
+}
diff --git a/gnu/java/rmi/server/UnicastRef.java b/gnu/java/rmi/server/UnicastRef.java
index 8097a0466..115ee171f 100644
--- a/gnu/java/rmi/server/UnicastRef.java
+++ b/gnu/java/rmi/server/UnicastRef.java
@@ -38,6 +38,8 @@ exception statement from your version. */
package gnu.java.rmi.server;
+import gnu.java.rmi.dgc.LeaseRenewingTask;
+
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
@@ -50,6 +52,7 @@ import java.lang.reflect.Method;
import java.rmi.ConnectException;
import java.rmi.Remote;
import java.rmi.RemoteException;
+import java.rmi.dgc.Lease;
import java.rmi.server.ObjID;
import java.rmi.server.Operation;
import java.rmi.server.RMIClientSocketFactory;
@@ -61,6 +64,11 @@ import java.rmi.server.UID;
public class UnicastRef
implements RemoteRef, ProtocolConstants {
+ /**
+ * Use serial version UID for iteroperability
+ */
+private static final long serialVersionUID = 1;
+
public ObjID objid;
UnicastConnectionManager manager;
@@ -73,7 +81,7 @@ public UnicastRef() {
}
public UnicastRef(ObjID objid, String host, int port, RMIClientSocketFactory csf) {
- this(objid);
+ this(objid);
manager = UnicastConnectionManager.getInstance(host, port, csf);
}
@@ -103,6 +111,128 @@ public Object invoke(Remote obj, Method method, Object[] params, long opnum) thr
return (invokeCommon(obj, method, params, -1, opnum));
}
+/**
+ * The ordinary number of the DGC messages.
+ */
+static long dgcSequence;
+
+/**
+ * The DGC object id, also serves as a synchronization target to increment
+ * the dgcSequence safely.
+ */
+static final ObjID dgcId = new ObjID(ObjID.DGC_ID);
+
+ObjID []this_id;
+
+/**
+ * The number of the method "dirty" in the DGC.
+ */
+static int DIRTY = 1;
+
+/**
+ * The DGC interface hash code.
+ */
+static final long dgcInterfaceHash = -669196253586618813L;
+
+ /**
+ * Notify the DGC of the remote side that we still hold this object.
+ */
+ public Lease notifyDGC(Lease lease) throws Exception
+ {
+ long seq;
+ synchronized (dgcId)
+ {
+ seq = dgcSequence++;
+ }
+
+ if (this_id == null)
+ this_id = new ObjID[] { objid };
+
+ UnicastConnection conn;
+ try
+ {
+ conn = manager.getConnection();
+ }
+ catch (IOException e1)
+ {
+ throw new RemoteException("connection failed to host: "
+ + manager.serverName, e1);
+ }
+
+ ObjectOutputStream out;
+ DataOutputStream dout;
+ try
+ {
+ dout = conn.getDataOutputStream();
+ dout.writeByte(MESSAGE_CALL);
+
+ out = conn.startObjectOutputStream(); // (re)start ObjectOutputStream
+
+ dgcId.write(out);
+ // The number of the operation is 1 ("dirty")
+ out.writeInt(DIRTY);
+ out.writeLong(dgcInterfaceHash);
+
+ RMIObjectOutputStream rout = (RMIObjectOutputStream) out;
+
+ rout.writeValue(this_id, this_id.getClass());
+ rout.writeLong(seq);
+ rout.writeValue(lease, lease.getClass());
+
+ out.flush();
+ }
+ catch (IOException e2)
+ {
+ throw new RemoteException("DGC call failed: ", e2);
+ }
+
+ int returncode;
+ Object returnval;
+ DataInputStream din;
+ ObjectInputStream in;
+ UID ack;
+ try
+ {
+ din = conn.getDataInputStream();
+
+ if ((returncode = din.readUnsignedByte()) != MESSAGE_CALL_ACK)
+ {
+ conn.disconnect();
+ throw new RemoteException("DGC Call not acked:" + returncode);
+ }
+
+ in = conn.startObjectInputStream(); // (re)start ObjectInputStream
+ returncode = in.readUnsignedByte();
+ ack = UID.read(in);
+
+ if (returncode == RETURN_NACK)
+ {
+ returnval = in.readObject(); // get Exception
+
+ }
+ else
+ {
+ returnval = ((RMIObjectInputStream) in).readValue(Lease.class);
+ }
+ }
+ catch (IOException e3)
+ {
+ throw new RemoteException("DGC call return failed: ", e3);
+ }
+
+ manager.discardConnection(conn);
+
+ if (returncode != RETURN_ACK && returnval != null)
+ {
+ if (returncode == RETURN_NACK)
+ throw (Exception) returnval;
+ else
+ throw new RemoteException("DGC unexpected returncode: " + returncode);
+ }
+
+ return (Lease) returnval;
+ }
+
private Object invokeCommon(Remote obj, Method method, Object[] params, int opnum, long hash) throws Exception {
UnicastConnection conn;
try {
@@ -242,6 +372,12 @@ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundExcept
if (ack != RETURN_ACK && ack != 0/*jdk ack value*/) {
throw new IOException("no ack found");
}
+
+ // Notify the DGC of the remote side that we hold the reference to the
+ // received object. Do not notify if the client and server are on the
+ // same virtual machine.
+ if (manager.serverobj == null)
+ LeaseRenewingTask.scheduleLeases(this);
}
public boolean remoteEquals(RemoteRef ref) {
@@ -276,4 +412,31 @@ public void dump(UnicastConnection conn) {
}
}
+ /**
+ * Check if this UnicastRef points to the object as the passed UnicastRef.
+ * Both the object Id and manager must be the same.
+ *
+ * @return true if the passed reference points to the same remote object
+ * as this reference, false otherwise.
+ */
+ public boolean equals(Object other)
+ {
+ if (other instanceof UnicastRef)
+ {
+ UnicastRef r = (UnicastRef) other;
+ return r.manager.equals(manager) && r.objid.equals(objid);
+ }
+ else
+ return false;
+ }
+
+ /**
+ * Get the hash code of this UnicastRef, combining hash code of the
+ * manager with hash code of the object id.
+ */
+ public int hashCode()
+ {
+ return manager.hashCode() ^ objid.hashCode();
+ }
+
}
diff --git a/gnu/java/rmi/server/UnicastServer.java b/gnu/java/rmi/server/UnicastServer.java
index a8da72563..4364b3dfd 100644
--- a/gnu/java/rmi/server/UnicastServer.java
+++ b/gnu/java/rmi/server/UnicastServer.java
@@ -40,6 +40,7 @@ exception statement from your version. */
package gnu.java.rmi.server;
import gnu.java.rmi.dgc.DGCImpl;
+import gnu.java.util.WeakIdentityHashMap;
import java.io.DataOutputStream;
import java.io.IOException;
@@ -51,16 +52,25 @@ import java.rmi.RemoteException;
import java.rmi.ServerError;
import java.rmi.server.ObjID;
import java.rmi.server.UID;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
+import java.util.Iterator;
import java.util.Map;
-import java.util.Hashtable;
-import java.util.IdentityHashMap;
+import java.util.WeakHashMap;
public class UnicastServer
implements ProtocolConstants {
-static private Hashtable objects = new Hashtable(); //mapping OBJID to server ref
-static private Map refcache = Collections.synchronizedMap(new IdentityHashMap()); //mapping obj itself to server ref
+ /**
+ * Mapping OBJID to server ref by .equals().
+ */
+static private Map objects = Collections.synchronizedMap(new WeakHashMap());
+
+/**
+ * Mapping obj itself to server ref by identity.
+ */
+static private Map refcache = Collections.synchronizedMap(new WeakIdentityHashMap());
static private DGCImpl dgc;
public static void exportObject(UnicastServerRef obj) {
@@ -82,6 +92,34 @@ public static UnicastServerRef getExportedRef(Remote remote){
return (UnicastServerRef)refcache.get(remote);
}
+ /**
+ * Get the server references to the object, previously exported via this
+ * server. As the identity map is scanned, more than one reference may match
+ * this Id.
+ *
+ * @param id the id of the exported object
+ * @return the server reference to this object, null if none.
+ */
+ public static Collection getExported(Object id)
+ {
+ synchronized (objects)
+ {
+ ArrayList list = new ArrayList();
+ Iterator iter = objects.entrySet().iterator();
+ Map.Entry e;
+ Object key;
+ while (iter.hasNext())
+ {
+ e = (Map.Entry) iter.next();
+ key = e.getKey();
+ if (key!=null && key.equals(id))
+ list.add(e.getValue());
+ }
+ return list;
+ }
+ }
+
+
private static synchronized void startDGC() {
if (dgc == null) {
try {