diff options
author | Audrius Meskauskas <audriusa@Bioinformatics.org> | 2006-03-06 23:42:19 +0000 |
---|---|---|
committer | Audrius Meskauskas <audriusa@Bioinformatics.org> | 2006-03-06 23:42:19 +0000 |
commit | 487217005ac9f00277c857261a7e15bb36830d13 (patch) | |
tree | 3cf01ed01d18ced6e1956416c93fa5db33a271a4 | |
parent | 752dd5cba10d3c1b010505efc48586a37097a9c9 (diff) | |
download | classpath-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-- | ChangeLog | 13 | ||||
-rw-r--r-- | gnu/java/rmi/dgc/DGCImpl.java | 152 | ||||
-rw-r--r-- | gnu/java/rmi/dgc/LeaseRenewingTask.java | 226 | ||||
-rw-r--r-- | gnu/java/rmi/server/UnicastRef.java | 165 | ||||
-rw-r--r-- | gnu/java/rmi/server/UnicastServer.java | 46 |
5 files changed, 515 insertions, 87 deletions
@@ -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 { |