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