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 2012/01/21 08:28:36 UTC

svn commit: r1234278 [15/29] - in /river/tck: ./ configs/ doc/ doc/api/ doc/api/com/ doc/api/com/sun/ doc/api/com/sun/jini/ doc/api/com/sun/jini/compat/ doc/api/com/sun/jini/compat/admin1/ doc/api/com/sun/jini/compat/admin2/ doc/api/com/sun/jini/compat...

Added: river/tck/src/com/sun/jini/compat/harness/TestUtility.java
URL: http://svn.apache.org/viewvc/river/tck/src/com/sun/jini/compat/harness/TestUtility.java?rev=1234278&view=auto
==============================================================================
--- river/tck/src/com/sun/jini/compat/harness/TestUtility.java (added)
+++ river/tck/src/com/sun/jini/compat/harness/TestUtility.java Sat Jan 21 07:28:27 2012
@@ -0,0 +1,430 @@
+/*
+ * 
+ * Copyright 2005 Sun Microsystems, Inc.
+ * 
+ * Licensed 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 com.sun.jini.compat.harness;
+
+// java.io
+import java.io.IOException;
+import java.io.File;
+import java.io.PrintWriter;
+
+// java.rmi
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+
+// java.util
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Random;
+import java.util.Properties;
+
+// com.sun.jini
+import com.sun.jini.admin.DestroyAdmin;
+import com.sun.jini.compat.start.ServiceStarter;
+import com.sun.jini.compat.reggie.RegistrarAdmin;
+
+// net.jini
+import net.jini.admin.Administrable;
+import net.jini.discovery.DiscoveryGroupManagement;
+import net.jini.lookup.DiscoveryAdmin;
+import net.jini.core.event.EventRegistration;
+import net.jini.core.lease.UnknownLeaseException;
+import net.jini.core.lease.Lease;
+import net.jini.core.lookup.ServiceRegistrar;
+import net.jini.core.lookup.ServiceTemplate;
+import net.jini.core.lookup.ServiceMatches;
+import net.jini.core.lookup.ServiceItem;
+import net.jini.core.lookup.ServiceID;
+
+/**
+ * A utility class that contains many static methods that are useful
+ * to the LDJ Kit.
+ */
+public class TestUtility {
+
+    private static PrintWriter log;
+    private static int debugLevel;
+
+    /**
+     * Extracts the log PrintWriter and log debug level from the 
+     * parameter config object.
+     */
+    public static void setLog(Config config) {
+	log = config.getLog();
+	try {
+	    debugLevel = config.getDebugLevel();
+	} catch (Exception e) {
+	    debugLevel = Config.ALL;
+	}
+    }
+
+    /**
+     * Logs the message to the log PrintWriter if <code>level</code> is
+     * "less than or equal to" the com.sun.jini.compat.debugLevel property.
+     */
+    public static void log(int level, String msg) {
+	log(level,msg,null);
+    }
+
+    /**
+     * Logs the message string and Throwable to the log PrintWriter if 
+     * <code>level</code> is "less than or equal to" the 
+     * com.sun.jini.compat.debugLevel property.
+     */
+    public static void log(int level, String msg, Throwable t) {
+	if (log != null && level <= debugLevel) {
+	    log.println(msg);
+	    if (t != null) {
+		t.printStackTrace(log);
+	    }
+	}
+    }
+
+    /**
+     * This method starts the program being tested (client, service,
+     * or lookup) and collects n multicast request datagram packets from it.
+     * The timeout parameter is the maximum time to wait for each packet.
+     * If no packets are received in time, then an
+     * <code>Exception</code> is thrown.
+     *
+     * @param timeout the number of milliseconds to wait for each packet
+     * @param n the number of packets to wait for
+     * @param admin the admin interface for the program
+     *
+     * @return a List of the <code>DatagramPacket</code>s received
+     */
+    public static List startProductAndGetMulticastRequests(
+	long timeout, int n, BasicAdmin admin) throws Exception
+    { 
+	List keepers = new ArrayList();
+	MulticastPacketGatherer gatherer = new MulticastPacketGatherer(admin);
+	try {
+	    gatherer.setPriority(Thread.MAX_PRIORITY);
+	} catch (Exception ignore) {}
+
+	synchronized(gatherer) {
+	    gatherer.start();
+	    // wait to make sure the gatherer is ready
+	    while (!gatherer.isReady()) {
+		gatherer.wait();
+	    }
+
+	    // start the program
+	    admin.start();
+	    
+	    while (keepers.size() < n) {
+		log(Config.ALL,"TestUtility: waiting up to " + timeout 
+		    + " milliseconds for multicast request packet number " 
+		    + (keepers.size()+1));
+		gatherer.wait(timeout);
+		
+		List pkts = gatherer.getPackets();
+		if (pkts.size() == 0) { // we timed out
+		    gatherer.interrupt();
+		    throw new IOException("Only received " + keepers.size() 
+			+ " of the expected " + n + " packets");
+		}
+		keepers.addAll(pkts);
+	    }
+	}
+	gatherer.interrupt();
+	return keepers;
+    }
+
+    /**
+     * This method registers with a lookup to receive notification when
+     * the service being tested registers with the lookup.
+     * Due to the unreliability of remote events this method also does 
+     * a final check with the lookup if no notification has been received
+     * by <code>timeout</code>.
+     *
+     * @param serv    the ServiceRegistrar for the lookup to register with
+     * @param timeout the length of time (in milliseconds) to wait for the
+     *                service to register
+     * @param conf    the Config object for this test run
+     *
+     * @return if the service was found, return the <code>ServiceItem</code>;
+     *         otherwise return <code>null</code>
+     */
+    public static ServiceItem waitForRegistration(ServiceRegistrar lookup, 
+	long timeout, Config conf) throws RemoteException, InterruptedException
+    {
+	BasicServiceAdmin admin = (BasicServiceAdmin)conf.getAdmin();
+	Object lock = new Object();
+	RegistrationListener rl = new RegistrationListener(lock, admin);
+	EventRegistration er = null;
+	synchronized (lock) {
+	    er = lookup.notify(admin.getTemplate(),
+		ServiceRegistrar.TRANSITION_NOMATCH_MATCH,
+		rl, null, timeout * 2);
+
+	    // check to see if the service was registered already
+	    ServiceItem si = getService(lookup, conf);
+	    if (si != null) {
+		return si;
+	    }
+
+	    lock.wait(timeout);
+	}
+
+	try {
+	    if (er != null) {
+		er.getLease().cancel();
+	    }
+	} catch (UnknownLeaseException ignore) {}
+        try {
+            UnicastRemoteObject.unexportObject(rl,true);
+        } catch (RemoteException ignore) {}
+	
+	if (!rl.getNotified())
+	    return getService(lookup, conf);
+
+	return rl.getServiceItem();
+    }
+
+    /**
+     * Attempts to get the service from the lookup.  Returns
+     * <code>null</code> if the service is not in the lookup.
+     * 
+     * @param lookup the ServiceRegistrar for the lookup service of interest
+     * @param conf the Config object for this test run
+     *
+     * @return the service or <code>null</code> if one could not be found
+     */
+    public static ServiceItem getService(ServiceRegistrar lookup, Config conf)
+	throws RemoteException
+    {
+	BasicServiceAdmin admin = (BasicServiceAdmin)conf.getAdmin();
+	ServiceMatches sm = lookup.lookup(admin.getTemplate(), 20);
+
+	// check for possible null values in sm.items array
+	List servicesList = new ArrayList();
+	for (int i = 0; i < sm.items.length; i++) {
+	    if (sm.items[i].service != null)
+		servicesList.add(sm.items[i]);
+	}
+
+	ServiceItem[] serviceItems = new ServiceItem[servicesList.size()];
+	for (int i = 0; i < servicesList.size(); i++) {
+	    serviceItems[i] = (ServiceItem)servicesList.get(i);
+	}
+
+	return admin.pickService(serviceItems);
+    }
+
+    /**
+     * Starts the lookup server and returns the 
+     * <code>ServiceRegistrar</code>.  It uses the properties:
+     * <p><ul>
+     *   <li> com.sun.jini.compat.installDir
+     *   <li> com.sun.jini.compat.scratchDir
+     * </ul><p>
+     * to generate the classpath, the codebase, and the temp directory for
+     * the lookup server, and to find the policy file.
+     * <p><ul>
+     *   <li> The classpath is set to: installDir/lib/compat-lus.jar
+     *   <li> The codebase is obtained from the class server for this test run
+     *   <li> The temp directory is generated somewhere in scratchDir
+     *   <li> The policy file is found in: installDir/policy/policy.all
+     * </ul><p>
+     * 
+     * @param groups a string array of the groups the lookup must join
+     * @param maxLeaseDuration the maximum lease duration for the lookup
+     * @param lookupDir the prefix of the scratch directory for the 
+     *                  lookup within the LDJ Kit scratch directory;
+     *                  must be at least 3 characters
+     * @param conf the Config object for this test run
+     *
+     * @return the <code>ServiceRegistrar</code> of the lookup that was started
+     */
+    public static ServiceRegistrar startLookup(String[] groups,
+       int maxLeaseDuration, String lookupDir, Config conf) throws Exception
+    {
+	return startLookup(groups, maxLeaseDuration, lookupDir, null, conf);
+    }
+
+    /**
+     * Starts the lookup server and returns the 
+     * <code>ServiceRegistrar</code>.  It uses the properties:
+     * <p><ul>
+     *   <li> com.sun.jini.compat.installDir
+     *   <li> com.sun.jini.compat.scratchDir
+     * </ul><p>
+     * to generate the classpath, the codebase, and the temp directory for
+     * the lookup server, and to find the policy file.
+     * <p><ul>
+     *   <li> The classpath is set to: installDir/lib/compat-lus.jar
+     *   <li> The codebase is obtained from the class server for this test run
+     *   <li> The temp directory is generated somewhere in scratchDir
+     *   <li> The policy file is found in: installDir/policy/policy.all
+     * </ul><p>
+     * 
+     * @param groups a string array of the groups the lookup must join
+     * @param maxLeaseDuration the maximum lease duration for the lookup
+     * @param lookupDir the prefix of the scratch directory for the 
+     *                  lookup within the LDJ Kit scratch directory;
+     *                  must be at least 3 characters
+     * @param name the name of the downloadable JAR file in the lookup codebase
+     * @param conf the Config object for this test run
+     *
+     * @return the <code>ServiceRegistrar</code> of the lookup that was started
+     */
+    public static ServiceRegistrar startLookup(String[] groups,
+	int maxLeaseDuration, String lookupDir, String name,
+	Config conf) throws Exception
+    {
+	SysConfig sysConfig = conf.getSysConfig();
+
+	InstrumentedClassServer ics = conf.getClassServer();
+
+	String codebase = null;
+	if (name != null) {
+	    codebase = ics.getCodeURL().toString() + name;
+	} else {
+	    codebase = ics.getCodeURL().toString() + "compat-lus-dl.jar";
+	}
+	log(Config.ALL,"TestUtility: lookup codebase: " + codebase);
+
+        String sep = File.separator;
+
+        String installDir = 
+	    sysConfig.getStringConfigVal("com.sun.jini.compat.installDir",null);
+	if (installDir == null) {
+	    throw new Exception("Environment Failure: the "
+		+ "com.sun.jini.compat.installDir property must be set");
+        }
+
+	String classpath = installDir + sep + "lib" + sep + "compat-lus.jar";
+	log(Config.ALL,"TestUtility: lookup classpath: " + classpath);
+
+	String policy = installDir + sep + "policy" + sep + "policy.all";
+	log(Config.ALL,"TestUtility: lookup policy: " + policy);
+
+        String scratchDir = 
+            sysConfig.getStringConfigVal("com.sun.jini.compat.scratchDir",null);
+	File tempFile = null;
+        if (scratchDir == null) {
+	    tempFile = File.createTempFile(lookupDir, null);
+        } else {
+	    tempFile = File.createTempFile(lookupDir,null,new File(scratchDir));
+	}
+	String lookupScratchDir = tempFile.getAbsolutePath();
+	tempFile.delete();
+	log(Config.ALL,"TestUtility: lookup lookupScratchDir: " 
+            + lookupScratchDir);
+
+        String platformDir = sysConfig.getStringConfigVal(
+            "com.sun.jini.compat.lookupPlatformDir",null);
+        Properties props = null;
+        if (platformDir != null) {
+            props = new Properties();
+            props.setProperty("java.ext.dirs",platformDir);
+        }
+	log(Config.ALL,"TestUtility: lookup platformDir: " + platformDir);
+
+        /* Start the lookup service implementation that resides in the
+         * package 'com.sun.jini.compat.reggie' */
+	ServiceStarter.Created c = ServiceStarter.create
+                                      (codebase,
+                                       policy,
+                                       lookupScratchDir,
+                                       DiscoveryGroupManagement.NO_GROUPS,
+                                       null, // locators
+                                       null, // default javaProgram
+                                       props, // properties
+                                       null, // default options
+                                       "com.sun.jini.compat.reggie.CreateLookup",
+	                               "com.sun.jini.compat.reggie.RegistrarImpl",
+                                       classpath,
+                                       "lookup");
+        ServiceRegistrar lookup = (ServiceRegistrar)c.proxy;
+
+	Administrable a = (Administrable)lookup;
+	RegistrarAdmin ra = (RegistrarAdmin)a.getAdmin();
+	ra.setMinMaxServiceLease(maxLeaseDuration);
+	// Make sure the events never timeout too early
+	ra.setMinMaxEventLease(100 * conf.getFailureTime());
+
+	DiscoveryAdmin da = (DiscoveryAdmin)a.getAdmin();
+	da.addMemberGroups(groups);
+
+	return lookup;
+    }
+
+    /**
+     * Stops the lookup.
+     * 
+     * @param lookup the <code>ServiceRegistrar</code> for the lookup
+     * @param conf the Config object for this test run
+     */
+    public static void stopLookup(ServiceRegistrar lookup, Config conf)  
+	throws RemoteException
+    {
+	if (lookup != null) {
+	    DestroyAdmin da = (DestroyAdmin)((Administrable)lookup).getAdmin();
+	    da.destroy();
+	}
+
+	//to use ServiceDestroyer, we would need to save the gid in startLookup
+        //com.sun.jini.start.ServiceDestroyer.destroy(gid, lookup, 30);
+    }
+
+    /**
+     * This method returns a String of a specified length that is made
+     * up of random characters.  The characters in the string all return
+     * <code>true</code> when passed to <code>Character.isLetter()</code>.
+     * 
+     * @param length the length of the string to return
+     *
+     * @return the random string
+     */
+    public static String randomString(int length) {
+	StringBuffer randString = new StringBuffer(length);
+	Random random = new Random(System.currentTimeMillis());
+
+	while (randString.length() < length){
+	   char c = (char)((random.nextInt() % ('z'-'A')) + 'A');
+	   if (Character.isLetter(c)) {
+		randString = randString.append(c);
+	   }
+	}
+
+	return randString.toString();
+    }	
+
+    /**
+     * Checks if the value of the given service ID conforms to the
+     * ServiceID specification.
+     *
+     * @param serviceID the Service ID to check
+     *
+     * @return <code>null</code> if <code>serviceID</code> is properly formatted;
+     *         otherwise a description of the problem is returned
+     */
+    public static String checkServiceID(ServiceID serviceID) {
+        int variant = (int) (serviceID.getLeastSignificantBits() >> 62) & 0x3;
+        if (variant != 2) {
+            return (serviceID + " has invalid variant " + variant);
+        }
+        int version = (int) (serviceID.getMostSignificantBits() >> 12) & 0xF;
+        if (!(version == 1 || version == 4)) {
+            return (serviceID + " has invalid version " + version);
+        }
+        return null;
+    }
+}
+

Propchange: river/tck/src/com/sun/jini/compat/harness/TestUtility.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: river/tck/src/com/sun/jini/compat/harness/package.html
URL: http://svn.apache.org/viewvc/river/tck/src/com/sun/jini/compat/harness/package.html?rev=1234278&view=auto
==============================================================================
--- river/tck/src/com/sun/jini/compat/harness/package.html (added)
+++ river/tck/src/com/sun/jini/compat/harness/package.html Sat Jan 21 07:28:27 2012
@@ -0,0 +1,17 @@
+<!--
+ ! 
+ ! Copyright 2005, Sun Microsystems, Inc.
+ ! Licensed under the <a href="http://www.apache.org/licenses/LICENSE-2.0">
+ ! Apache License, Version 2.0</a>.
+ ! 
+ !-->
+<BODY BGCOLOR="white">
+
+This package contains the test harness component of the Jini(TM) Technology Lookup, Discovery, and Join Compatibility Kit.
+It selects the tests to run and runs the tests.
+
+@see <a href="../../../../../../test-spec.html"><i>Jini Technology Lookup, Discovery, and Join Compatibility Kit Test Descriptions</i></a>
+@see <a href="../../../../../../running.html"><i>Running the Jini Technology Lookup, Discovery, and Join Compatibility Kit</i></a>
+
+</BODY>
+</HTML>

Propchange: river/tck/src/com/sun/jini/compat/harness/package.html
------------------------------------------------------------------------------
    svn:eol-style = native

Added: river/tck/src/com/sun/jini/compat/lease/DesiredExpirationListener.java
URL: http://svn.apache.org/viewvc/river/tck/src/com/sun/jini/compat/lease/DesiredExpirationListener.java?rev=1234278&view=auto
==============================================================================
--- river/tck/src/com/sun/jini/compat/lease/DesiredExpirationListener.java (added)
+++ river/tck/src/com/sun/jini/compat/lease/DesiredExpirationListener.java Sat Jan 21 07:28:27 2012
@@ -0,0 +1,48 @@
+/*
+ * 
+ * Copyright 2005 Sun Microsystems, Inc.
+ * 
+ * Licensed 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 com.sun.jini.compat.lease;
+
+/**
+ * Sub-interface of <code>LeaseListener</code> that clients must
+ * implement if they want to receive desired expiration reached events
+ * in addition to renewal failure events.
+ *
+ * @author Sun Microsystems, Inc.
+ *
+ * @see LeaseRenewalManager
+ */
+public interface DesiredExpirationListener extends LeaseListener {
+    /**
+     * Method used to delivered desired expiration reached events.
+     * The <code>getException</code> method of the passed event will
+     * always return <code>null</code>.
+     * <p>
+     * Note that prior to invoking this method, the
+     * <code>LeaseRenewalManager</code> removes the affected lease
+     * from the managed set of leases. Note also that because of the
+     * re-entrancy guarantee made by the <code>LeaseRenewalManager</code>,
+     * the it is safe to call back into the renewal manager from this
+     * method.
+     * <p>
+     * @param e  instance of <code>LeaseRenewalEvent</code> containing
+     *           information about the lease that the 
+     *           <code>LeaseRenewalManager</code> that was removed 
+     *           because its desired expiration was reached.
+     */
+    public void expirationReached(LeaseRenewalEvent e);
+}

Propchange: river/tck/src/com/sun/jini/compat/lease/DesiredExpirationListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: river/tck/src/com/sun/jini/compat/lease/LeaseListener.java
URL: http://svn.apache.org/viewvc/river/tck/src/com/sun/jini/compat/lease/LeaseListener.java?rev=1234278&view=auto
==============================================================================
--- river/tck/src/com/sun/jini/compat/lease/LeaseListener.java (added)
+++ river/tck/src/com/sun/jini/compat/lease/LeaseListener.java Sat Jan 21 07:28:27 2012
@@ -0,0 +1,82 @@
+/*
+ * 
+ * Copyright 2005 Sun Microsystems, Inc.
+ * 
+ * Licensed 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 com.sun.jini.compat.lease;
+
+/** 
+ * The interface that receivers of <code>LeaseRenewalEvent</code>s must
+ * implement.
+ * <p>
+ * With respect to an entity that uses the <code>LeaseRenewalManager</code>
+ * to manage leases granted to the entity, this interface defines the
+ * mechanism through which an entity receives notification that the 
+ * <code>LeaseRenewalManager</code> has failed to renew one of the leases
+ * the <code>LeaseRenewalManager</code> is managing for the entity. Such
+ * renewal failures typically occur because of one of the following
+ * conditions is met:
+ * <ul>
+ *   <li> After successfully renewing a lease any number of times, and
+ *        experiencing no failures, the
+ *        <code>LeaseRenewalManager</code> determines - prior to the
+ *        next renewal attempt - that the actual expiration time of
+ *        the lease has passed; implying that any further attempt to
+ *        renew the lease would be fruitless
+ *   <li> An indefinite exception occurs during each attempt to renew a
+ *        lease from the point that the first such exception occurs
+ *        until the point when the <code>LeaseRenewalManager</code>
+ *        determines that lease's actual expiration time has passed.
+ *   <li> A bad object, bad invocation, or <code>LeaseException</code>
+ *        occurs during a lease renewal attempt (collectively referred 
+ *        to as definite exceptions).
+ * </ul>
+ * It is the responsibility of the entity to register with the 
+ * <code>LeaseRenewalManager</code>. The object that
+ * implements this interface should define the actions to
+ * take upon receipt of such notifications. Then, when one of the above 
+ * conditions occurs, the <code>LeaseRenewalManager</code> will send an 
+ * instance of the <code>LeaseRenewalEvent</code> class to that listener
+ * object. Note that prior to sending the event, the 
+ * <code>LeaseRenewalManager</code> will remove the affected lease from
+ * its managed set of leases.
+ *
+ * @author Sun Microsystems, Inc.
+ *
+ * @see net.jini.core.lease.Lease
+ * @see com.sun.jini.compat.lease.LeaseRenewalManager
+ * @see com.sun.jini.compat.lease.LeaseRenewalEvent
+ */
+public interface LeaseListener extends java.util.EventListener {
+    /**
+     * Called by the <code>LeaseRenewalManager</code> when it cannot 
+     * renew a lease that it is managing, and the lease's desired expiration
+     * time has not yet been reached.
+     * <p>
+     * Note that prior to invoking this method, the
+     * <code>LeaseRenewalManager</code> removes the affected lease
+     * from the managed set of leases. Note also that because of the
+     * re-entrancy guarantee made by the <code>LeaseRenewalManager</code>,
+     * new leases can be safely added by this method.
+     * <p>
+     * @param e  instance of <code>LeaseRenewalEvent</code> containing
+     *           information about the lease that the 
+     *           <code>LeaseRenewalManager</code> was unable to renew, as
+     *           well as information about the condition that made the 
+     *           <code>LeaseRenewalManager</code> fail to renew the lease 
+     */
+    void notify(LeaseRenewalEvent e);
+}

Propchange: river/tck/src/com/sun/jini/compat/lease/LeaseListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: river/tck/src/com/sun/jini/compat/lease/LeaseRenewalEvent.java
URL: http://svn.apache.org/viewvc/river/tck/src/com/sun/jini/compat/lease/LeaseRenewalEvent.java?rev=1234278&view=auto
==============================================================================
--- river/tck/src/com/sun/jini/compat/lease/LeaseRenewalEvent.java (added)
+++ river/tck/src/com/sun/jini/compat/lease/LeaseRenewalEvent.java Sat Jan 21 07:28:27 2012
@@ -0,0 +1,193 @@
+/*
+ * 
+ * Copyright 2005 Sun Microsystems, Inc.
+ * 
+ * Licensed 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 com.sun.jini.compat.lease;
+
+import net.jini.core.lease.*;
+
+/**
+ * Event generated when a <code>LeaseRenewalManager</code> cannot renew
+ * a lease.
+ * <p>
+ * The <code>LeaseRenewalEvent</code>s are sent to the
+ * <code>LeaseListener</code> (if any) associated with a given lease
+ * when the lease was added to the managed set.  These events are
+ * typically generated because of one of the following conditions occur:
+ * <ul>
+ *   <li> After successfully renewing a lease any number of times and
+ *        experiencing no failures, the
+ *        <code>LeaseRenewalManager</code> determines - prior to the
+ *        next renewal attempt - that the actual expiration time of
+ *        the lease has passed; implying that any further attempt to
+ *        renew the lease would be fruitless.
+ *   <li> An indefinite exception occurs during each attempt to renew
+ *        a lease from the point that the first such exception occurs
+ *        until the point when the LeaseRenewalManager determines that
+ *        lease's actual expiration time has passed.
+ *   <li> A definite exception occurs during a lease renewal 
+ *        attempt.
+ * </ul>
+ * <p>
+ * Note, bad object exceptions, bad invocation exceptions, and
+ * <code>LeaseException</code>s are all considered definite
+ * exceptions.
+ * <p>
+ * This class encapsulates information about both the lease on which the
+ * failure occurred, as well as information about the condition that 
+ * caused the renewal attempt to fail.
+ * <p>
+ * This class is a subclass of the class <code>EventObject</code>, adding
+ * the following additional items of abstract state:
+ * <ul>
+ *   <li> The lease on which the renewal attempt failed, and to which
+ *        the event pertains
+ *   <li> The desired expiration time of the affected lease
+ *   <li> The <code>Throwable</code> associated with the last renewal
+ *        attempt (if any)
+ * </ul>
+ * <p>
+ * In addition to the methods of the <code>EventObject</code> class, this
+ * class defines methods through which this additional state may be retrieved.
+ * 
+ * The source associated with a <code>LeaseRenewalEvent</code> will be the
+ * <code>LeaseRenewalManager</code> that generated the event.
+ *
+ * @author Sun Microsystems, Inc.
+ *
+ * @see net.jini.core.lease.Lease
+ * @see net.jini.core.lease.LeaseException
+ * @see com.sun.jini.compat.lease.LeaseRenewalManager
+ * @see com.sun.jini.compat.lease.LeaseListener
+ * @see java.util.EventObject
+ */
+public class LeaseRenewalEvent extends java.util.EventObject {
+    private static final long serialVersionUID = -626399341646348302L;
+
+    /**
+     * The failed lease.
+     *
+     * @serial
+     */
+    private Lease lease;
+
+    /**
+     * The desired expiration of the failed lease.
+     *
+     * @serial
+     */
+    private long expiration;
+
+    /**
+     * The exception that caused the failure, if any.
+     *
+     * @serial
+     */
+    private Throwable ex;
+
+    /** 
+     * Constructs an instance of this class with the specified state.
+     *
+     * @param source     reference to the instance of the
+     *                   <code>LeaseRenewalManager</code> that generated the
+     *                   event
+     * @param lease      the lease to which the event pertains
+     * @param expiration the desired expiration time for the
+     *                   affected lease
+     * @param ex         The <code>Throwable</code> associated with the
+     *                   last renewal attempt (if any)
+     *
+     * @see net.jini.core.lease.Lease
+     * @see com.sun.jini.compat.lease.LeaseRenewalManager
+     */
+    public LeaseRenewalEvent(LeaseRenewalManager source,
+			     Lease lease,
+			     long expiration,
+			     Throwable ex) 
+    {
+	super(source);
+	this.lease = lease;
+	this.expiration = expiration;
+	this.ex = ex;
+    }
+
+    /** 
+     * Returns a reference to the lease to which the event pertains.
+     *
+     * @return the <code>Lease</code> object corresponding to the lease
+     *         on which the renewal attempt failed
+     *
+     * @see net.jini.core.lease.Lease
+     */
+    public Lease getLease() {
+	return lease;
+    }
+
+    /** 
+     * Returns the desired expiration time of the lease to which
+     * event pertains.
+     *
+     * @return a <code>long</code> value that represents the desired 
+     *         expiration time of the lease on which the renewal attempt
+     *         failed
+     */
+    public long getExpiration() {
+	return expiration;
+    }
+
+    /** 
+     * Returns the exception (if any) that caused the event to be sent.
+     * The conditions under which the event may be sent, and the related
+     * values returned by this method, are as follows:
+     * <ul>
+
+     * <li> When any lease in the managed set has passed its actual
+     *      expiration time, and either the most recent renewal
+     *      attempt was successful or there have been no renewal
+     *      attempts, the <code>LeaseRenewalManager</code> will cease
+     *      any further attempts to renew the lease, and will send a
+     *      LeaseRenewalEvent with no associated exception. In this
+     *      case, invoking this method will return null
+     * <li> For any lease from the managed set for which the most     
+     *      recent renewal attempt was unsuccessful because of the
+     *      occurrence of a indefinite exception, the
+     *      <code>LeaseRenewalManager</code> will continue to attempt
+     *      to renew the affected lease at the appropriate times
+     *      until: the renewal succeeds, the lease's expiration time
+     *      has passed, or a renewal attempt throws a definite
+     *      exception. If a definite exception is thrown or the lease
+     *      expires, the <code>LeaseRenewalManager</code> will cease
+     *      any further attempts to renew the lease, and will send a
+     *      <code>LeaseRenewalEvent</code> containing the exception
+     *      associated with the last renewal attempt.
+
+     * <li> If, while attempting to renew a lease from the managed
+     *      set, a definite exception is encountered, the
+     *      <code>LeaseRenewalManager</code> will cease any further
+     *      attempts to renew the lease, and will send a
+     *      <code>LeaseRenewalEvent</code> containing the particular
+     *      exception that occurred.
+     * </ul>
+     *
+     * @return an instance of <code>Throwable</code> or <code>null</code>,
+     *         indicating the condition that caused the 
+     *         <code>LeaseRenewalManager</code> to fail to renew the 
+     *         affected lease */
+    public Throwable getException() {
+	return ex;
+    }
+}

Propchange: river/tck/src/com/sun/jini/compat/lease/LeaseRenewalEvent.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: river/tck/src/com/sun/jini/compat/lease/LeaseRenewalManager.java
URL: http://svn.apache.org/viewvc/river/tck/src/com/sun/jini/compat/lease/LeaseRenewalManager.java?rev=1234278&view=auto
==============================================================================
--- river/tck/src/com/sun/jini/compat/lease/LeaseRenewalManager.java (added)
+++ river/tck/src/com/sun/jini/compat/lease/LeaseRenewalManager.java Sat Jan 21 07:28:27 2012
@@ -0,0 +1,1155 @@
+/*
+ * 
+ * Copyright 2005 Sun Microsystems, Inc.
+ * 
+ * Licensed 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 com.sun.jini.compat.lease;
+
+import com.sun.jini.compat.thread.TaskManager;
+import com.sun.jini.compat.constants.ThrowableConstants;
+import java.rmi.NoSuchObjectException;
+import java.rmi.RemoteException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import net.jini.core.lease.*;
+import net.jini.lease.ExpirationWarningEvent;
+import net.jini.lease.LeaseRenewalService;
+import net.jini.lease.LeaseRenewalSet;
+import net.jini.lease.LeaseUnmarshalException;
+import net.jini.lease.RenewalFailureEvent;
+
+/**
+ * Provides for the systematic renewal and overall management of a set
+ * of leases associated with one or more remote entities on behalf of
+ * a local entity.
+ * <p>
+ * This class removes much of the administrative burden associated
+ * with lease renewal.  Clients of the renewal manager simply give
+ * their lease's to the manager and the manager renews each lease as
+ * necessary to achieve a <em>desired expiration</em> time (which may
+ * be latter than the lease's current <em>actual expiration</em>
+ * time).  Failures encountered while renewing a lease can optionally
+ * be reflected to the client via <code>LeaseRenewalEvent</code>s.
+ * <p>
+ * Note that this class is not remote. Entities wishing to use this
+ * class must create an instance of this class in their own virtual
+ * machine to locally manage the leases granted to them.  If the
+ * virtual machine that the manager was created in exits or crashes
+ * the renewal manager will be destroyed.
+ * <p>
+ * The LeaseRenewalManager distinguishes between two time values
+ * associated with lease expiration: the <em>desired expiration</em>
+ * time for the lease, and the <em>actual expiration</em> time granted
+ * when the lease is created or last renewed. The desired expiration
+ * represents when the client would like the lease to expire. The
+ * actual expiration represents when the lease is going to expire if
+ * it is not renewed. Both time values are absolute times, not
+ * relative time durations. The desired expiration time can be
+ * retrieved using the renewal manager's <code>getExpiration</code>
+ * method. The actual expiration time of a lease object can be
+ * retrieved by invoking the lease's <code>getExpiration</code>
+ * method.
+ * <p>
+ * Each lease in the managed set also has two other associated
+ * attributes: a desired <em>renewal duration</em>, and a
+ * <em>remaining desired duration</em>. The desired renewal duration
+ * is specified (directly or indirectly) when the lease is added to
+ * the set. This duration must normally be a positive number; however,
+ * it may be <code>Lease.ANY</code> if the lease's desired expiration
+ * is <code>Lease.FOREVER</code>. The remaining desired duration is
+ * always the desired expiration less the current time.
+ * <p>
+ * Each time a lease is renewed, the renewal manager will ask for an
+ * extension equal to the lease's renewal duration if the renewal
+ * duration is:
+ * <ul>
+ * <li> <code>Lease.ANY</code>, or 
+ * <li> less than the remaining desired duration,
+ * </ul>
+ * otherwise it will ask for an extension equal to the lease's
+ * remaining desired duration. 
+ * <p>
+ * Once a lease lease is given to a lease renewal manager, the manager
+ * will continue to renew the lease until one of the following occurs:
+ * <ul>
+ * <li> The lease's desired or actual expiration time is reached.
+ * <li> An explicit removal of the lease from the set is requested via
+ *      a <code>cancel</code>, <code>clear</code>, or <code>remove</code>
+ *      call on the renewal manager.
+ * <li> The renewal manager tries to renew the lease and gets a bad
+ *      object exception, bad invocation exception, or
+ *      <code>LeaseException</code>.
+ * </ul>
+ * <p>
+ * The methods of this class are appropriately synchronized for
+ * concurrent operation. Additionally, this class makes certain
+ * guarantees with respect to concurrency. When this class makes a
+ * remote call (for example, when requesting the renewal of a lease),
+ * any invocations made on the methods of this class will not be
+ * blocked. Similarly, this class makes a re-entrancy guarantee with
+ * respect to the listener objects registered with this class. Should
+ * this class invoke a method on a registered listener (a local call),
+ * calls from that method to any other method of this class are
+ * guaranteed not to result in a deadlock condition.
+ * 
+ * @author Sun Microsystems, Inc.
+ *
+ * @see net.jini.core.lease.Lease
+ * @see net.jini.core.lease.LeaseException
+ * @see LeaseRenewalEvent 
+ */
+
+public class LeaseRenewalManager {
+    /** time window in which to look for batchable leases */
+    private static final long RENEW_BATCH_TIME_GAP = 1000 * 60 * 5;
+    /** assumed round trip time for any lease renewal */
+    private static final long RENEW_RTT = 1000 * 10;
+    /** maximum number of concurrent lease renewals */
+    private static final int  MAX_RENEW_THREAD = 10;
+
+    /** 
+     * Entries for leases that are not actively being renewed.
+     * Lease with the earliest renewal is last in the map.
+     */
+    private final SortedMap leases = new TreeMap();
+
+    /** entries for leases that are actively being renewed */
+    private final List leaseInRenew = new ArrayList(1);
+    /** the queuer task */
+    private QueuerTask queuer = null;
+    /** task manager for queueing and renewing leases */
+    TaskManager tasker = new TaskManager(MAX_RENEW_THREAD + 1,
+					 1000 * 15, 1.0f);
+    /** maintains concurrency constraints when calculating actual renewals */
+    private final List calcList = new ArrayList(MAX_RENEW_THREAD);
+
+    private final class RenewTask implements TaskManager.Task {
+	/** entries of leases to renew (if multiple, all can be batched) */
+	private final List bList;
+
+	/** 
+	 * true if this task only holds leases that have reached their actual
+	 * or desired expiration
+	 */
+	private final boolean noRenewals;
+
+	/**
+	 * Create a collection of entries whose leases can be batch
+	 * renewed with the last lease in the map, or a list of
+	 * entries whose leases that need to be removed.  Which is
+	 * created depends on the state of the last lease in the map.
+	 * Remove each entry from the map, and add them to
+	 * leaseInRenew.
+	 */
+	RenewTask(long now) {
+	    bList = new ArrayList(1);
+	    Entry e = (Entry)leases.lastKey();
+
+	    if (e.renewalsDone() || e.endTime <= now) {
+		noRenewals = true;
+		Map lMap = leases.tailMap(new Entry(now));
+		for (Iterator iter = lMap.values().iterator(); 
+		     iter.hasNext(); )
+		{
+		    Entry be = (Entry)iter.next();
+		    if (be.renewalsDone() || be.endTime <= now) {
+			iter.remove();
+			/* Only add to bList if we need to tell someone
+			 * about this lease's departure
+			 */
+			if (be.listener != null)
+			    bList.add(be);
+		    }
+		}
+	    } else {
+		noRenewals = false;
+		Map lMap = leases.tailMap(
+		    new Entry(e.renew + RENEW_BATCH_TIME_GAP));
+		for (Iterator iter = lMap.values().iterator(); 
+		     iter.hasNext(); )
+		{
+		    Entry be = (Entry)iter.next();
+		    if (be == e || be.canBatch(e)) {
+			iter.remove();
+			leaseInRenew.add(be);
+			bList.add(be);
+		    }
+		}
+	    }
+	}
+
+	public void run() {
+	    if (noRenewals) {
+		// Just notify
+		tell(bList);
+	    } else {
+		// Get rid of any leases that have expired and then
+		// do renewals
+		long now = System.currentTimeMillis();
+		List bad = processBadLeases(now);
+		if (!bList.isEmpty())
+		    renewAll(bList, now);
+		if (bad != null)
+		    tell(bad);
+	    }
+	}
+
+	/** No ordering. */
+	public boolean runAfter(List tasks, int size) {
+	    return false;
+	}
+
+	/**
+	 * Find any expired leases, remove them from bList and
+	 * leaseInRenew, and return any with listeners.
+	 */
+	private List processBadLeases(long now) {
+	    List bad = null;
+	    synchronized (LeaseRenewalManager.this) {
+		for (Iterator iter = bList.iterator(); iter.hasNext(); ) {
+		    Entry e = (Entry)iter.next();
+		    if (e.endTime <= now) {
+			iter.remove();
+			if (removeLeaseInRenew(e) && e.listener != null) {
+			    if (bad == null)
+				bad = new ArrayList(1);
+			    bad.add(e);
+			}
+		    }
+		}
+	    }
+	    return bad;
+	}
+    }
+
+    private static class Entry implements Comparable {
+	/* Since the cnt only gets modified in the constructor, and the
+	 * constructor is always called from synchronized code, the cnt
+	 * does not need to be synchronized.
+	 */
+	private static long cnt = 0;
+
+	/** uid */
+	public final long id;
+	/** the lease */
+	public final Lease lease;
+	/** desired expiration */
+	public long expiration;
+	/** renew duration */
+	public long renewDuration;
+	/** the listener, or null */
+	public final LeaseListener listener;
+	/** current actual expiration */
+	public long endTime;
+
+	/** 
+	 * The next time we have to do something with this
+	 * lease. Usually a renewal, but could be removing it from the managed
+	 * set because its desired expiration has been reached 
+	 */
+	public long renew;
+
+	/** actual time to renew, given concurrency limitations */
+	public long actualRenew;
+	/** renewal exception, or null */
+	public Throwable ex = null;
+
+	public Entry(Lease lease,
+		     long expiration,
+		     long renewDuration,
+		     LeaseListener listener)
+	{
+	    this.endTime = lease.getExpiration();
+	    this.lease = lease;
+	    this.expiration = expiration;
+	    this.renewDuration = renewDuration;
+	    this.listener = listener;
+	    id = cnt++;
+	}
+
+	/** create a fake entry for tailMap */
+	public Entry(long renew) {
+	    this.renew = renew;
+	    id = Long.MAX_VALUE;
+	    lease = null;
+	    listener = null;
+	}
+
+	/**
+	 * If the renewDuration is ANY, return ANY, otherwise return the
+	 * minimum of the renewDuration and the time remaining until the
+	 * desired expiration.
+	 */
+	public long getRenewDuration(long now) {
+	    if (renewDuration == Lease.ANY)
+		return renewDuration;
+	    return Math.min(expiration - now, renewDuration);
+	}
+
+	/** calculate the renew time for the lease entry */
+	public void calcRenew(long now) {
+	    endTime = lease.getExpiration();
+	    if (renewalsDone()) {
+		if (null == desiredExpirationListener()) 
+		    // Nothing urgent needs to be done with this lease
+		    renew = Long.MAX_VALUE;
+		else 
+		    /* tell listener about dropping this lease in a timely
+		     * fashion 
+		     */
+		    renew = expiration; 
+		return;
+	    }
+	    long delta = endTime - now;
+	    if (delta <= RENEW_RTT * 2)
+		delta = RENEW_RTT;
+	    else if (delta <= RENEW_RTT * 8)
+		delta /= 2;
+	    else if (delta <= 1000 * 60 * 60 * 24 * 7)
+		delta /= 8;
+	    else if (delta <= 1000 * 60 * 60 * 24 * 14)
+		delta = 1000 * 60 * 60 * 24;
+	    else
+		delta = 1000 * 60 * 60 * 24 * 3;
+	    renew = endTime - delta;
+	}
+
+	/** calculate a new renew time due to an indefinite exception */
+	public void delayRenew() {
+	    long delta = endTime - renew;
+	    if (delta <= RENEW_RTT)
+		return;
+	    else if (delta <= RENEW_RTT * 3)
+		 delta = RENEW_RTT;
+	    else if (delta <= 1000 * 60 * 60)
+		delta /= 3;
+	    else if (delta <= 1000 * 60 * 60 * 24)
+		delta = 1000 * 60 * 30;
+	    else if (delta <= 1000 * 60 * 60 * 24 * 7)
+		delta = 1000 * 60 * 60 * 3;
+	    else
+		delta = 1000 * 60 * 60 * 8;
+	    renew += delta;
+	}
+
+	/** sort by decreasing renew time, secondary sort by decreasing id */
+	public int compareTo(Object obj) {
+	    if (this == obj)
+		return 0;
+	    Entry e = (Entry)obj;
+	    if (renew < e.renew || (renew == e.renew && id < e.id))
+		return 1;
+	    return -1;
+	}
+
+	/**
+	 * Returns true if this lease can be batched with the (earlier)
+	 * renewal of the given lease. Returns true if the two leases can
+	 * be batched and a) either the renew duration of this lease is
+	 * ANY or the time remaining until the renewal of this lease
+	 * (measured at the time e would be renewed) is no more than
+	 * RENEW_RTT/2 or the time remaining until the current expiration
+	 * of this lease (measured at the time e would be renewed) is no more
+	 * than 1/2 of the renew duration, and b) either the renew duration
+	 * of e's lease is ANY or e's lease will not be renewed again before
+	 * this lease needs to be renewed.  This method must be called with
+	 * an entry such that e.renew <= this.renew.
+	 */
+	public boolean canBatch(Entry e) {
+	    return (!renewalsDone() &&
+		    !e.renewalsDone() &&
+		    lease.canBatch(e.lease) &&
+		    (renewDuration == Lease.ANY ||
+		     renew - e.renew <= RENEW_RTT / 2 ||
+		     endTime - e.renew <= renewDuration / 2) &&
+		    (e.renewDuration == Lease.ANY ||
+		     e.renew > renew - e.renewDuration ||
+		     e.renew >= e.expiration - e.renewDuration));
+	}
+
+	/**
+	 * Return the DesiredExpirationListener associated with this
+	 * lease, or null if there none.
+	 */
+	public DesiredExpirationListener desiredExpirationListener() {
+	    if (listener == null)
+		return null;
+
+	    if (listener instanceof DesiredExpirationListener) 
+		return (DesiredExpirationListener)listener;
+
+	    return null;
+	}
+
+	/**
+	 * Return true if the actual expiration is greater than or
+	 * equal to the desired expiration (e.g. we don't need
+	 * to renew this lease any more.
+	 */
+	public boolean renewalsDone() {
+	    return expiration <= endTime;
+	}
+    }
+
+    /**
+     * No-argument constructor that creates an instance of this class that
+     * initially manages no leases.
+     */
+    public LeaseRenewalManager() {
+    }
+
+    /**
+     * Constructs an instance of this class that will initially manage
+     * a single lease. Employing this form of the constructor is
+     * equivalent to invoking the no-argument form of the constructor
+     * followed by an invocation of the three-argument form of the
+     * <code>renewUntil</code> method.  See <code>renewUntil</code>
+     * for details on the arguments and what exceptions may be thrown
+     * by this constructor.
+     *
+     * @param lease       reference to the initial lease to manage
+     * @param desiredExpiration  the desired expiration
+     *                    for <code>lease</code>
+     * @param listener    reference to the <code>LeaseListener</code> object
+     *                    that will receive notifications of any exceptional
+     *                    conditions that occur during renewal attempts. If
+     *                    <code>null</code> no notifications will
+     *                    be sent.
+     * @throws java.lang.NullPointerException if <code>lease</code> is 
+     * <code>null</code>
+     *
+     * @see net.jini.lease.LeaseListener
+     * @see com.sun.jini.compat.lease.LeaseRenewalManager#renewUntil 
+     */
+    public LeaseRenewalManager(Lease lease,
+                               long desiredExpiration,
+			       LeaseListener listener)
+    {
+	renewUntil(lease, desiredExpiration, listener);
+    }
+
+    /**
+     * Include a lease in the managed set 
+     * until a specified time. 
+     * <p>
+     * If <code>desiredExpiration</code> is <code>Lease.ANY</code> calling
+     * this method is equivalent the following call:
+     * <pre>
+     *     renewUntil(lease, Lease.FOREVER, Lease.ANY, listener)
+     * </pre>
+     * otherwise it is equivalent to this call:
+     * <pre>
+     *     renewUntil(lease, desiredExpiration, Lease.FOREVER, listener)
+     * </pre>
+     * <p>
+     * @param lease The <code>Lease</code> to be managed
+     * @param desiredExpiration When the client wants the lease to expire
+     *                          in milliseconds since the beginning of the 
+     *                          epoch.
+     * @param listener    reference to the <code>LeaseListener</code> object
+     *                    that will receive notifications of any exceptional
+     *                    conditions that occur during renewal attempts. If
+     *                    <code>null</code> no notifications will
+     *                    be sent.
+     * @throws NullPointerException if lease is <code>null</code>
+     * @see com.sun.jini.compat.lease.LeaseRenewalManager#renewUntil
+     */
+    public void renewUntil(Lease lease,
+			   long desiredExpiration,
+			   LeaseListener listener)
+    {
+	if (desiredExpiration == Lease.ANY)
+	    renewUntil(lease, Lease.FOREVER, Lease.ANY, listener);
+	else
+	    renewUntil(lease, desiredExpiration, Lease.FOREVER, listener);
+    }
+
+    /**
+     * Include a lease in the managed set until a
+     * specified time and with a specified renewal duration.
+     * <p>
+     * This method takes as arguments: a reference to the lease to
+     * manage, the desired expiration time of the lease, the renewal
+     * duration time for the lease, and a reference to the
+     * <code>LeaseListener</code> object that will receive notification of
+     * exceptional conditions when attempting to renew this lease. The
+     * <code>LeaseListener</code> argument may be <code>null</code>.
+     * <p>
+     * If <code>null</code> is passed as the lease parameter, a
+     * <code>NullPointerException</code> will be thrown. If the
+     * <code>desiredExpiration</code> parameter is
+     * <code>Lease.FOREVER</code>, the <code>renewalDuration</code>
+     * parameter may be <code>Lease.ANY</code> or any positive value;
+     * otherwise, the <code>renewalDuration</code> parameter must be a
+     * positive value. If the <code>renewalDuration</code> parameter
+     * does not meet these requirements, an
+     * <code>IllegalArgumentException</code> will be thrown.
+     * <p>
+     * If the lease passed to this method is already in the set of
+     * managed leases, the listener object, the desired expiration, and
+     * the renewal duration associated with that lease will be replaced
+     * with the new listener, desired expiration, and renewal duration.
+     * <p>
+     * The lease will remain in the set until one of the following occurs:
+     * <ul>
+     * <li> The lease's desired or actual expiration time is reached.
+     * <li> An explicit removal of the lease from the set is requested via
+     *      a <code>cancel</code>, <code>clear</code>, or <code>remove</code>
+     *      call on the renewal manager.
+     * <li> The renewal manager tries to renew the lease and gets a bad
+     *      object exception, bad invocation exception, or
+     *      <code>LeaseException</code>.
+     * </ul>
+     * <p>
+     * This method will interpret the value of the
+     * <code>desiredExpiration</code> parameter as the desired
+     * absolute system time after which the lease is no longer
+     * valid. This argument provides the ability to indicate an
+     * expiration time that extends beyond the actual expiration of
+     * the lease. If the value passed for this argument does indeed
+     * extend beyond the lease's actual expiration time, then the
+     * lease will be systematically renewed at appropriate times until
+     * one of the conditions listed above occurs.  If the value is
+     * less than or equal to the actual expiration time, nothing will
+     * be done to modify the time when the lease actually
+     * expires. That is, the lease will not be renewed with an
+     * expiration time that is less than the actual expiration time of
+     * the lease at the time of the call.
+     * <p>
+     * If a non-<code>null</code> object reference is passed in as the
+     * <code>LeaseListener</code> parameter, it will receive
+     * notification of exceptional conditions occurring upon a renewal
+     * attempt of the lease. In particular, exceptional conditions
+     * include the reception of a <code>LeaseException</code>, bad
+     * object exception, or bad invocation exception (collectively
+     * these are referred to as <em>definite exceptions</em>) during a
+     * renewal attempt or the lease's actual expiration being reached
+     * before its desired expiration.
+     * <p>
+     * If a definite exception occurs during a lease renewal request,
+     * the exception will be wrapped in an instance of the
+     * <code>LeaseRenewalEvent</code> class and sent to the listener.
+     * <p>
+     * If an indefinite exception occurs during a renewal request for
+     * the lease, renewal requests will continue to be made for that
+     * lease until: the lease is renewed successfully, a renewal
+     * attempt results in a definite exception, or the lease's actual
+     * expiration time has been exceeded. If the lease cannot be
+     * successfully renewed before its actual expiration is reached,
+     * the exception associated with the most recent renewal attempt
+     * will be wrapped in an instance of the
+     * <code>LeaseRenewalEvent</code> class and sent to the listener.
+     * <p>
+     * If the lease's actual expiration is reached before the lease's
+     * desired expiration time, and either 1) the last renewal attempt
+     * succeeded or 2) there have been no renewal attempts, a
+     * <code>LeaseRenewalEvent</code> containing a <code>null</code>
+     * exception will be sent to the listener.
+     * <p>    
+     * @param lease The <code>Lease</code> to be managed
+     * @param desiredExpiration When the client wants the lease to expire
+     *                          in milliseconds since the beginning of the 
+     *                          epoch.
+     * @param renewDuration The renewal duration to associate with the lease in 
+     *                      milliseconds.
+     * @param listener    reference to the <code>LeaseListener</code> object
+     *                    that will receive notifications of any exceptional
+     *                    conditions that occur during renewal attempts. If
+     *                    <code>null</code> no notifications will
+     *                    be sent.
+     * @throws NullPointerException if lease is <code>null</code>
+     * @throws IllegalArgumentException if <code>renewDuration</code> is invalid
+     * <p>
+     * @see LeaseRenewalEvent
+     * @see net.jini.core.lease.LeaseException
+     */
+    public void renewUntil(Lease lease,
+			   long desiredExpiration,
+			   long renewDuration,
+			   LeaseListener listener)
+    {
+	validateDuration(renewDuration, desiredExpiration == Lease.FOREVER,
+			 "desiredExpiration");
+	addLease(lease, desiredExpiration, renewDuration, listener,
+		 System.currentTimeMillis());
+    }
+
+    /**
+     * Include a lease in the managed set for a specified duration.  
+     * <p>
+     * Calling this method is equivalent the following call:
+     * <pre>
+     *     renewFor(lease, desiredDuration, Lease.FOREVER, listener)
+     * </pre>
+     *
+     * @param lease       reference to the new lease to manage
+     * @param desiredDuration
+     *                    The desired duration (relative time) that the 
+     *                    caller wants <code>lease</code> to be valid for in
+     *                    milliseconds.
+     * @param listener    reference to the <code>LeaseListener</code> object
+     *                    that will receive notifications of any exceptional
+     *                    conditions that occur during renewal attempts. If
+     *                    <code>null</code> is input, no notifications will
+     *                    be sent.
+     *
+     * @throws java.lang.NullPointerException this exception occurs when
+     *         the value of the <code>lease</code> parameter is 
+     *         <code>null</code>.
+     *
+     * @see com.sun.jini.compat.lease.LeaseRenewalManager#renewFor 
+     */
+    public void renewFor(Lease lease, long desiredDuration, 
+			 LeaseListener listener) 
+    {
+	renewFor(lease, desiredDuration, Lease.FOREVER, listener);
+    }    
+
+    /**
+     * Include a lease in the managed set for a
+     * specified duration and with specified renewal duration.
+     * <p>
+     * The semantics of this method are similar to those of the
+     * four-argument form of <code>renewUntil</code>, with
+     * <code>desiredDuration</code> + current time being used for the
+     * value of the <code>desiredExpiration</code> parameter of
+     * <code>renewUntil</code>. The only exception to this is that, in
+     * the context of <code>renewFor</code> the value of the
+     * <code>renewDuration</code> parameter may only be
+     * <code>Lease.ANY</code> if the value of the
+     * <code>desiredDuration</code> parameter is <em>exactly</em>
+     * <code>Lease.FOREVER.</code>
+     * <p>
+     * This method tests for arithmetic overflow in the desired
+     * expiration time computed from the value of
+     * <code>desiredDuration</code> parameter
+     * (<code>desiredDuration</code> + current time). Should such
+     * overflow be present, a value of <code>Lease.FOREVER</code> is
+     * used to represent the lease's desired expiration time.  
+     * <p>
+     *
+     * @param lease       reference to the new lease to manage
+     * @param desiredDuration
+     *                    The desired duration (relative time) that the 
+     *                    caller wants <code>lease</code> to be valid for in
+     *                    milliseconds.
+     * @param renewDuration The renewal duration to associate with the lease in 
+     *                      milliseconds.
+     * @param listener    reference to the <code>LeaseListener</code> object
+     *                    that will receive notifications of any exceptional
+     *                    conditions that occur during renewal attempts. If
+     *                    <code>null</code> is input, no notifications will
+     *                    be sent.
+     *
+     * @throws java.lang.NullPointerException this exception occurs when
+     *         the value of the <code>lease</code> parameter is 
+     *         <code>null</code>.
+     * @throws IllegalArgumentException if <code>renewDuration</code> is invalid
+     *
+     * @see com.sun.jini.compat.lease.LeaseRenewalManager#renewUntil 
+     */
+    public void renewFor(Lease lease,
+			 long desiredDuration,
+			 long renewDuration,
+			 LeaseListener listener)
+    {
+	/* Validate before calculating effective desiredExpiration,
+	 * if they want a renewDuration of Lease.ANY, desiredDuration has
+	 * to be exactly Lease.FOREVER
+	 */
+	validateDuration(renewDuration, desiredDuration == Lease.FOREVER,
+			 "desiredDuration");
+
+	long now = System.currentTimeMillis();
+	long desiredExpiration;
+	if (desiredDuration < Lease.FOREVER - now) // check overflow.
+	    desiredExpiration = now + desiredDuration;
+	else
+	    desiredExpiration = Lease.FOREVER;
+	addLease(lease, desiredExpiration, renewDuration, listener, now);
+    }
+
+    /**
+     * Error checking function that ensures renewDuration is valid taking
+     * into account the whether or not the desired expiration/duration is
+     * Lease.FOREVER.  Throws an appropriate IllegalArgumentException if
+     * an invalid renewDuration is passed.
+     * @param renewDuration Renew duration the clients wants
+     * @param isForever     Should be true if client asked for a
+     *                      desired expiration/duration of exactly Lease.FOREVER
+     * @param name          Name of the desired expiration/duration field, used
+     *                      to construct exception
+     * @throws IllegalArgumentException if renewDuration is invalid
+     */
+    private void validateDuration(long renewDuration, boolean isForever, 
+				  String name) 
+    {
+ 	if (renewDuration <= 0 && 
+	    !(renewDuration == Lease.ANY && isForever))
+	{
+	    /* A negative renew duration and is not lease.ANY with a
+	     * forever desired expiration
+	     */
+	    if (renewDuration == Lease.ANY) {
+		/* Must have been Lease.ANY with a non-FOREVER desired
+		 * expiration
+		 */
+		throw new IllegalArgumentException("A renewDuration of " +
+		     "Lease.ANY can only be used with a " + name + " of " +
+		     "Lease.FOREVER");
+	    } 
+
+	    if (isForever) {
+		// Must have been a non-Lease.ANY, non-positive renewDuration
+		throw new IllegalArgumentException("When " + name + " is " +
+		    "Lease.FOREVER the only valid values for renewDuration " +
+		    "are a positive number, Lease.ANY, or Lease.FOREVER");
+	    }
+
+	    /* Must be a non-positive renewDuration with a non-Forever 
+	     * desired expiration
+	     */
+	    throw new IllegalArgumentException("When the " + name +
+		" is not Lease.FOREVER the only valid values for " +
+		"renewDuration are a positive number or Lease.FOREVER");
+	}
+    }
+
+    private synchronized void addLease(Lease lease,
+				       long desiredExpiration,
+				       long renewDuration,
+				       LeaseListener listener,
+				       long now)
+    {	    
+	Entry e = findEntryDo(lease);
+	if (e != null && !removeLeaseInRenew(e))
+	    leases.remove(e);
+	insertEntry(new Entry(lease, desiredExpiration, renewDuration,
+			      listener),
+		    now);
+	calcActualRenews(now);
+    }
+
+    /** calculate the preferred renew time, and put in the map */
+    private void insertEntry(Entry e, long now) {
+	e.calcRenew(now);
+	leases.put(e, e);
+    }
+
+    /**
+     * Returns the current desired expiration time associated with a
+     * particular lease, (not the actual expiration that was granted
+     * when the lease was created or last renewed).
+     *
+     * @param lease  the lease the caller wants the current desired
+     *               expiration for.
+     *
+     * @return a <code>long</code> value corresponding to the current
+     *         desired expiration time associated with <code>lease</code>
+     *
+     * @throws <code>net.jini.core.lease.UnknownLeaseException</code> this
+     *         exception occurs when the lease passed to this method is not in
+     *         the set of managed leases
+     *
+     * @see net.jini.core.lease.UnknownLeaseException
+     * @throws UnknownLeaseException if the lease passed to this method 
+     *         is not in the set of managed leases
+     *         
+     * @see #setExpiration 
+     */
+    public synchronized long getExpiration(Lease lease)
+	throws UnknownLeaseException
+    {
+	return findEntry(lease).expiration;
+    }
+
+    /**
+     * Replaces the current desired expiration of a given lease from the
+     * managed set with a new desired expiration time.
+     * <p>
+     * Note that an invocation of this method with a lease that is currently
+     * a member of the managed set is equivalent to an invocation of the
+     * <code>renewUntil</code> method with the lease's current listener
+     * input to that method's <code>listener</code> parameter. Specifically,
+     * if the value of the <code>expiration</code> parameter is less than
+     * or equal to the lease's current desired expiration, this method takes
+     * no action.
+     *
+     * @param lease       the lease whose desired expiration time should
+     *                    be replaced
+     *
+     * @param expiration  <code>long</code> value representing the new
+     *                    desired expiration time for the
+     *                    <code>lease</code> parameter
+     *
+     * @throws UnknownLeaseException if the lease passed to this method 
+     *         is not in the set of managed leases
+     *
+     * @see com.sun.jini.compat.lease.LeaseRenewalManager#renewUntil
+     * @see net.jini.core.lease.UnknownLeaseException
+     * @see #getExpiration 
+     */
+    public synchronized void setExpiration(Lease lease, long expiration)
+	throws UnknownLeaseException
+    {
+	Entry e = findEntry(lease);
+	e.expiration = expiration;
+	if (expiration != Lease.FOREVER && e.renewDuration == Lease.ANY)
+	    e.renewDuration = Lease.FOREVER;
+	if (leaseInRenew.indexOf(e) < 0) {
+	    leases.remove(e);
+	    long now = System.currentTimeMillis();
+	    insertEntry(e, now);
+	    calcActualRenews(now);
+	}
+    }
+
+    /**
+     * Removes a given lease from the managed set, and cancels it.
+     * <p>
+     * Note that even if an exception is thrown as a result of the cancel
+     * operation, the lease will still have been removed from the set of
+     * leases managed by this class. Additionally, any exception thrown by
+     * the <code>cancel</code> method of the lease object itself may also
+     * be thrown by this method.
+     *
+     * @param lease the lease to remove and cancel
+     *
+     * @throws UnknownLeaseException if the lease passed to this method 
+     *         is not in the set of managed leases
+     *
+     * @throws java.rmi.RemoteException typically, this exception occurs when
+     *         there is a communication failure between the client and the
+     *         server. When this exception does occur, the lease may or may
+     *         not have been successfully cancelled, (but the lease is
+     *         guaranteed to have been removed from the managed set).
+     *
+     * @see net.jini.core.lease.Lease#cancel
+     * @see net.jini.core.lease.UnknownLeaseException
+     */
+    public void cancel(Lease lease)
+	throws UnknownLeaseException, RemoteException
+    {
+	remove(lease);
+	lease.cancel();
+    }
+
+    /**
+     * Removes a given lease from the managed set of leases; but does not
+     * cancel the given lease.
+     *
+     * @param lease the lease to remove from the managed set
+     *
+     * @throws UnknownLeaseException if the lease passed to this method 
+     *         is not in the set of managed leases
+     *
+     * @see net.jini.core.lease.UnknownLeaseException
+     */
+    public synchronized void remove(Lease lease) throws UnknownLeaseException {
+	Entry e = findEntry(lease);
+	if (!removeLeaseInRenew(e))
+	    leases.remove(e);
+	calcActualRenews();
+    }
+
+    /**
+     * Removes all leases from the managed set of leases. This method does
+     * not request the cancellation of the removed leases.
+     */
+    public synchronized void clear() {
+	leases.clear();
+	leaseInRenew.clear();
+	calcActualRenews();
+    }
+
+    /** calculate the actual renew times, and poke/restart the queuer */
+    private void calcActualRenews() {
+	calcActualRenews(System.currentTimeMillis());
+    }
+
+    /** calculate the actual renew times, and poke/restart the queuer */
+    private void calcActualRenews(long now) {
+	for (Iterator iter = leases.values().iterator(); iter.hasNext(); ) {
+	    Entry e = (Entry)iter.next();
+
+	    // Start by assuming we can renew the lease when we want
+	    e.actualRenew = e.renew;
+
+	    if (e.renewalsDone()) {
+		/**
+		 * The lease's actual expiration is >= desired
+		 * expiration, drop the lease if the desired
+		 * expiration has been reached and we don't have to
+		 * tell anyone about it
+		 */
+		if (now >= e.expiration && 
+		    e.desiredExpirationListener() == null) 
+		{
+		    iter.remove();
+		}
+
+		/*
+		 * Even if we have to send an event we assume that
+		 * it won't consume a slot in our schedule
+		 */
+		continue; 
+	    }
+
+	    if (e.endTime <= now && e.listener == null) {
+		// lease has expired and no listener, just remove it.
+		iter.remove();
+		continue;
+	    }
+
+	    /*
+	     * Calculate when we have to renew this lease based
+	     * on scheduling constraints, make actualRenew sooner if
+	     * we have to
+	     */
+	    if (!canBatch(e)) {
+		for (Iterator listIter = calcList.iterator();
+		     listIter.hasNext(); )
+		{
+		    if (e.renew >=
+			((Entry)listIter.next()).actualRenew - RENEW_RTT)
+			break;
+		    listIter.remove();
+		}
+		if (calcList.size() == MAX_RENEW_THREAD) {
+		    Entry e1 = (Entry)calcList.remove(0);
+		    e.actualRenew = e1.actualRenew - RENEW_RTT;
+		}
+		calcList.add(e);
+	    }
+	}
+	calcList.clear();
+	long newWakeup = wakeupTime();
+	if (queuer == null) {
+	    if (newWakeup < Long.MAX_VALUE) {
+		queuer = new QueuerTask(newWakeup);
+		tasker.add(queuer);
+	    }
+	} else if (newWakeup < queuer.wakeup ||
+		   (newWakeup == Long.MAX_VALUE && leaseInRenew.isEmpty()))
+	{
+	    notifyAll();
+	}
+    }
+
+    /**
+     * Return true if e can be batched with another entry that expires
+     * between e.renew - RENEW_BATCH_TIME_GAP and e.renew.
+     */
+    private boolean canBatch(Entry e) {
+	Iterator iter = leases.tailMap(e).values().iterator();
+	iter.next(); // skip e itself
+	while (iter.hasNext()) {
+	    Entry be = (Entry)iter.next();
+	    if (e.renew - be.renew > RENEW_BATCH_TIME_GAP)
+		break;
+	    if (e.canBatch(be))
+		return true;
+	}
+	return false;
+    }
+
+    /** find a lease entry, throw exception if not found or expired normally */
+    private Entry findEntry(Lease lease) throws UnknownLeaseException {
+	Entry e = findEntryDo(lease);
+	if (e != null &&
+	    (e.renew < e.endTime || System.currentTimeMillis() < e.endTime))
+	    return e;
+	throw new UnknownLeaseException();
+    }
+
+    /** find a lease entry, or null */
+    private Entry findEntryDo(Lease lease) {
+	Entry e = findLeaseFromIterator(leases.values().iterator(), lease);
+	if (e == null)
+	    e = findLeaseFromIterator(leaseInRenew.iterator(), lease);
+	return e;
+    }
+
+    /** find a lease entry, or null */
+    private static Entry findLeaseFromIterator(Iterator iter, Lease lease) {
+	while (iter.hasNext()) {
+	    Entry e = (Entry)iter.next();
+	    if (e.lease.equals(lease))
+		return e;
+	}
+	return null;
+    }
+
+    /** notify the listener for each lease */
+    private void tell(List bad) {
+	for (Iterator iter = bad.iterator(); iter.hasNext(); ) {
+	    Entry e = (Entry)iter.next();
+	    if (e.renewalsDone()) {
+		final DesiredExpirationListener del = 
+		    e.desiredExpirationListener();
+		if (del != null) 
+		    del.expirationReached(new LeaseRenewalEvent(this, e.lease,
+		        e.expiration, null));
+
+		continue; 
+	    }
+
+	    e.listener.notify(new LeaseRenewalEvent(this, e.lease,
+						    e.expiration, e.ex));
+	}
+    }
+
+    /** renew all of the leases (if multiple, all can be batched) */
+    private void renewAll(List bList, long now) {
+        Map lmeMap = null;
+	Throwable t = null;
+	List bad = null;
+
+	try {
+	    if (bList.size() == 1) {
+		Entry e = (Entry)bList.get(0);
+		e.lease.renew(e.getRenewDuration(now));
+	    } else {
+		LeaseMap batchLeaseMap = createBatchLeaseMap(bList, now);
+		batchLeaseMap.renewAll();
+	    }
+	} catch (LeaseMapException ex) {
+	    lmeMap = ex.exceptionMap;
+	    bad = new ArrayList(lmeMap.size());
+	} catch (Throwable ex) {
+	    t = ex;
+	    bad = new ArrayList(bList.size());  // They may all be bad
+	}
+
+	/* For each lease we tried to renew determine the associated
+	 * exception (if any), and then ether add the lease back
+	 * to leases (if the renewal was successful), schedule a retry
+	 * and add back to leases (if the renewal was indefinite), or
+	 * drop the lease (by not adding it back to leases) and 
+	 * notify any interested listeners.  In any event remove 
+	 * lease from the list of leases being renewed.
+	 */
+
+	now = System.currentTimeMillis();
+	synchronized (this) {
+	    for (Iterator iter = bList.iterator(); iter.hasNext(); ) {
+		Entry e = (Entry)iter.next();
+
+		if (!removeLeaseInRenew(e))
+		    continue;
+
+		// Update the entries exception field 
+		if (bad == null) {
+		    e.ex = null;
+		} else {
+		    e.ex = (t != null)?t:(Throwable)lmeMap.get(e.lease);
+		}
+
+		if (e.ex == null) {
+		    // No problems just put back in list
+		    insertEntry(e, now);
+		    continue;
+		} 
+
+		/* Some sort of problem.  If definite don't put back
+		 * into leases and setup to notify the appropriate
+		 * listener, if indefinite schedule for a retry and put
+		 * back into leases
+		 */
+		final int cat = ThrowableConstants.retryable(e.ex);
+		if (cat == ThrowableConstants.INDEFINITE) {
+		    e.delayRenew();
+		    leases.put(e, e);		    
+		} else if (e.listener != null) { 
+		    // Note: For us ThrowableConstants.UNCATEGORIZED == definite
+		    bad.add(e);
+		}		
+	    }
+	    calcActualRenews(now);
+	}
+
+	if (bad != null)
+	    tell(bad);	
+    }
+
+    /** create a LeaseMap for batch renewal */
+    private static LeaseMap createBatchLeaseMap(List bList, long now) {
+	Iterator iter = bList.iterator();
+	Entry e = (Entry)iter.next();
+	LeaseMap batchLeaseMap =
+	    e.lease.createLeaseMap(e.getRenewDuration(now));
+	while (iter.hasNext()) {
+	    e = (Entry)iter.next();
+	    batchLeaseMap.put(e.lease, new Long(e.getRenewDuration(now)));
+	}
+	return batchLeaseMap;
+    }
+
+    /** remove from leaseInRenew, return true if removed */
+    private boolean removeLeaseInRenew(Entry e) {
+	int index = leaseInRenew.indexOf(e); // avoid iterator cons
+	if (index < 0)
+	    return false;
+	leaseInRenew.remove(index);
+	return true;
+    }
+
+    /** return the soonest actual renewal time */
+    private long wakeupTime() {
+	if (leases.isEmpty())
+	    return Long.MAX_VALUE;
+	return ((Entry)leases.lastKey()).actualRenew;
+    }
+
+    private class QueuerTask implements TaskManager.Task {
+
+	/** when to next wake up and queue a new renew task */
+	long wakeup;
+
+	QueuerTask(long wakeup) {
+	    this.wakeup = wakeup;
+	}
+
+	/** No ordering */
+	public boolean runAfter(List tasks, int size) {
+	    return false;
+	}
+
+        public void run() {
+	    synchronized (LeaseRenewalManager.this) {
+		try {
+		    while (true) {
+			wakeup = wakeupTime();
+			if (wakeup == Long.MAX_VALUE && leaseInRenew.isEmpty())
+			    break;
+			final long now = System.currentTimeMillis();
+			long delta = wakeup - now;
+			if (delta <= 0)
+			    tasker.add(new RenewTask(now));
+			else
+			    LeaseRenewalManager.this.wait(delta);
+		    }
+		} catch (InterruptedException ex) {
+		}
+		queuer = null;
+	    }
+	}
+    }
+}

Propchange: river/tck/src/com/sun/jini/compat/lease/LeaseRenewalManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: river/tck/src/com/sun/jini/compat/reggie/ClassMapper.java
URL: http://svn.apache.org/viewvc/river/tck/src/com/sun/jini/compat/reggie/ClassMapper.java?rev=1234278&view=auto
==============================================================================
--- river/tck/src/com/sun/jini/compat/reggie/ClassMapper.java (added)
+++ river/tck/src/com/sun/jini/compat/reggie/ClassMapper.java Sat Jan 21 07:28:27 2012
@@ -0,0 +1,241 @@
+
+/*
+ * 
+ * Copyright 2005 Sun Microsystems, Inc.
+ * 
+ * Licensed 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 com.sun.jini.compat.reggie;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.WeakHashMap;
+import java.lang.ref.SoftReference;
+
+/**
+ * Maps Class to ServiceType/Base, Class to EntryClass/Base, and Class to
+ * Field[], with caching for efficiency.
+ *
+ * @author Sun Microsystems, Inc.
+ *
+ */
+class ClassMapper {
+
+    /** Weak Map from Class to SoftReference(ServiceTypeBase) */
+    private static final WeakHashMap serviceMap = new WeakHashMap(23);
+    /** Weak Map from Class to SoftReference(EntryClassBase) */
+    private static final WeakHashMap entryMap = new WeakHashMap(17);
+    /** Weak Map from Class to SoftReference(sorted Field[]) */
+    private static final WeakHashMap fieldMap = new WeakHashMap(17);
+    /** Comparator for sorting fields */
+    private static final FieldComparator comparator = new FieldComparator();
+    private static final ServiceType[] empty = {};
+    private static final Class[] noArg = new Class[0];
+
+    private ClassMapper() {}
+
+    /** Returns a ServiceTypeBase descriptor for a class. */
+    public static ServiceTypeBase toServiceTypeBase(Class cls) {
+	synchronized (serviceMap) {
+	    return toServiceTypeBase(cls, true);
+	}
+    }
+
+    /**
+     * Returns a ServiceTypeBase descriptor for a class.  If needCodebase
+     * is false, the returned descriptor's codebase may be null.
+     */
+    private static ServiceTypeBase toServiceTypeBase(Class cls,
+						     boolean needCodebase)
+    {
+	if (cls == null)
+	    return null;
+	SoftReference cref = (SoftReference)serviceMap.get(cls);
+	ServiceTypeBase stype = null;
+	if (cref != null)
+	    stype = (ServiceTypeBase)cref.get();
+	if (stype == null) {
+	    stype = new ServiceTypeBase(
+			   new ServiceType(cls,
+					   toServiceType(cls.getSuperclass()),
+					   toServiceType(cls.getInterfaces())),
+			   null);
+	    serviceMap.put(cls, new SoftReference(stype));
+	}
+	if (needCodebase && stype.codebase == null)
+	    stype.setCodebase(cls);
+	return stype;
+    }
+
+    /** Returns a ServiceType descriptor for a class. */
+    private static ServiceType toServiceType(Class cls) {
+	if (cls != null)
+	    return toServiceTypeBase(cls, false).type;
+	return null;
+    }
+
+    /** Converts an array of Class to an array of ServiceType. */
+    public static ServiceType[] toServiceType(Class[] classes) {
+	if (classes == null)
+	    return null;
+	if (classes.length == 0)
+	    return empty;
+	ServiceType[] stypes = new ServiceType[classes.length];
+	synchronized (serviceMap) {
+	    for (int i = classes.length; --i >= 0; ) {
+		stypes[i] = toServiceType(classes[i]);
+	    }
+	}
+	return stypes;
+    }
+
+    /** Returns a EntryClassBase descriptor for a class. */
+    public static EntryClassBase toEntryClassBase(Class cls) {
+	synchronized (entryMap) {
+	    return toEntryClassBase(cls, true);
+	}
+    }
+
+    /**
+     * Returns a EntryClassBase descriptor for a class.  If base is false,
+     * the returned descriptor's codebase may be null, and the class need
+     * not be public and need not have a no-arg constructor.
+     */
+    private static EntryClassBase toEntryClassBase(Class cls, boolean base) {
+	if (cls == null)
+	    return null;
+	SoftReference cref = (SoftReference)entryMap.get(cls);
+	EntryClassBase eclass = null;
+	if (cref != null)
+	    eclass = (EntryClassBase)cref.get();
+	if (eclass == null) {
+	    if (base) {
+		if (!Modifier.isPublic(cls.getModifiers()))
+		    throw new IllegalArgumentException("entry class " +
+						       cls.getName() +
+						       " is not public");
+		try {
+		    cls.getConstructor(noArg);
+		} catch (NoSuchMethodException e) {
+		    throw new IllegalArgumentException("entry class " +
+						       cls.getName() +
+			        " does not have a public no-arg constructor");
+		}
+	    }
+	    eclass = new EntryClassBase(
+			     new EntryClass(cls,
+					    toEntryClass(cls.getSuperclass())),
+			     null);
+	    entryMap.put(cls, new SoftReference(eclass));
+	}
+	if (base && eclass.codebase == null)
+	    eclass.setCodebase(cls);
+	return eclass;
+    }
+
+    /** Returns an EntryClass descriptor for a class. */
+    private static EntryClass toEntryClass(Class cls) {
+	if (cls != null)
+	    return toEntryClassBase(cls, false).eclass;
+	return null;
+    }
+
+    /** Field of an Entry class, with marshalling information */
+    static class EntryField {
+	/** Field for the field */
+	public final Field field;
+	/**
+	 * True if instances of the field need to be converted
+	 * to MarshalledObject.  False if the type of the field
+	 * is String, Integer, Boolean, Character, Long, Float,
+	 * Double, Byte, or Short.
+	 */
+	public final boolean marshal;
+
+	/**
+	 * Basic constructor.
+	 */
+	public EntryField(Field field) {
+	    this.field = field;
+	    Class c = field.getType();
+	    marshal = !(c == String.class ||
+			c == Integer.class ||
+			c == Boolean.class ||
+			c == Character.class ||
+			c == Long.class ||
+			c == Float.class ||
+			c == Double.class ||
+			c == Byte.class ||
+			c == Short.class);
+	}
+    }
+
+    /**
+     * Returns public fields, in super to subclass order, sorted
+     * alphabetically within a given class.
+     */
+    public static EntryField[] getFields(Class cls) {
+	synchronized (fieldMap) {
+	    SoftReference cref = (SoftReference)fieldMap.get(cls);
+	    EntryField[] efields = null;
+	    if (cref != null)
+		efields = (EntryField[])cref.get();
+	    if (efields == null) {
+		Field[] fields = cls.getFields();
+		Arrays.sort(fields, comparator);
+		int len = 0;
+		for (int i = 0; i < fields.length; i++) {
+		    if ((fields[i].getModifiers() &
+			 (Modifier.STATIC|Modifier.FINAL|Modifier.TRANSIENT))
+			== 0)
+		    {
+			if (fields[i].getType().isPrimitive())
+			    throw new IllegalArgumentException("entry class " +
+							       cls.getName() +
+						  " has a primitive field");
+			fields[len++] = fields[i];
+		    }
+		}
+		efields = new EntryField[len];
+		while (--len >= 0) {
+		    efields[len] = new EntryField(fields[len]);
+		}
+		fieldMap.put(cls, new SoftReference(efields));
+	    }
+	    return efields;
+	}
+    }
+
+    /** Comparator for sorting fields. */
+    private static class FieldComparator implements Comparator {
+	public FieldComparator() {}
+
+	/** Super before subclass, alphabetical within a given class */
+	public int compare(Object o1, Object o2) {
+	    Field f1 = (Field)o1;
+	    Field f2 = (Field)o2;
+	    if (f1 == f2)
+		return 0;
+	    if (f1.getDeclaringClass() == f2.getDeclaringClass())
+		return f1.getName().compareTo(f2.getName());
+	    if (f1.getDeclaringClass().isAssignableFrom(
+						     f2.getDeclaringClass()))
+		return -1;
+	    return 1;
+	}
+    }
+}

Propchange: river/tck/src/com/sun/jini/compat/reggie/ClassMapper.java
------------------------------------------------------------------------------
    svn:eol-style = native