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 [23/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/start/StartUtil.java
URL: http://svn.apache.org/viewvc/river/tck/src/com/sun/jini/compat/start/StartUtil.java?rev=1234278&view=auto
==============================================================================
--- river/tck/src/com/sun/jini/compat/start/StartUtil.java (added)
+++ river/tck/src/com/sun/jini/compat/start/StartUtil.java Sat Jan 21 07:28:27 2012
@@ -0,0 +1,1260 @@
+/*
+ * 
+ * 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.start;
+
+import com.sun.jini.debug.Debug;
+import com.sun.jini.compat.start.ParsedArgs;
+
+import net.jini.discovery.DiscoveryGroupManagement;
+
+import net.jini.core.discovery.LookupLocator;
+
+import java.io.File;
+import java.io.PrintWriter;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import java.security.AccessController;
+
+import java.text.MessageFormat;
+
+import java.util.ArrayList;
+import java.util.MissingResourceException;
+import java.util.Properties;
+import java.util.ResourceBundle;
+import java.util.StringTokenizer;
+
+/** 
+ * This class provides utilities that can be useful when starting clients
+ * or services. In particular, this class can be useful when starting a
+ * version of a service that is activatable and persistent (referred to
+ * as an <i>activatable</i> version), an activatable version that is able to 
+ * share an activation group with other activatable services (referred to
+ * as a <i>shared-activatable</i> version), or a version of the service that 
+ * is non-activatable and non-persistent (referred to as a <i>transient</i>
+ * version). The particular version of the service that is started as a result
+ * of employing this facility depends on the values and format of the arguments
+ * entered on the command line and contained in the <code>args</code>
+ * parameter. A particular syntax and format for the command line arguments
+ * is required when starting a service using this facility.
+ * <p>
+ * To start an activatable service:
+ * <p>
+ * If the service being started is an activatable service, then not only
+ * are the first three arguments required, but they must be input in a
+ * pre-defined order. The remaining arguments are optional and can occur
+ * in any order. Although the number of arguments that follow the first
+ * three required arguments has no limit, they fall into one of four
+ * categories: the "groups" argument, the "locators" argument, the
+ * "java program" argument, and the "properties & options" argument. The
+ * optional arguments can be input to the command line in one of two
+ * formats. The first format imposes no order on the optional arguments.
+ * The second format is allowed so that scripts written to start services
+ * that follow the command line syntax from previous releases, remain
+ * compatible. This second format of the command line imposes a particular
+ * order and format on the optional arguments; whereas the new format is
+ * less restrictive.
+ * <P>
+ * The required format of the command line is as follows:
+ * <p>
+ * <pre>
+ *   The new, less restrictive format -
+ * <p>
+ *   codebase policyfile logdir [ [group0] [group1] [group2] ... ]
+ *                              [ [locator0] [locator1] [locator2] ... ]
+ *                              [javaprogram] 
+ *                              [ [property0] [option0] [property1] ... ]
+ * <p>
+ *    where the first three arguments are required and must appear in the
+ *    order shown above, and the remaining arguments are optional and can
+ *    appear in any order
+ * </pre>
+ * <p>
+ * <pre>
+ *   The original format -
+ * <p>
+ *   codebase policyfile logdir
+ *                         [group0,locator0,locator1,group1,locator2 ... ]
+ *                         [javaprogram] 
+ *                         [ [property0] [option0] [property1] ... ]
+ * <p>
+ *    where the first three arguments are required and the remaining
+ *    arguments are optional, but must appear in the order shown above.
+ *    Note that although the fourth argument above contains both groups
+ *    and locators, it is actually a single argument; with the groups
+ *    and locators delimited by commas (with no spaces). Note also that
+ *    in earlier releases, locators were not recognized. 
+ * </pre>
+ * <p>
+ * For each of the arguments, both required and optional, the following
+ * formats are imposed:
+ * <p>
+ * <pre>
+ *   codebase:    '"http://&lt;hostname&gt;:&lt;port&gt;/jarFile.jar"'
+ * <p>              or
+ *                '"http://&lt;hostname&gt;:&lt;port&gt;/&lt;directoryPath&gt;/"'
+ * <p>
+ *   policyfile:  '&lt;directoryPath&gt;/&lt;policyFilename&gt;'
+ * <p>
+ *   logdir:      '&lt;directoryPath&gt;/&lt;logFilename&gt;'
+ * <p>
+ *   group:       'any String not in the jini protocol format'
+ * <p>
+ *   locator:     'String in the jini protocol format
+ * <p>             'jini://&lt;hostname&gt;:[&lt;port&gt;]' (the port is optional)
+ * <p>
+ *   javaprogram: '&lt;directoryPath&gt;/&lt;nameOfFileToExecute&gt;'
+ * <p>
+ *   property:    '-Dmy.property0=propVal0 -Dmy.property1=propVal1 ...'
+ * <p>
+ *   option:      '-classpath &lt;classpath value&gt; ...'
+ * </pre>
+ * <p>
+ * An example command line might be something like the following (note
+ * that this example is broken into separate lines and annotated
+ * for readability):
+ * <p>
+ * <pre>
+ * java -jar myService.jar  // the service to start using jar execution
+ * <p>          "http://myHost:8081/myService-dl.jar"  // codebase
+ * <p>          /home/myDir/myService.policy           // policy
+ * <p>          /tmp/myServiceLog0                     // log dir
+ * <p>          com.my.service.group,jini://yourHost   // group & locator
+ * <p>          /export/progs/jdk1.3/java              // java prog to run
+ * <p>          -classpath /files/jdk1.2/lib           // option0
+ * <p>          -Dcom.sun.jini.discovery.debug=true    // property0
+ * <p>          -Dcom.sun.jini.reggie.proxy.debug=true // property1
+ * <p>          -Djava.compiler=                       // property2
+ * <p>  
+ * </pre>   
+ * To start a shared-activatable service:
+ * <p>
+ * If the service being started is a shared-activatable service, then the 
+ * first three and fifth arguments are required, but they must be input in a
+ * pre-defined order. The fourth argument is optional. The arguments that 
+ * follow the first three required arguments fall into one of three
+ * categories: the "groups" argument, the "locators" argument, 
+ * and the "sharedvm" argument. 
+ * <P>
+ * <pre>
+ *   The format -
+ * <p>
+ *   codebase policyfile logdir
+ *                         [group0,locator0,locator1,group1,locator2 ... ]
+ *                         sharedvm_log
+ * <p>
+ *    where the first three and the fifth arguments are required and the 
+ *    remaining arguments are optional, but must appear in the order shown 
+ *    above.
+ *    Note that although the fourth argument above contains both groups
+ *    and locators, it is actually a single argument; with the groups
+ *    and locators delimited by commas (with no spaces). Note also that
+ *    in earlier releases, locators were not recognized. 
+ * </pre>
+ * <p>
+ * The formats for each of the arguments, both required and optional, are
+ * described above (in the "activatable service" description), except for 
+ * the <code>sharedvm_log</code> argument. The <code>sharedvm_log</code>
+ * argument has the following format:
+ * <p>
+ * <pre>
+ *   sharedvm_log:      '&lt;directoryPath&gt;'
+ * </pre>
+ * where &lt;directoryPath&gt; refers to an existing directory path 
+ * (previously) created by a shared group creation process.  
+ * <p>
+ * To start a transient service:
+ * <p>
+ * If the service being started is a transient service, then the command
+ * line requires only the "log directory" argument; and accepts only the 
+ * optional "groups" and "locators" arguments.
+ * <p>
+ * If the codebase, the policy file, and/or any options are desired to
+ * be set for the VM of a transient service started by this facility,
+ * those items should be set through arguments input directly to the
+ * java command used to start the service, (for the case of the transient
+ * service, this class and the service share the same VM). For example,
+ * <p>
+ * <pre>
+ * /export/progs/jdk1.3/java 
+ * <p>     -cp /jars/jini-core.jar:/jars/jini-ext.jar:/jars/myService.jar
+ * <p>     -Dcom.sun.jini.discovery.debug=true
+ * <p>     -Dcom.sun.jini.reggie.proxy.debug=true
+ * <p>     -Djava.compiler=
+ * <p>     -Djava.rmi.server.codebase="http:myHost:8081/myService-dl.jar"
+ * <p>     -Djava.security.policy=/home/myDir/myService.policy
+ * <p>     my.service.package.myService             // class file to execute
+ * <p>     /tmp/myServiceLog0                       // log dir argument
+ * <p>     group0,jini://host0,group1,jini://host1  // groups & locators
+ * <p>          
+ * </pre>
+ *
+ * @author Sun Microsystems, Inc.
+ *
+ * @see  com.sun.jini.compat.start.ParsedArgs
+ */
+public class StartUtil {
+    /**
+     * Utility for outputting debug messages for this class.
+     */
+    private static final Debug debug = 
+	(Debug)AccessController.doPrivileged(
+	    Debug.getDebugAction("com.sun.jini.compat.start.startutil.debug"));
+
+    /**
+     * Print writer for shared group creation related debug messages.
+     */
+    private static final PrintWriter dbgParse = debug.getWriter("parse");
+
+    public static class ParsedListResult {
+        public int      nArgsParsed;  //total number of args that were examined
+        public Object[] extractedArgs;//groups or locs extracted from the list
+        public ParsedListResult(int nArgsParsed, Object[] extractedArgs) {
+            this.nArgsParsed   = nArgsParsed;
+            this.extractedArgs = extractedArgs;
+        }
+    }//end class ParsedListResult
+
+    /** Fully-qualified package name that, together with a resource item
+     *  name, identifies the resource bundle from which to retrieve 
+     *  resource information (which can be localized)
+     */
+    private static final String resourcePackage
+                                           = "com.sun.jini.compat.start.resources";
+    /** name of particular item in resource bundle that will be printed */
+    private String resourceName;
+    /** The set of resources used to enable localization */
+    private ResourceBundle resources;
+    /** The default set of groups to discover and join */
+    private String[] defaultGroups = new String[]{""}; //public group
+
+    /**
+     * Constructs a new instance of this utility class, initializing the
+     * information about the resource bundle that is used to display usage
+     * and error information.
+     * 
+     * @param ResourceName    the name used to identify the particular item
+     *                        in the resource bundle that will be printed
+     */
+    public StartUtil(String resourceName) {
+        this.resourceName = resourceName;
+	try {
+	    resources = ResourceBundle.getBundle
+                                           (resourcePackage+"."+resourceName);
+        } catch (MissingResourceException e) {
+            e.printStackTrace();
+        }
+    }//end constructor
+
+    /**
+     * Parses the elements contained in the <code>args</code> parameter, 
+     * which should contain the arguments input on the command line
+     * when requesting that a program be started. This method assumes a
+     * particular syntax and format for that command line (see this class'
+     * description for a complete description of the expected syntax).
+     * <p>
+     *
+     * @param args <code>String</code> array containing the arguments input
+     *        to the command line minus any initial "required" arguments
+     *        (all required parameters are assumed to have already been
+     *        parsed before this method is invoked).
+     *
+     * @return instance of the class <code>ParsedArgs</code>
+     *         containing the values of the arguments that were parsed.
+     *         Will return <code>null</code> if the arguments cannot be
+     *         parsed for any reason (for example, the argument list format
+     *         is invalid).
+     *
+     * @see ParsedArgs
+     */
+    public ParsedArgs parseArgs(String[] args) {
+        return parseArgs(args,0);
+    }//end parseArgs
+
+    /**
+     * Beginning with element at the index corresponding to the value of the
+     * <code>startIndx</code> parameter, parses the elements contained in
+     * the <code>args</code> parameter, which should contain the arguments
+     * input on the command line when requesting that a program be started.
+     * This method assumes a particular syntax and format for that command
+     * line (see this class' description for a complete description of the
+     * expected syntax).
+     * <p>
+     *
+     * @param args      <code>String</code> array containing the arguments
+     *                  input to the command line.
+     * @param startIndx <code>int</code> value indicating the index of the
+     *                  element of the <code>args</code> parameter where
+     *                  parsing should begin.
+     *
+     * @return instance of the class <code>ParsedArgs</code>
+     *         containing the values of the arguments that were parsed.
+     *         Will return <code>null</code> if the arguments cannot be
+     *         parsed for any reason (for example, the argument list format
+     *         is invalid).
+     *
+     * @see ParsedArgs
+     */
+    public ParsedArgs parseArgs(String[] args, int startIndx) {
+        if (args == null) 
+	    return null;
+        boolean argsValid = true;
+        /* Parse the remaining arguments, looking for groups, locators, etc. */
+        String[] groups          = defaultGroups;
+        LookupLocator[] locators = new LookupLocator[]{};
+        String javaProgram       = null;
+	String sharedvm_log      = null;
+        Properties properties    = null;
+        String[] options         = null;
+        ArrayList groupList      = new ArrayList();
+        ArrayList locatorList    = new ArrayList();
+        ArrayList propertyList   = new ArrayList();
+        ArrayList optionList     = new ArrayList();
+
+        for(int i=startIndx;i<args.length; ) {
+            if (dbgParse != null) { 
+               dbgParse.println("StartUtil::parseArgs: parsing arg["
+                   + i + "]: " + args[i]);  
+            }  
+            if(isClasspath(args[i])) {
+                if(args.length <= (i+1)) {
+                    print("classpath.invalid", null);
+                    argsValid = false;
+                    break;
+                }
+	        if (sharedvm_log != null) {
+		    print("java_opts.incompatible", args[i]);
+		    argsValid = false;
+		    break;
+		}
+                optionList.add(args[i]);
+                optionList.add(args[i+1]);
+                i = i+2; //step over -classpath and arg just set
+                continue;
+            }//endif(-classpath arg)
+            if(isOption(args[i])) {
+	        if (sharedvm_log != null) {
+		    print("java_opts.incompatible", args[i]);
+		    argsValid = false;
+		    break;
+		}
+                optionList.add(args[i]);
+                i = i+1; //step over arg just set
+                continue;
+            }//endif(-Xdebug -verbose)
+            if(isProperty(args[i])) {
+	        if (sharedvm_log != null) {
+		    print("java_props.incompatible", args[i]);
+		    argsValid = false;
+		    break;
+		}
+                propertyList.add(args[i]);
+                i = i+1; //step over arg just set
+                continue;
+            }//endif(-DpropKey0.propKey1.propKey2=propertyValue)
+	    if(isSharedGroupLog(args[i])) {
+	        if (javaProgram != null || 
+                    propertyList.size() > 0 ||
+                    optionList.size() > 0) {
+		    print("sharedvm_log.incompatible", args[i]);
+		    argsValid = false;
+		    break;
+		}
+		sharedvm_log = args[i];
+		i = i+1;
+		continue;
+	    }//endif(/dir0/dir1/dir2/<cookie>)
+	    if(isProgram(args[i])) {
+	        if (sharedvm_log != null) {
+		    print("java_prog.incompatible", args[i]);
+		    argsValid = false;
+		    break;
+		}
+                javaProgram = args[i];
+                i = i+1; //step over arg just set
+                continue;
+            }//endif(/dir0/dir1/dir2/javaProgramToExecute)
+            LookupLocator loc = getLocator(args[i]); // for a singleton locator
+            if(loc != null) {
+                locatorList.add(loc);
+                i = i+1; //step over arg just set
+                continue;
+            }//endif(jini://host:[port])
+            if(isCommaSeparatedList(args[i])) {
+                String[] grps = getGroupsFromCommaSeparatedList(args[i]);
+                /* If groups has already been set to ALL_GROUPS because of
+                 * some other argument, that value takes precedence
+                 */
+                if(groups != DiscoveryGroupManagement.ALL_GROUPS) {
+                    /* If grps is NO_GROUPS, that takes precedence over all
+                     * other groups, except ALL_GROUPS
+                     */
+                    if( (grps == DiscoveryGroupManagement.ALL_GROUPS)) {
+                        groups = DiscoveryGroupManagement.ALL_GROUPS;
+                    } else if( (grps == DiscoveryGroupManagement.NO_GROUPS)) {
+                        groups = DiscoveryGroupManagement.NO_GROUPS;
+                    } else { //add the groups found to the current ArrayList
+                        for(int j=0;j<grps.length;j++) {
+                            groupList.add(grps[j]);
+                        }
+                    }
+                }//endif(groups != ALL_GROUPS)
+                LookupLocator[] locs
+                                = getLocatorsFromCommaSeparatedList(args[i]);
+                for(int j=0;j<locs.length;j++) {
+                    locatorList.add(locs[j]);
+                }
+                i = i+1; //step over the single arg just processed
+                continue;
+            }//endif(g0,jini://host0,g1,jini://host1:port,g2)
+            if( (args[i].equals("-groups")) || (args[i].equals("-group")) ) {
+                if((i+1) == args.length) {
+                    print("groups.invalid", null);
+                    argsValid = false;
+                    break;
+                }
+                int srcOffset = i+1;
+                int count = args.length - srcOffset;
+                String[] dst = new String[count];
+                System.arraycopy(args, srcOffset, dst, 0, count);
+                ParsedListResult grpsResult = parseGroupList(dst);
+                for(int j=0;j<(grpsResult.extractedArgs).length;j++) {
+                    groupList.add(grpsResult.extractedArgs[j]);
+                }
+                i = i+grpsResult.nArgsParsed+1; //step over the arg SET
+                continue;
+            }//endif(-groups g0 g1 g2)
+            if(    (args[i].equals("-locators")) || (args[i].equals("-locs"))
+                || (args[i].equals("-locator"))  || (args[i].equals("-loc")) )
+            {
+                if((i+1) == args.length) {
+                    print("locators.invalid", null);
+                    argsValid = false;
+                    break;
+                }
+                int srcOffset = i+1;
+                int count = args.length - srcOffset;
+                String[] dst = new String[count];
+                System.arraycopy(args, srcOffset, dst, 0, count);
+                ParsedListResult locsResult = parseLocatorList(dst);
+                for(int j=0;j<(locsResult.extractedArgs).length;j++) {
+                    locatorList.add(locsResult.extractedArgs[j]);
+                }
+                i = i+locsResult.nArgsParsed+1; //step over the arg SET
+                continue;
+            }//endif(-locators host0 host1 host2)
+
+            /* If the current argument satisfies none of the above
+             * characteristics, assume it must be a group name
+             */
+            if(groups != DiscoveryGroupManagement.ALL_GROUPS) {
+                /* If args[i] is NO_GROUPS, that takes precedence over all
+                 * other groups, except ALL_GROUPS
+                 */
+                if(args[i].equals(getString("groups.all"))) {
+                    groups = DiscoveryGroupManagement.ALL_GROUPS;
+                } else if(args[i].equals(getString("groups.none"))) {
+                    groups = DiscoveryGroupManagement.NO_GROUPS;
+                } else if(args[i].equals(getString("groups.public"))) {
+                    groupList.add("");
+                } else { //add the group found to the current ArrayList
+                    groupList.add(args[i]);
+                }
+                i = i+1; //step over the single arg just processed
+                continue;
+            }//endif(groups != ALL_GROUPS)
+	    print("parse.unprocessed_args", args[i]);
+            argsValid = false; // don't ignore 
+            break;
+            // i = i+1; //step over arg just set
+        }//end loop(i)
+
+        if(!argsValid) return null;
+
+        /* Prepare the groups for return */
+        if(!groupList.isEmpty()) { //arg list had at least 1 group
+            groups = (String[])(groupList.toArray
+                                           (new String[groupList.size()]));
+        }
+        /* Prepare the locators for return */
+        if(!locatorList.isEmpty()) { //arg list had at least 1 locator
+            locators = (LookupLocator[])(locatorList.toArray
+                                      (new LookupLocator[locatorList.size()]));
+            /* If arg list had only locators, then re-set groups = NO_GROUPS */
+            if(    groupList.isEmpty()
+                && groups != DiscoveryGroupManagement.ALL_GROUPS ) {
+                groups = DiscoveryGroupManagement.NO_GROUPS;
+            }
+        }
+        /* Prepare the properties for return */
+        if(!propertyList.isEmpty()) {
+            properties = new Properties();
+            for(int i=0;i<propertyList.size();i++) {
+                String prop = (String)(propertyList.get(i));
+                int k = prop.indexOf("=");
+                if (k < 0) {
+                    properties.put(prop.substring(2), "");
+                } else {
+                    properties.put(prop.substring(2, k),  // property key
+                                   prop.substring(k + 1));//property value
+                }
+            }
+        }
+        /* Prepare the options for return */
+        if(!optionList.isEmpty()) {
+            options = (String[])(optionList.toArray
+                                           (new String[optionList.size()]));
+        }
+
+        ParsedArgs procArgs = 
+            new ParsedArgs(null,null,null,
+                           groups,
+                           locators,
+                           javaProgram,
+                           properties,
+                           options,
+			   sharedvm_log);
+
+        if (dbgParse != null) { 
+               dbgParse.println("StartUtil::parseArgs: " 
+                  + "returning processed args ...");
+               printParsedArgs(procArgs);
+        }  
+        return procArgs;
+    }//end parseArgs
+
+    /**
+     * Parses the elements contained in the <code>args</code> parameter, 
+     * which should contain the arguments input on the command line
+     * when requesting that a program be started. This method assumes a
+     * particular syntax and format for that command line: that of an
+     * activatable service.
+     * <p>
+     *
+     * @param args <code>String</code> array containing the arguments input
+     *        to the command line
+     *
+     * @return instance of the class <code>ParsedArgs</code>
+     *         containing the values of the arguments that were parsed.
+     *         Will return <code>null</code> if the arguments cannot be
+     *         parsed for any reason (for example, the argument list format
+     *         is invalid).
+     *
+     * @see ParsedArgs
+     */
+    public ParsedArgs parseActivatableArgs(String[] args) {
+        if( (args == null) || (args.length < 3) ) return null;
+	String codebase   = args[0];
+	String policyFile = getPolicyFile(args[1]);
+	String logDir     = getLogDirectory(args[2]);
+        if(    (!isValidCodebaseUrl(codebase))
+            || (policyFile == null) || (logDir == null) ) return null;
+        ParsedArgs parsedArgs = parseArgs(args,3);
+        if (parsedArgs == null) 
+            return null;
+        return new ParsedArgs(codebase,
+                              policyFile,
+                              logDir,
+                              parsedArgs.getGroups(),
+                              parsedArgs.getLocators(),
+                              parsedArgs.getJavaProgram(),
+                              parsedArgs.getProperties(),
+                              parsedArgs.getOptions(),
+                              parsedArgs.getSharedVMLog());
+    }//end parseActivatableArgs
+    
+    /**
+     * Parses the elements contained in the <code>args</code> parameter, 
+     * which should contain the arguments input on the command line
+     * when requesting that a program be started. This method assumes a
+     * particular syntax and format for that command line: that of a
+     * transient service.
+     * <p>
+     *
+     * @param args <code>String</code> array containing the arguments input
+     *        to the command line
+     *
+     * @return instance of the class <code>ParsedArgs</code>
+     *         containing the values of the arguments that were parsed.
+     *         Will return <code>null</code> if the arguments cannot be
+     *         parsed for any reason (for example, the argument list format
+     *         is invalid).
+     *
+     * @see ParsedArgs
+     */
+    public ParsedArgs parseTransientArgs(String[] args) {
+        if( (args == null) || (args.length < 1) ) return null;
+        /* Verify logDir does not already exist and get the absolute path */
+	String logDir = getLogDirectory(args[0]);
+        if(logDir == null) return null;
+        ParsedArgs parsedArgs = parseArgs(args,1);
+        if (parsedArgs == null) 
+            return null;
+        return new ParsedArgs(null,  //codebase,
+                              null,  //policyFile,
+                              logDir,
+                              parsedArgs.getGroups(),
+                              parsedArgs.getLocators(),
+                              parsedArgs.getJavaProgram(),
+                              parsedArgs.getProperties(),
+                              parsedArgs.getOptions());
+    }//end parseTransientArgs
+
+    /** Prints a general usage message extracted from the resource bundle */
+    public void printUsage() {
+        print("usage", null);
+        print("groups.usage", null);
+        print("locators.usage", null);
+        print("javaprog.usage", null);
+        print("properties.usage", null);
+        print("options.usage", null);
+        print("sharedvm.usage", null);
+    }//end printUsage
+
+    /** Prints a usage message, specific to a transient service, extracted 
+     *  from the resource bundle
+     */
+    public void printTransientUsage() {
+        print("transient.usage", null);
+        print("groups.usage", null);
+        print("locators.usage", null);
+    }//end printTransientUsage
+
+    /** Prints a usage message, specific to a transient, non-persistent
+     *  service, extracted from the resource bundle
+     */
+    public void printNonPersistentUsage() {
+        print("nonpersistent.usage", null);
+        print("groups.usage", null);
+        print("locators.usage", null);
+    }//end printTransientUsage
+
+    /**
+     * Displays the resource String corresponding to a particular key, but 
+     * with a given value substituted in the appropriate location in the
+     * String.
+     *
+     * @param resourceKey a non-null case-sensitive String containing the
+     *                    suffix identifier that, together with the
+     *                    resourceName, make up the resource whose String
+     *                    is to be printed.
+     * <p>
+     *                    For example, if a resource stored in the resource
+     *                    bundle is identified by a key such as
+     *                    "service.activation.notUp", then the component
+     *                    of the key referred to as the resourceName of
+     *                    the resource is the prefix substring "service".
+     *                    Similarly, the component referred to as the
+     *                    resourceKey of the resource is the suffix substring
+     *                    that appears immediately after the period which
+     *                    follows the resourceName; "activation.notUp" in
+     *                    this case.
+     * <p>
+     *                    Thus, if it is desired that the string associated
+     *                    with a particular resource be printed by this
+     *                    method, then only the resourceKey component, not
+     *                    the whole key itself, need be input to this method;
+     *                    "activation.notUp" in the example above. The
+     *                    resourceName (which is initialized when this
+     *                    class is constructed) will be pre-pended so that
+     *                    the correct key is used when retrieving the string
+     *                    value from the resource bundle.
+     * @param val         the value to substitute in the resource String
+     *                    before displaying 
+     */
+    public void print(String resourceKey, String val) {
+	String fmt = getString(resourceKey);
+        String key = resourceName+"."+resourceKey;
+	if (fmt == null)
+	    fmt = "no text found: \"" + key + "\" {0}";
+	System.out.println(MessageFormat.format(fmt, new String[]{val}));
+    }//end print
+
+    /**
+     * Displays the contents of each field of the input parameter. Useful
+     * for debugging.
+     *
+     * @param parsedArgs data structure containing values of arguments input
+     *                   to the command line and parsed prior to invoking
+     *                   this method
+     */
+    public static void printParsedArgs(ParsedArgs parsedArgs) {
+        System.out.println("codebase    = "+parsedArgs.getCodebase());
+        System.out.println("policyFile  = "+parsedArgs.getPolicyFile());
+        System.out.println("logDir      = "+parsedArgs.getLogDir());
+        System.out.println("SharedVMLog = "+parsedArgs.getSharedVMLog());
+
+        String[] groups = parsedArgs.getGroups();
+        if(groups == DiscoveryGroupManagement.ALL_GROUPS) {
+            System.out.println("groups      = ALL_GROUPS");
+        }else if(groups.length == 0){
+            System.out.println("groups      = NO_GROUPS");
+        }else{
+            for(int i=0;i<groups.length;i++) {
+                if(groups[i].equals("")) {
+                    System.out.println("  groups["+i+"] = The PUBLIC Group");
+                } else {
+                    System.out.println("  groups["+i+"] = "+groups[i]);
+                }
+            }//end loop
+        }//endif(groups)
+
+        LookupLocator[] locators = parsedArgs.getLocators();
+        if(locators == null) {
+            System.out.println("locators    = null");
+        }else if(locators.length == 0){
+            System.out.println("locators    = NO_LOCATORS");
+        }else{
+            for(int i=0;i<locators.length;i++) {
+                System.out.println("  locators["+i+"] = "+locators[i]);
+            }//end loop
+        }//endif(locators)
+
+        System.out.println("javaProgram = "+parsedArgs.getJavaProgram());
+        Properties properties = parsedArgs.getProperties();
+        if(properties == null) {
+            System.out.println("properties  = null");
+        }else {
+            properties.list(System.out);
+        }//endif
+
+        String[] options = parsedArgs.getOptions();
+        if(options == null) {
+            System.out.println("options  = null");
+        }else {
+            for(int k=0;k<options.length;k++) {
+                System.out.println("  options["+k+"] = "+options[k]);
+            }//end loop
+        }//endif
+    }//end printParsedArgs
+
+    /**
+     * Retrieves a String resource corresponding to a particular key from the
+     * resource bundle. If the resource bundle does not yet exist, this
+     * method will first load and instantiate the resource bundle.
+     *
+     * @param resourceKey a non-null case-sensitive String containing the
+     *                    suffix identifier that, together with the
+     *                    resourceName, make up the resource whose String
+     *                    is to be retrieved.
+     * <p>
+     *                    For example, if a resource stored in the resource
+     *                    bundle is identified by a key such as
+     *                    "service.activation.notUp", then the component
+     *                    of the key referred to as the resourceName of
+     *                    the resource is the prefix substring "service".
+     *                    Similarly, the component referred to as the
+     *                    resourceKey of the resource is the suffix substring
+     *                    that appears immediately after the period which
+     *                    follows the resourceName; "activation.notUp" in
+     *                    this case.
+     * <p>
+     *                    Thus, if it is desired that the string associated
+     *                    with a particular resource be retrieved by this
+     *                    method, then only the resourceKey component, not
+     *                    the whole key itself, need be input to this method;
+     *                    "activation.notUp" in the example above. The
+     *                    resourceName (which is initialized when this
+     *                    class is constructed) will be pre-pended so that
+     *                    the correct key is used when retrieving the string
+     *                    value from the resource bundle.
+     *
+     * @return a non-null String containing the value of the desired resource
+     */
+    private String getString(String resourceKey) {
+        String key = resourceName+"."+resourceKey;
+	try {
+	    return resources.getString(key);
+	} catch (MissingResourceException e) {
+	    return null;
+	}
+    }//end getString
+
+    /**
+     * Retrieves a formatted String resource corresponding to a particular
+     * key from the resource bundle. If the resource bundle does not yet
+     * exist, this method will first load and instantiate the resource bundle.
+     *
+     * @param resourceKey a non-null case-sensitive String containing the
+     *                    suffix identifier that, together with the
+     *                    resourceName, make up the resource whose String
+     *                    is to be retrieved.
+     * <p>
+     *                    For example, if a resource stored in the resource
+     *                    bundle is identified by a key such as
+     *                    "service.activation.notUp", then the component
+     *                    of the key referred to as the resourceName of
+     *                    the resource is the prefix substring "service".
+     *                    Similarly, the component referred to as the
+     *                    resourceKey of the resource is the suffix substring
+     *                    that appears immediately after the period which
+     *                    follows the resourceName; "activation.notUp" in
+     *                    this case.
+     * <p>
+     *                    Thus, if it is desired that the string associated
+     *                    with a particular resource be retrieved by this
+     *                    method, then only the resourceKey component, not
+     *                    the whole key itself, need be input to this method;
+     *                    "activation.notUp" in the example above. The
+     *                    resourceName (which is initialized when this
+     *                    class is constructed) will be pre-pended so that
+     *                    the correct key is used when retrieving the string
+     *                    value from the resource bundle.
+     * @param val         the value to substitute in the resource String
+     *                    when formatting
+     *
+     * @return a non-null String containing the formatted value of the
+     *         desired resource
+     */
+    public String getString(String resourceKey, String val) {
+	String fmt = getString(resourceKey);
+        String key = resourceName+"."+resourceKey;
+	if (fmt == null)
+	    fmt = "no text found: \"" + key + "\" {0}";
+	return (MessageFormat.format(fmt, new String[]{val}));
+    }//end getString
+
+    /** Determines if the input String is a valid URL */
+    private boolean isValidCodebaseUrl(String str) {
+        return isValidCodebaseUrl(str,true);
+    }//end isValidCodebaseUrl
+
+    /** Determines if the input String is a valid URL.
+     *  <p>
+     *  If <code>true</code> is input to the <code>notify</code> parameter,
+     *  then this method will print a diagnostic message; otherwise this
+     *  method simply tests if the input String is a valid URL, and returns
+     *  the appropriate boolean value.
+     */
+    private boolean isValidCodebaseUrl(String str, boolean notify) {
+        boolean codebaseValid = true;
+        StringTokenizer st = new StringTokenizer(str," \"");
+        for (int i = 0; st.hasMoreTokens(); i++) {
+            String url = st.nextToken();
+            if (!url.endsWith("/") &&
+                !url.endsWith(".jar") && !url.endsWith(".zip"))
+            {
+                if(!notify) return false;
+                print("codebase.slash", url);
+                codebaseValid = false;
+	    }
+            try {
+                new URL(url);
+            } catch (MalformedURLException e) {
+                if(!notify) return false;
+                print("codebase.syntax", url);
+                codebaseValid = false;
+            }
+        }
+        return codebaseValid;
+    }//end isValidCodebaseUrl
+
+    /** Determines if the input String corresponds to an existing file */
+    private boolean isValidPolicyFile(String filename) {
+        return isValidPolicyFile(filename,true);
+    }//end isValidPolicyFile
+
+    /** Determines if the input String corresponds to an existing file.
+     *  <p>
+     *  If <code>true</code> is input to the <code>notify</code> parameter,
+     *  then this method will print a diagnostic message; otherwise this
+     *  method simply tests if the input String corresponds to an existing
+     *  file, and returns the appropriate boolean value.
+     */
+    private boolean isValidPolicyFile(String filename, boolean notify) {
+        File pf = new File(filename);
+        if (!pf.exists()) {
+            if(!notify) return false;
+            print("policy.exist", filename);
+            return false;
+        }
+        return true;
+    }//end isValidPolicyFile
+
+    /** Determines if the input String corresponds to an existing file and
+     *  returns the absolute path to the file.
+     */
+    private String getPolicyFile(String filename) {
+        return getPolicyFile(filename,true);
+    }//end getPolicyFile
+
+    /** Determines if the input String corresponds to an existing file and
+     *  returns the absolute path to the file.
+     *  <p>
+     *  If <code>true</code> is input to the <code>notify</code> parameter,
+     *  then this method will print a diagnostic message; otherwise this
+     *  method simply tests if the input String corresponds to an existing
+     *  file, and returns the absolute path to the file.
+     */
+    private String getPolicyFile(String filename, boolean notify) {
+        if(!isValidPolicyFile(filename,notify)) return null;
+        String absDirPath = new String(filename);
+        File df = new File(absDirPath);
+        if (!df.isAbsolute()) {
+            absDirPath = df.getAbsolutePath();
+            print("policy.abs", absDirPath);
+        }
+        return absDirPath;
+    }//end getPolicyFile
+
+    /** 
+     * Determines if the input String is a valid shared group log directory.
+     * A valid log directory is one that contains a well known cookie file,
+     * defined by <code>GroupConstants.GROUP_COOKIE_FILE</code>, that is also
+     * readable.
+     */
+    private boolean isSharedGroupLog(String dirPath) {
+	if(!canTestForFile(dirPath)) 
+	    return false;
+        File cookie = new File(dirPath, GroupConstants.GROUP_COOKIE_FILE);
+	return (cookie.exists() && cookie.isFile());
+    }//end isSharedGroupLog
+
+    /** Determines if the input String can be used as a log directory.
+     *  <p>
+     *  If <code>true</code> is input to the <code>notify</code> parameter,
+     *  then this method will print a diagnostic message; otherwise this
+     *  method simply tests if the input String can be used as a log
+     *  directory, and returns the appropriate boolean value.
+     */
+    private boolean isValidLogDirectory(String dirPath, boolean notify) {
+        File df = new File(dirPath);
+        if (df.exists()) { //if it already exists, can't use it
+            if(!notify) return false;
+            print("logdir.exist", dirPath);
+            return false;
+        } else if(    (isValidCodebaseUrl(dirPath,false)) 
+                   || (isValidPolicyFile(dirPath,false))
+                   || (isClasspath(dirPath))
+                   || (isOption(dirPath))
+                   || (isProperty(dirPath))
+                   || (isProgram(dirPath))
+                   || (isCommaSeparatedList(dirPath)) )
+        {
+            if(!notify) return false;
+            print("logdir.invalid", dirPath);
+            return false;
+        }//endif
+        return true;
+    }//end isValidLogDirectory
+
+    /** Determines if the input String can be used as a log directory,
+     *  and returns the absolute path of the directory.
+     */
+    private String getLogDirectory(String dirPath) {
+        return getLogDirectory(dirPath,true);
+    }//end getLogDirectory
+
+    /** Determines if the input String can be used as a log directory,
+     *  and returns the absolute path of the directory.
+     *  <p>
+     *  If <code>true</code> is input to the <code>notify</code> parameter,
+     *  then this method will print a diagnostic message; otherwise this
+     *  method simply tests if the input String can be used as a log
+     *  directory, and returns the absolute path of the directory.
+     */
+    private String getLogDirectory(String dirPath, boolean notify) {
+        if(!isValidLogDirectory(dirPath,notify)) return null;
+        String absDirPath = new String(dirPath);
+        File df = new File(absDirPath);
+        if (!df.isAbsolute()) {
+            absDirPath = df.getAbsolutePath();
+            if(notify)
+                print("logdir.abs", absDirPath);
+        }
+        return absDirPath;
+    }//end getLogDirectory
+
+    /** Determines if the input String represents a classpath identifier */
+    private static boolean isClasspath(String arg) {
+        if( (arg.equals("-classpath")) || (arg.equals("-cp")) ) return true;
+        return false;
+    }//end isClasspath
+
+    /** Determines if the input String represents a valid java option */
+    private static boolean isOption(String arg) {
+        /* This method considers the input an option if it begins with
+         * sub-strings such as: "-C-D", "-J-D", "-verbose", "-X", etc.;
+         * that is, anything that begins with a "-" that is not "-classpath",
+         * "-cp", or "-D" and does not start with -group(s) or -loc(ators)
+         */
+        if(     (arg.startsWith("-"))
+            && !(    (arg.equals("-classpath"))   || (arg.equals("-cp"))
+                  || (arg.startsWith("-groups"))  || (arg.startsWith("-group"))
+                  || (arg.startsWith("-locators")) || (arg.startsWith("-locs"))
+                  || (arg.startsWith("-locator"))  || (arg.startsWith("-loc"))
+                  || (arg.startsWith("-D"))
+                )
+          )
+        {
+            return true;
+        }
+        return false;
+    }//end isOption
+
+    /** Determines if the input String represents a valid java property */
+    private static boolean isProperty(String arg) {
+        return arg.startsWith("-D");
+    }//end isProperty
+
+    /** Determines if the input String can be used as a program. */
+    private static boolean isProgram(String arg) {
+        /* If the argument doesn't begin with '-', isn't a group name
+         * (doesn't contain a file separator), isn't a locator (is not a
+         * valid Jini(TM) URL), exists as a file -- not a directory, and
+         * the absolute path of the file is given (that is, the argument
+         * begins with a file separator such as '/' or 'drive:\') then
+         * this method assumes the given argument is a program to be executed.
+         */
+        if( !canTestForFile(arg) ) return false;
+        File fd = new File(arg);
+        if( (fd.exists()) && !(fd.isDirectory()) && (fd.isAbsolute()) ) {
+            return true;
+        }
+        return false;
+    }//end isProgram
+
+    /** Instantiates a LookupLocator using the input String */
+    private static LookupLocator getLocator(String str) {
+        if(isCommaSeparatedList(str)) return null; //commas in URL not allowed
+        try {
+            return new LookupLocator(str);
+        } catch(MalformedURLException e) {
+            return null;
+        }
+    }//end getLocator
+
+    /** Instantiates a LookupLocator using the input hostname and the
+     *  default port number
+     */
+    private static LookupLocator hostToLocator(String hostname) {
+        try {
+            return new LookupLocator("jini://"+hostname+":4160/");
+        } catch(MalformedURLException e) {
+            return null;
+        }
+    }//end hostToLocator
+
+    /** Determines if the input String is delimited by commas */
+    private static boolean isCommaSeparatedList(String arg) {
+       /* If arg contains 'comma(s)', return true */
+        if(arg.indexOf(",") >= 0) return true;
+        return false;
+    }//end isCommaSeparatedList
+
+    /** Determines if the input <code>String</code> is "valid"; that is,
+     *  determines if the input <code>String</code> is not a property,
+     *  a comma separated list,
+     *  or a <code>LookupLocator</code> so that when input to the various
+     *  <code>File</code> methods, a <code>SecurityException</code> will
+     *  not occur.
+     *
+     * This method interprets the input argument as being valid only if
+     * the input argument is:
+     * <p><ul>
+     *      <li> not a property or option (does not begin with a '-')
+     *      <li> not a comma-separated list of groups and/or locators
+     *      <li> not a <code>LookupLocator</code> (is not a valid Jini URL)
+     *      <li> will not result in a <code>SecurityException</code> when
+     *           tested for file existence
+     * </ul><p>
+     * This is important
+     * because the call to <code>File.exists</code> will result in a
+     * <code>SecurityException</code> if read permission is not granted for
+     * the input argument (ex. a <code>LookupLocator</code> <code>URL</code>). 
+     * In order to avoid having to
+     * grant wide read permissions, methods which need to determine if a
+     * <code>String</code> is a valid file (ex. <code>isProgram</code> or 
+     * <code>isValidPolicy</code>) can invoke this method and decide not
+     * to continue processing the given <code>String</code> that is found
+     * to be a locator.
+     * 
+     * @return <code>true</code> if the argument doesn't begin with '-',
+     *         isn't a comma-separated list of groups and/or locators,
+     *         isn't a
+     *         locator (is not a valid Jini URL), and does not result in
+     *         a <code>SecurityException</code> when tested for file
+     *         existence; <code>false</code> otherwise.
+     */
+    private static boolean canTestForFile(String arg) {
+        if(arg.startsWith("-")) return false; //is a property or option
+        if(isCommaSeparatedList(arg)) return false; //is a comma-separated list
+        if(getLocator(arg) != null) return false; //is a valid Jini URL
+        try {
+            (new File(arg)).exists();//determine if a SecurityException occurs
+            return true;
+        } catch(SecurityException e) {
+            e.printStackTrace();
+            return false;
+        }
+    }//end canTestForFile
+
+    /** Parses a comma-separated String, extracting the sub-strings that
+     *  represent group names (sub-strings NOT in the jini URL format)
+     */
+    private String[] getGroupsFromCommaSeparatedList(String list) {
+        /* If the list contains "none" or "all", those override all other
+         * groups. If the list contains both "none" and "all", "all"
+         * takes precedence over "none". Additionally, if the group name
+         * "public" is input, it will be returned as the 'un-named public
+         * group' ("").
+         */
+        if(list.startsWith("-loc")) return DiscoveryGroupManagement.NO_GROUPS;
+        String lst = list;
+        if(list.startsWith("-group")) {//strip off -group,-groups
+            int offset = list.indexOf(",");
+            lst = list.substring(offset+1);
+        }
+        String publicGroup   = getString("groups.public");
+        String noGroups      = getString("groups.none");
+        String allGroups     = getString("groups.all");
+        boolean groupsNone   = false;
+        boolean containsLocs = false;
+        StringTokenizer st = new StringTokenizer(lst, " \t\n\r\f,");
+        int n = st.countTokens();
+        if (n > 0) {
+            ArrayList grpList = new ArrayList();
+            for (int i=0; st.hasMoreTokens(); i++) {
+                String group = st.nextToken();
+                if (getLocator(group) != null) {//skip locator urls
+                    containsLocs = true;
+                    continue;
+                } else if (group.equals(publicGroup)) {
+                    group = "";
+                } else if(group.equals(noGroups)) { //overrides other groups
+                    groupsNone = true;
+                } else if(group.equals(allGroups)) {//overrides everything
+                    return DiscoveryGroupManagement.ALL_GROUPS;
+                }
+                grpList.add(group);
+            }//end loop
+            /* If 'none' or only locators were input, then return NO_GROUPS */
+            if( groupsNone || (containsLocs && (grpList.size() == 0)) ) {
+                return DiscoveryGroupManagement.NO_GROUPS;
+            }
+            return ((String[])(grpList.toArray(new String[grpList.size()])));
+        }//end if
+        return defaultGroups;
+    }//end getGroupsFromCommaSeparatedList
+
+    /** Parses a comma-separated String, extracting the sub-strings that
+     *  are given in the jini URL format (jini://hostname:[port])
+     */
+    private static LookupLocator[] getLocatorsFromCommaSeparatedList
+                                                                 (String list)
+    {
+        if(list.startsWith("-group")) return new LookupLocator[0];
+        String lst = list;
+        boolean assumeAllAreLocs = false;
+        if(list.startsWith("-loc")) {//strip off -loc,-locs,-locator,-locators
+            int offset = list.indexOf(",");
+            lst = list.substring(offset+1);
+            assumeAllAreLocs = true;
+        }
+        StringTokenizer st = new StringTokenizer(lst, " \t\n\r\f,");
+        int n = st.countTokens();
+        if (n > 0) {
+            ArrayList locList = new ArrayList();
+            for (int i=0; st.hasMoreTokens(); i++) {
+                String locStr = st.nextToken();
+                LookupLocator loc = getLocator(locStr);
+                if(loc != null) {
+                    locList.add(loc);
+                } else {
+                    if(assumeAllAreLocs) {
+                        loc = hostToLocator(locStr);
+                        if(loc != null) {
+                            locList.add(loc);
+                        }
+                    }
+                }
+            }//end loop
+            return ((LookupLocator[])(locList.toArray
+                                        (new LookupLocator[locList.size()])));
+        }//end if
+        return new LookupLocator[0];
+    }//end getLocatorsFromCommaSeparatedList
+
+    /** Examines a String array, extracting the elements that that
+     *  represent group names (elements NOT in the jini URL format)
+     */
+    private static ParsedListResult parseGroupList(String[] subArgs) {
+        ArrayList grpList = new ArrayList();
+        int nParsed = 0;
+        for(int i=0;i<subArgs.length;i++) {
+            if(subArgs[i].startsWith("-"))      break;
+            if(isClasspath(subArgs[i]))         break;
+            if(isOption(subArgs[i]))            break;
+            if(isProperty(subArgs[i]))          break;
+            if(isProgram(subArgs[i]))           break;
+            LookupLocator loc = getLocator(subArgs[i]);
+            if(loc == null) grpList.add(subArgs[i]);
+            nParsed = nParsed+1;
+        }
+        String[] groups = (String[])(grpList.toArray
+                                               (new String[grpList.size()]));
+        return new ParsedListResult(nParsed,groups);
+    }//end parseGroupList
+
+    /** Examines a String array, extracting the elements that are given in
+     *  the jini URL format (jini://hostname:[port])
+     */
+    private static ParsedListResult parseLocatorList(String[] subArgs) {
+        ArrayList locList = new ArrayList();
+        int nParsed = 0;
+        for(int i=0;i<subArgs.length;i++) {
+            if(subArgs[i].startsWith("-")) break;
+            if(isClasspath(subArgs[i]))    break;
+            if(isOption(subArgs[i]))       break;
+            if(isProperty(subArgs[i]))     break;
+            if(isProgram(subArgs[i]))      break;
+            LookupLocator loc = getLocator(subArgs[i]);
+            if(loc != null) {
+                locList.add(loc);
+            } else {
+                loc = hostToLocator(subArgs[i]);
+                if(loc != null) {
+                    locList.add(loc);
+                }
+            }
+            nParsed = nParsed+1;
+        }
+        LookupLocator[] locators = (LookupLocator[])(locList.toArray
+                                        (new LookupLocator[locList.size()]));
+        return new ParsedListResult(nParsed,locators);
+    }//end parseLocatorList
+
+    /** Determines if the given String is a valid 'http', 'ftp', or 'file' 
+     *  URL 
+     */
+    private static boolean isUrl(String str) {
+        try {
+            URL url = new URL(str);
+            String protocol = url.getProtocol();
+            if( protocol.equals("http") ) return true;
+            if( protocol.equals("ftp") )  return true;
+            if( protocol.equals("file") ) return true;
+            return false;
+        } catch(MalformedURLException e) {
+            return false;
+        }
+    }//end isUrl
+
+}//end class StartUtil

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

Added: river/tck/src/com/sun/jini/compat/test/AnnouncementResponseTest.java
URL: http://svn.apache.org/viewvc/river/tck/src/com/sun/jini/compat/test/AnnouncementResponseTest.java?rev=1234278&view=auto
==============================================================================
--- river/tck/src/com/sun/jini/compat/test/AnnouncementResponseTest.java (added)
+++ river/tck/src/com/sun/jini/compat/test/AnnouncementResponseTest.java Sat Jan 21 07:28:27 2012
@@ -0,0 +1,297 @@
+/*
+ * 
+ * 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.test;
+
+import java.rmi.RemoteException;
+
+import java.net.InetAddress;
+import java.net.MulticastSocket;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.DatagramPacket;
+import java.net.UnknownHostException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+
+import com.sun.jini.compat.harness.Status;
+import com.sun.jini.compat.harness.DefaultTest;
+import com.sun.jini.compat.harness.BasicAdmin;
+import com.sun.jini.compat.harness.BasicServiceAdmin;
+import com.sun.jini.compat.harness.Config;
+import com.sun.jini.compat.harness.TestUtility;
+
+import net.jini.discovery.Constants;
+
+import net.jini.core.lookup.ServiceID;
+
+/**
+ * Tests whether a service responds to an announcement from a 
+ * lookup service that it should respond to by connecting to 
+ * the specified socket and sending a unicast request.
+ */
+public class AnnouncementResponseTest extends DefaultTest {
+
+    private BasicAdmin admin;
+    SocketThread st;
+    Socket sock;
+
+    /**
+     * Sets up a socket-listening thread that listens on the port 
+     * specified in the announcement packet. The test then sends 
+     * out properly formatted announcement packets in
+     * the appropriate multicast group and in the public lookup 
+     * service group every second up to the failureTime. If 
+     * the service connects to the socket and sends a unicast
+     * request packet, the test decodes the packet and checks 
+     * its formatting. If the service doesn't connect to the socket 
+     * within the failureTime, or sends an improperly formatted 
+     * unicast request packet, the service fails.
+     *
+     * @param args Command line arguments passed through
+     * @return The status of this run (failed or passed
+     *         and a comment)
+     */
+    public Status run(String[] args) {
+	Config conf = getConfig();
+	admin = conf.getAdmin();
+
+	try {
+	    TestUtility.log(Config.ALL,"Test: starting program");
+	    admin.start();
+	} catch (RemoteException re) {
+            TestUtility.log(Config.ERROR,"Test: caught exception",re);
+            return Status.failed(Status.ENV,"Problem starting program");
+        }
+
+	try {
+	    st = new SocketThread(conf.getFailureTime());
+	} catch (IOException ioe) {
+	    TestUtility.log(Config.ERROR,"Test: caught exception",ioe);
+	    return Status.failed(Status.ENV,"Could not construct a "
+		+ "ServerSocket");
+	}
+
+	MulticastSocket ms = null;
+	try {
+	    TestUtility.log(Config.ALL,"Test: opening multicast "
+		+ "announcement socket");
+	    ms = new MulticastSocket(Constants.discoveryPort);
+	    ms.joinGroup(Constants.getAnnouncementAddress());
+	    ms.setSoTimeout(conf.getFailureTime());
+	} catch (IOException ioe) {
+	    TestUtility.log(Config.ERROR,"Test: caught exception",ioe);
+	    return Status.failed(Status.ENV,"Could not construct or "
+		+ "configure a MulticastSocket");
+	}
+
+	DatagramPacket sendPacket = null;
+	try {
+	    TestUtility.log(Config.ALL,"Test: creating lookup announcement "
+		+ "containing host and port: " 
+		+ InetAddress.getLocalHost().getHostAddress() + ":"
+		+ st.getServerSocketPort());
+	    ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+	    DataOutputStream dataOut = new DataOutputStream(byteOut);
+	    ServiceID sid = new ServiceID(42l, 42l);
+	    dataOut.writeInt(1);
+	    dataOut.writeUTF(InetAddress.getLocalHost().getHostAddress());
+	    dataOut.writeInt(st.getServerSocketPort());
+	    sid.writeBytes(dataOut);
+	    dataOut.writeInt(1);
+	    dataOut.writeUTF("");
+
+	    byte[] buf = byteOut.toByteArray();
+	    sendPacket = new DatagramPacket(buf, buf.length,
+		Constants.getAnnouncementAddress(), 
+		Constants.discoveryPort);
+	} catch (UnknownHostException uhe) {
+	    TestUtility.log(Config.ERROR,"Test: caught exception",uhe);
+	    return Status.failed(Status.ENV,"Could not get local host "
+		+ "address");
+	} catch (IOException ioe) {
+	    TestUtility.log(Config.ERROR,"Test: caught exception",ioe);
+	    return Status.failed(Status.ENV,"Could not construct "
+		+ "outgoing multicast announcement");
+	}
+
+	st.start();
+	long startTime = System.currentTimeMillis();
+	long duration = 0;
+	try {
+	    synchronized(st) {
+		while ((sock == null) && (duration < conf.getFailureTime())) {
+	    	    TestUtility.log(Config.ALL,"Test: sending announcement");
+		    ms.send(sendPacket);
+		    st.wait(1000);
+	    	    TestUtility.log(Config.ALL,"Test: waiting for program "
+			+ "to connect");
+		    sock = st.getSocket();
+		    duration = System.currentTimeMillis() - startTime;
+		}
+	    }
+	    ms.leaveGroup(Constants.getAnnouncementAddress());
+	} catch (IOException ioe) {
+	    TestUtility.log(Config.ERROR,"Test: caught exception",ioe);
+	    return Status.failed(Status.ENV,"Could not send "
+		+ "multicast announcement");
+	} catch (InterruptedException ie) {
+	    TestUtility.log(Config.ERROR,"Test: caught exception",ie);
+	    return Status.failed(Status.ENV,"Test interrupted");
+	}
+
+	if (sock == null) {
+	    return Status.failed(Status.TEST, "Product did not "
+		+ "respond to multicast announcement in "
+		+ duration + " milliseconds"); 
+	}
+
+	try {
+	    DataInputStream dis = new DataInputStream(sock.getInputStream());
+	    TestUtility.log(Config.ALL,"Test: checking unicast request packet");
+	    int protoVers = dis.readInt();
+	    if (protoVers != 1) {
+		return Status.failed(Status.TEST, "Unicast request packet "
+		   + "contains the wrong protocol number: " + protoVers);
+	    }
+	} catch (EOFException eofe) {
+	    TestUtility.log(Config.ERROR,"Test: caught exception",eofe);
+	    return Status.failed(Status.TEST, "Unicast request packet "
+		+ "ended unexpectedly");
+	} catch (IOException ioe) {
+	    TestUtility.log(Config.ERROR,"Test: caught exception",ioe);
+	    return Status.failed(Status.TEST, "Unicast request packet "
+		+ "improperly formatted");
+	}    
+
+	return Status.passed("OK");
+    }
+
+    public void tearDown() {
+        super.tearDown();
+
+	if (st != null) {
+	    st.interrupt();
+	}
+
+	try {
+	    if (sock != null) {
+		sock.close();
+	    }
+	} catch (IOException ignore) {}
+
+        try {
+            if (admin != null) {
+	    	TestUtility.log(Config.ALL,"Test: stopping program");
+                admin.stop();
+            }
+        } catch (RemoteException ignore) {}
+    }
+
+    private class SocketThread extends Thread {
+	private ServerSocket serv;
+	private Socket sock = null;
+        private boolean interrupted = false;
+
+	SocketThread(int socketTimeout) throws IOException {
+	    super();
+	    serv = new ServerSocket(0);
+	    serv.setSoTimeout(socketTimeout);
+	}
+
+        /**
+         * Overrides Thread.interrupt().  This is a workaround for
+         * Thread.interrupt not working on
+         * ServerSocket.accept on all platforms.
+         */
+        public void interrupt() {
+            interrupted = true;
+	    try {
+                serv.close();
+	    } catch (IOException ignore) {}
+        }
+
+        /**
+         * Overrides Thread.isInterrupted().
+         */
+        public boolean isInterrupted() {
+            return interrupted;
+        }
+	
+	public void run() {
+	    Config conf = getConfig();
+	    BasicAdmin ba = conf.getAdmin();
+	    Socket tempSock;
+	    while (!isInterrupted()) {
+		try {
+		    tempSock = serv.accept();
+		    if (!tempSock.getInetAddress().equals(ba.getAddress())) {
+			TestUtility.log(Config.ALL,"Test: ignoring socket "
+			    + "connection from: " + tempSock.getInetAddress());
+			tempSock.close();
+		    } else {
+			TestUtility.log(Config.ALL,"Test: "
+			    + "got successful socket connection from: "
+			    + tempSock.getInetAddress());
+			setSocket(tempSock);
+			break;
+		    }
+		} catch (Exception ignore) {
+		    TestUtility.log(Config.ERROR,"Test: "
+			+ "ignoring the following exception from "
+			+ "either accept, setSoTimeOut or getAddress:",ignore);
+		}
+	    }   
+
+	    try {
+		serv.close();
+	    } catch (IOException ignore) {}
+	}
+	
+	private synchronized void setSocket(Socket s) {
+	    sock = s;
+	    notifyAll();
+	}
+
+	public int getServerSocketPort() {
+	    return serv.getLocalPort();
+	}
+	
+	public synchronized Socket getSocket() {
+	    return sock;
+	}
+    }
+	    
+    public String[] getCategories() {
+	return (new String[] { BasicServiceAdmin.CATEGORY });
+    }
+
+    public Class[] getRequiredAdmins() {
+	return (new Class[] { BasicAdmin.class });
+    }
+			  
+    public String getDescription() {
+	return "AnnouncementResponseTest";
+    }
+
+}
+

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

Added: river/tck/src/com/sun/jini/compat/test/CodeDownloadTest.java
URL: http://svn.apache.org/viewvc/river/tck/src/com/sun/jini/compat/test/CodeDownloadTest.java?rev=1234278&view=auto
==============================================================================
--- river/tck/src/com/sun/jini/compat/test/CodeDownloadTest.java (added)
+++ river/tck/src/com/sun/jini/compat/test/CodeDownloadTest.java Sat Jan 21 07:28:27 2012
@@ -0,0 +1,220 @@
+/*
+ * 
+ * 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.test;
+
+import java.io.IOException;
+import java.rmi.RemoteException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.File;
+import java.net.InetAddress;
+
+import com.sun.jini.compat.harness.TestUtility;
+import com.sun.jini.compat.harness.Status;
+import com.sun.jini.compat.harness.DefaultTest;
+import com.sun.jini.compat.harness.BasicAdmin;
+import com.sun.jini.compat.harness.BasicClientAdmin;
+import com.sun.jini.compat.harness.BasicServiceAdmin;
+import com.sun.jini.compat.harness.Config;
+import com.sun.jini.compat.harness.DownloadEvent;
+import com.sun.jini.compat.harness.InstrumentedClassServer;
+import com.sun.jini.compat.harness.DownloadListener;
+
+import net.jini.core.lookup.ServiceRegistrar;
+
+/**
+ * Tests whether a service or a client downloads code from the
+ * java.rmi.server.codebase provided by the class server of the LDJ Kit.
+ * The code-base URL must use the HTTP protocol.
+ */
+public class CodeDownloadTest extends DefaultTest {
+
+    String randomName;
+    String tempJarFileName;
+    ServiceRegistrar lookup;
+    BasicAdmin admin;
+    InstrumentedClassServer ics;
+    CDListener cdl;
+
+    /**
+     * Copies the JAR file that contains the lookup service's 
+     * downloadable code to a random file name. This new file is 
+     * created to prevent caching from causing false negative
+     * results. The test then starts a lookup service with a 
+     * codebase that points to the new file and registers a 
+     * DownloadListener with the instrumented class server
+     * provided with the LDJ Kit. CodeDownloadTest then starts the 
+     * program and waits for five times the failureTime. After 
+     * either the wait times out or the test is notified,
+     * the test checks to see if the appropriate file (the 
+     * randomly named file) was actually downloaded. The 
+     * randomly named file is then deleted. If the randomly named file
+     * wasn't downloaded, the client or service being tested fails.
+     *
+     * @param args Command line arguments passed through
+     * @return The status of this run (failed or passed
+     *         and a comment)
+     */
+    public Status run(String[] args) {
+	Config conf = getConfig();
+	cdl = new CDListener();
+	admin = conf.getAdmin();
+	ics = conf.getClassServer();
+	randomName = TestUtility.randomString(8) + ".jar";
+
+	try {
+	    createTempJar();
+	} catch (IOException ioe) {
+            TestUtility.log(Config.ERROR,"Test: caught exception",ioe);
+            return Status.failed(Status.ENV,"Could not create a temporary "
+                + "copy of compat-lus-dl.jar");
+        }
+	    
+	try {
+	    TestUtility.log(Config.ALL,"Test: starting lookup in public group");
+	    lookup = TestUtility.startLookup(new String[] {""}, 
+		conf.getQuietTime(), "CodeDownloadTest_RegistrarLog", 
+		randomName, conf);
+	} catch (Exception e) {
+            TestUtility.log(Config.ERROR,"Test: caught exception",e);
+            return Status.failed(Status.ENV,"Problem starting lookup service");
+        }
+	    
+	try {
+	    synchronized(cdl) {
+		ics.addDownloadListener(cdl);
+		TestUtility.log(Config.ALL,"Test: starting program");
+		admin.start();
+		long waitTime = 5 * conf.getFailureTime();
+		TestUtility.log(Config.ALL,"Test: waiting up to " 
+		    + waitTime + " milliseconds for code to be download");
+		cdl.wait(waitTime);
+	    }
+	} catch (RemoteException re) {
+	    TestUtility.log(Config.ERROR,"Test: caught exception",re);
+            return Status.failed(Status.ENV,"Problem starting program");
+	} catch (InterruptedException ie) {
+            TestUtility.log(Config.ERROR,"Test: caught exception",ie);
+            return Status.failed(Status.ENV,"Test interrupted");
+        }
+
+	if (cdl.getFileName() == null) {
+	    return Status.failed(Status.TEST, "Product never downloaded "
+		+ "the lookup jar");
+	}
+
+	return Status.passed("OK");
+    }    
+
+    public void tearDown() {
+        super.tearDown();
+
+	deleteTempJar();
+	ics.removeDownloadListener(cdl);
+        try {
+            if (lookup != null) {
+		TestUtility.log(Config.ALL,"Test: stopping lookup");
+                TestUtility.stopLookup(lookup, getConfig());
+            }
+        } catch (RemoteException ignore) {}
+        try {
+            if (admin != null) {
+		TestUtility.log(Config.ALL,"Test: stopping program");
+                admin.stop();
+            }
+        } catch (RemoteException ignore) {}
+    }
+    
+    private class CDListener implements DownloadListener {
+
+	private String fname = null;
+
+	public synchronized String getFileName() {
+	    return fname;
+	}
+
+	public synchronized void fileDownloaded(DownloadEvent de) {
+	    String fileName = de.getName();
+	    InetAddress address = de.getAddress();
+	    Config conf = getConfig();
+	    BasicAdmin ba = conf.getAdmin();
+	    InetAddress ourAddr = null;
+
+	    try {
+		ourAddr = ba.getAddress();
+	    } catch(RemoteException re) {
+		TestUtility.log(Config.ERROR,"Test: <admin>.getAddress "
+		    + "threw a RemoteException",re);
+	    }
+	    TestUtility.log(Config.ALL,"Test: " + address + " downloaded " 
+		+ fileName);
+	    if (fileName.equals(randomName) && address.equals(ourAddr)) {
+		fname = fileName;
+		notifyAll();
+	    }
+	}
+    }
+    
+    private void createTempJar() throws IOException {
+	String iDir = getConfig().getSysConfig().getStringConfigVal(
+	    "com.sun.jini.compat.installDir", ".");
+
+	String sep = File.separator;
+	String path = iDir + sep + "lib" + sep + "dl" + sep; 
+	String oldJarFileName = path + "compat-lus-dl.jar";
+	tempJarFileName = path + randomName;
+
+	FileInputStream fis = new FileInputStream(oldJarFileName);
+	FileOutputStream fos = new FileOutputStream(tempJarFileName);
+	
+	TestUtility.log(Config.ALL,"Test: copying " + oldJarFileName);
+	TestUtility.log(Config.ALL,"      to " + tempJarFileName);
+
+	//must copy file from one location to the other
+	byte inBytes[] = new byte[1000];
+	int count;
+	while ((count = fis.read(inBytes)) != -1) {
+	    fos.write(inBytes, 0, count);
+	}
+	fos.close();
+	fis.close();
+    }
+
+    private void deleteTempJar() {
+	if (tempJarFileName != null) {
+	    File f = new File(tempJarFileName);
+	    f.delete();
+	    TestUtility.log(Config.ALL,"Test: deleted file " + tempJarFileName);
+	}
+    }
+
+    public String[] getCategories() {
+	return (new String[] { BasicClientAdmin.CATEGORY,
+			       BasicServiceAdmin.CATEGORY });
+    }
+
+    public Class[] getRequiredAdmins() {
+	return (new Class[] { BasicAdmin.class });
+    }
+    
+    public String getDescription() {
+	return "CodeDownloadTest";
+    }
+
+}
+

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

Added: river/tck/src/com/sun/jini/compat/test/EventLeaseExpirationTest.java
URL: http://svn.apache.org/viewvc/river/tck/src/com/sun/jini/compat/test/EventLeaseExpirationTest.java?rev=1234278&view=auto
==============================================================================
--- river/tck/src/com/sun/jini/compat/test/EventLeaseExpirationTest.java (added)
+++ river/tck/src/com/sun/jini/compat/test/EventLeaseExpirationTest.java Sat Jan 21 07:28:27 2012
@@ -0,0 +1,211 @@
+/*
+ * 
+ * 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.test;
+
+import com.sun.jini.compat.test.lookup.TestUtils;
+import com.sun.jini.compat.test.lookup.TestImpl;
+import com.sun.jini.compat.test.lookup.TestRegistrar;
+import com.sun.jini.compat.test.lookup.TestException;
+import com.sun.jini.compat.test.lookup.TestFailedException;
+import com.sun.jini.compat.test.lookup.TestUnresolvedException;
+import com.sun.jini.compat.harness.Config;
+
+import net.jini.core.lookup.ServiceRegistrar;
+import net.jini.core.lookup.ServiceEvent;
+import net.jini.core.lookup.ServiceItem;
+import net.jini.core.lookup.ServiceRegistration;
+import net.jini.core.lookup.ServiceTemplate;
+import net.jini.core.event.EventRegistration;
+import net.jini.core.event.RemoteEvent;
+import net.jini.core.event.RemoteEventListener;
+import net.jini.core.lease.UnknownLeaseException;
+import java.rmi.Remote;
+import java.rmi.NoSuchObjectException;
+import java.rmi.RemoteException;
+import java.rmi.StubNotFoundException;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.Vector;
+
+/** This class is used to test that all event notifications cease to be
+ *  delivered after all event leases have expired.
+ *
+ *  @see com.sun.jini.compat.test.lookup.Test
+ *  @see com.sun.jini.compat.test.lookup.TestImpl
+ *  @see com.sun.jini.compat.test.lookup.TestRegistrar
+ *  @see com.sun.jini.compat.test.lookup.TestUtils
+ */
+public class EventLeaseExpirationTest extends TestRegistrar {
+
+    /** Class which handles all events sent by the lookup service */
+    public class Listener implements RemoteEventListener {
+        Object proxy;
+        public Listener() throws RemoteException {
+            proxy = UnicastRemoteObject.exportObject((Remote)this);
+        }
+        /** Method called remotely by lookup to handle the generated event. */
+        public void notify(RemoteEvent ev) {
+            ServiceEvent srvcEvnt = (ServiceEvent)ev;
+            evntVec.addElement(srvcEvnt);
+        }
+        public Object getProxy() {
+            return proxy;
+        }
+    }
+
+    /** The event handler for the services registered by this class */
+    private static Listener listener;
+
+    protected Vector evntVec = new Vector();
+
+    private long realLeaseDuration;
+    private ServiceItem[] srvcItems ;
+    private ServiceRegistration[] srvcRegs ;
+    private ServiceTemplate template;
+    private ServiceRegistrar proxy;
+
+    /** Performs actions necessary to prepare for execution of the 
+     *  current  test.
+     *
+     *  Creates a single event handler to handle all events generated
+     *  by any of the registered service items. Creates the lookup
+     *  service. Loads and instantiates all service classes. Registers 
+     *  each service class instance with the maximum service lease duration. 
+     *  Retrieves the proxy to the lookup Registrar. Creates a single
+     *  ServiceTemplate using ServiceID of registered services.
+     *  Register an event notification request based on the contents of that
+     *  template, a transition mask including all three transitions, and
+     *  a lease duration that is less than the duration of the service
+     *  leases.
+     *  @exception TestException will usually indicate an "unresolved"
+     *  condition because at this point the test has not yet begun.
+     */
+    public void setupQATest() throws TestException {
+	try {
+	    listener = new Listener();
+	} catch (RemoteException e) {
+            throw new TestUnresolvedException
+                     ("setupQATest: RemoteException from listener creation",e);
+	}
+	super.setupQATest();
+	Config conf = getConfig();
+        srvcItems = super.createServiceItems(TEST_SRVC_CLASSES);
+	srvcRegs = super.registerAll();
+	proxy = super.getProxy();
+	template = new ServiceTemplate(srvcRegs[0].getServiceID(),null,null);
+	try {
+	    EventRegistration evntReg = proxy.notify(template,
+		ServiceRegistrar.TRANSITION_NOMATCH_MATCH |
+		    ServiceRegistrar.TRANSITION_MATCH_NOMATCH |
+		    ServiceRegistrar.TRANSITION_MATCH_MATCH,
+		(RemoteEventListener) listener.getProxy(), 
+                null, 
+                conf.getFailureTime());
+	    long leaseExpiration = evntReg.getLease().getExpiration();
+	    long leaseStartTime = TestUtils.getCurTime();
+	    realLeaseDuration = leaseExpiration - leaseStartTime;
+	} catch (RemoteException e) {
+            throw new TestUnresolvedException
+                                ("setupQATest: RemoteException from notify",e);
+	}
+    }
+
+    /** wait for the event lease to expire and then verify that NO events
+     *  have arrived
+     */
+    /** Executes the current  test.
+     *
+     *  Computes the amount of time to wait to guarantee that all event
+     *  leases have expired. Waits that amount of time. Cancels each
+     *  service lease (to generate an event for each cancellation). Waits
+     *  an appropriate amount of time to allow any events that may be
+     *  generated to be sent and collected. Verifies that no events were
+     *  sent by the Lookup service. 
+     *  @exception TestException usually indicates test failure
+     */
+
+    /*  The time-line diagram below shows the steps of this test:
+     *
+     *           |-----------------------------------------------------------|
+     *           :         :         ^      :  
+     * |-------------------|         :      :  
+     * 0                 5 secs      :      : 
+     *                     :         :      :
+     *                     :         :      :
+     *                  Expires    Cancel   :
+     *                             Service  :
+     *                             Lease    Analyze
+     */
+    public void doQATest() throws TestException {
+        ServiceEvent evnt = null;
+        /* wait for the event lease to expire */
+	try {
+            Thread.sleep(realLeaseDuration);
+	} catch (InterruptedException e) {
+        }
+
+	/* throw out previous, spurious received events */
+	evntVec = new Vector();
+
+        /* cancel each service lease so as to generate events */
+	for(int i=0; i<srvcRegs.length; i++) {
+	    try {
+                srvcRegs[i].getLease().cancel();
+	    } catch (UnknownLeaseException e) {
+                throw new TestFailedException
+                       ("doQATest: UnknownLeaseException from cancel (index = "
+                         +i+")",e);
+	    } catch (RemoteException e) {
+                throw new TestFailedException
+                             ("doQATest: RemoteException from cancel (index = "
+                               +i+")",e);
+            }
+	}
+        /* give the Listener a chance to collect all events */
+	try {
+            Thread.sleep(getConfig().getQuietTime());
+	} catch (InterruptedException e) {
+        }
+
+        /* Verify that no events have arrived. If no events have arrived
+         * the event vector should be empty. If not, declare failure
+         */
+        if (evntVec.size() != 0) {
+            throw new TestFailedException("doQATest: evntVec.size() != 0");
+	}
+    }
+
+    /** Performs cleanup actions necessary to achieve a graceful exit of 
+     *  the current  test.
+     *
+     *  Unexports the listener and then performs any remaining standard
+     *  cleanup duties.
+     *  @exception TestException will usually indicate an "unresolved"
+     *  condition because at this point the test has completed.
+     */
+    public void cleanupQATest() throws TestException  {
+	try {
+	    UnicastRemoteObject.unexportObject((Remote)listener, true);
+	} catch (NoSuchObjectException e) {
+        }
+	super.cleanupQATest();
+    }
+    
+    public String getDescription() {
+	return "EventLeaseExpirationTest";
+    }   
+}

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