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 [21/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/ServiceStarter.java
URL: http://svn.apache.org/viewvc/river/tck/src/com/sun/jini/compat/start/ServiceStarter.java?rev=1234278&view=auto
==============================================================================
--- river/tck/src/com/sun/jini/compat/start/ServiceStarter.java (added)
+++ river/tck/src/com/sun/jini/compat/start/ServiceStarter.java Sat Jan 21 07:28:27 2012
@@ -0,0 +1,2564 @@
+/*
+ * 
+ * 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 net.jini.admin.Administrable;
+import net.jini.admin.JoinAdmin;
+
+import net.jini.lookup.DiscoveryAdmin;
+
+import net.jini.core.discovery.LookupLocator;
+import net.jini.core.lookup.ServiceRegistrar;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.PrintWriter;
+import java.io.Serializable;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import java.rmi.activation.Activatable;
+import java.rmi.activation.ActivationDesc;
+import java.rmi.activation.ActivationException;
+import java.rmi.activation.ActivationGroup;
+import java.rmi.activation.ActivationGroupDesc;
+import java.rmi.activation.ActivationGroupDesc.CommandEnvironment;
+import java.rmi.activation.ActivationGroupID;
+import java.rmi.activation.ActivationID;
+import java.rmi.activation.ActivationSystem;
+import java.rmi.MarshalledObject;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.rmi.RMISecurityManager;
+import java.rmi.server.RemoteObject;
+
+import java.security.AccessController;
+
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+/** 
+ * This class provides static methods that can be used to start an
+ * activatable service.
+ *
+ * @author Sun Microsystems, Inc.
+ *
+ */
+public class ServiceStarter {
+
+    private static boolean invocationTargetException = false;
+
+    /**
+     * Utility for outputting shared group related debug messages.
+     */
+    private static final Debug debug = 
+	(Debug)AccessController.doPrivileged(
+	    Debug.getDebugAction(GroupConstants.DEBUG_PROP));
+
+    /**
+     * Print writer for shared group creation related debug messages.
+     */
+    private static final PrintWriter dbgCreate = debug.getWriter("create");
+
+    /* Default timeout to use for destroying services */
+    static final int DEFAULT_DESTROY_TIMEOUT = 60; /* seconds */ 
+
+    /* Starter class' <code>createGroup</code> method */
+    private static final String createGroupMethod = "createGroup";
+
+    /* Parameters to the starter class' <code>createGroup</code> method */
+    private static final Class[] createGroupArgTypes = { 
+        String.class,      //classpath
+        String.class,      //codebase
+        String.class,      //policyFile
+        Properties.class,  //properties
+        String.class,      //javaProgram cmdPath
+        String[].class     //options (argv)
+    };
+
+    /** Data structure used by some of the <code>create</code> methods to
+     *  return multiple values (such as the service reference and the
+     *  <code>ActivationGroupID</code> of the service).
+     */
+    public static class Created {
+        /** The activation group id of the service */
+        public final ActivationGroupID gid;
+        /** The activation id of the service */
+        public final ActivationID aid;
+        /** The reference to the proxy of the created service */
+	public final Object proxy;
+        /** Constructs an instance of this class.
+         * @param gid   activation group id of the created service
+         * @param aid   activation id of the created service
+         * @param proxy reference to the proxy of the created service
+         */
+        public Created(ActivationGroupID gid, ActivationID aid, Object proxy) {
+            this.gid   = gid;
+            this.aid   = aid;
+            this.proxy = proxy;
+        }//end constructor
+    }//end class Created
+
+    /** Data structure used by the "shared" <code>create</code> methods to
+     *  return multiple values (such as the service reference and the
+     *  <code>ActivationGroupID</code> of the service).
+     */
+    public static class SharedCreated implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+        /** The activation group id hosting the service */
+        public final ActivationGroupID gid;
+
+        /** The activation id of the service */
+        public final ActivationID aid;
+
+        /** 
+	 * A <code>MarshalledObject</code> containing the proxy of 
+	 * the created service. A <code>MarshalledObject</code> is used,
+	 * instead of a direct reference, in order to preserve any
+	 * codebase annotations. This is useful in cases where the
+	 * the reference is stored to disk and used across multiple
+	 * program (virtual machine) invocations.
+	 */
+	public final MarshalledObject mProxy;
+
+        /**
+	 * Trivial constructor.
+	 */
+	public SharedCreated(ActivationGroupID gid, ActivationID aid, 
+			     MarshalledObject mProxy) 
+	{
+	    this.gid = gid;
+	    this.aid = aid;
+	    this.mProxy = mProxy;
+	}
+    }
+
+    /**
+     * This method creates an activatable instance of a service's
+     * implementation by parsing a set of arguments retrieved from the 
+     * command line, and then invoking a static creation method on a 
+     * "service starting class" that is supplied by the entity that
+     * invokes this method. The class supplied to this method must
+     * know how to instantiate the proxy that provides client-side
+     * access to the service instance.
+     * <p>
+     * This method provides a mechanism where a service may be started
+     * through a non-generic, custom-defined, class that knows the
+     * specific start requirements of the desired service. The 
+     * arguments input (through the <code>args</code> parameter) 
+     * must satisfy the argument list format for starting activatable
+     * services as specified by the <code>StartUtil</code> class.
+     * <p>
+     * The service starter framework supports both unshared and shared
+     * activation groups. An unshared activation group is specially 
+     * customized to host a single (unshared) service instance. A shared 
+     * activation group is generally not customized to host a specific service 
+     * and can therefore be used to host multiple (shared) service instances.
+     * <p>
+     * Note that when an unshared service is destroyed (through the
+     * <code>com.sun.jini.admin.DestroyAdmin</code> interface provided
+     * by the service's implementation), it will also destroy the 
+     * activation group to which the service belongs, regardless of
+     * whether or not there are other activatable objects still registered
+     * in that activation group. This is due to the following reason:
+     * <p>
+     * When the <code> ActivationDesc</code> used to register the desired
+     * service is ultimately created (through the invocation of this method),
+     * the <code>location</code> parameter to that class' constructor is
+     * <i>not</i> set. If the value of that parameter were to be set, then
+     * both the import codebase (the classpath) and the export codebase of
+     * the service to be registered would be identical. This means that the
+     * activation system would use that single location to both retrieve
+     * classes when activating the server-side of the service, and to
+     * annotate (with the RMI codebase) all remote classes that are
+     * downloaded to the client-side.
+     * <p>
+     * Because it is desirable to allow for different values in the import
+     * and export codebases, this class wishes to distinguish those 
+     * parameters for the activation system. To do this, this class must
+     * communicate each codebase's value to the activation system through
+     * a different mechanism than the <code>location</code> parameter of
+     * the constructor of <code>ActivationDesc</code>. That alternate
+     * mechanism consists of creating an activation group descriptor
+     * (<code>ActivationGroupDesc</code>) in which the properties are
+     * over-ridden to include the desired import codebase as the new
+     * classpath property ("-cp <import codebase>"); and the export 
+     * codebase as the new <code>java.rmi.server.codebase</code> property.
+     * The activation descriptor (<code>ActivationDesc</code>) to be used
+     * in the service's registration with the activation system is then
+     * created using the same group ID (<code>ActivationGroupID</code>)
+     * as that generated by the creation of the activation group descriptor
+     * with the desired import and export codebases.
+     * <p>
+     * Because the activation group that is created and registered with the
+     * activation system is useful only to entities that have the same
+     * import and export codebases as those registered with the group,
+     * (and because such entities wishing to register with that activation
+     * group would also have to obtain the activation group ID from the
+     * service registered by this class), it is not expected that when
+     * the service is destroyed through the <code>DestroyAdmin</code> that
+     * any other entities will be registered in that activation group. Being
+     * the only entity registered in the activation group means that when
+     * it is destroyed, it is also the last entity registered in the group.
+     * Thus, upon destroying the service, the activation group created here
+     * is also ultimately unregistered (removed) from the activation system
+     * because once that service is destroyed, there is no way to access
+     * the activation group ID for future registrations (or even for 
+     * destroying).
+     * <p>
+     * Note that when a shared service is destroyed (through the
+     * <code>com.sun.jini.admin.DestroyAdmin</code> interface provided
+     * by the service's implementation), it will not destroy the 
+     * activation group to which the service belongs.
+     * <p>
+     * @param args             <code>String</code> array containing the
+     *                         arguments (typically retrieved from the command
+     *                         line) to use when starting the service. The
+     *                         arguments contained in this parameter must
+     *                         satisfy the argument list format specified by
+     *                         the <code>StartUtil</code> class. 
+     * @param starterClassName the fully-qualified path.classname of the
+     *                         class that defines the public static
+     *                         <code>create</code> method (and possibly an
+     *                         optional public static <code>createGroup</code>
+     *                         method that this method invokes through
+     *                         reflection in order to start a desired service
+     * @param implClassName    the fully-qualified path.classname of the
+     *                         class that defines the implementation of the
+     *                         backend server of the service that is to be
+     *                         started by this method
+     * @param resourcename     the name used to identify a particular 
+     *                         resource bundle from which usage/error
+     *                         message information may be retrieved for
+     *                         display (helpful with internationalization).
+     *                         Two common values for this parameter are:
+     *                         "lookup" and "service".
+     * 
+     * @return an instance of the service's interface which is a proxy that
+     *         provides client-side access to the created service
+     *
+     * @see com.sun.jini.compat.start.StartUtil
+     */
+    public static Object create(String[] args,
+                                String   starterClassName,
+                                String   implClassName,
+                                String   resourcename) 
+    {
+        /* Parse the input args */
+        String resource = ((resourcename == null) ? "service" : resourcename);
+        StartUtil startUtil = new StartUtil(resource);
+        ParsedArgs parsedArgs = startUtil.parseActivatableArgs(args);
+        if(parsedArgs == null) {
+            startUtil.printUsage();
+            return null;
+        }
+	try {
+            return create(parsedArgs,starterClassName,
+			  implClassName,resourcename);
+	} catch (Exception e) {
+	    if(!invocationTargetException) e.printStackTrace();
+            return null;
+	}
+    }//end create
+
+    /**
+     * This method creates an activatable instance of a service's
+     * implementation by invoking a static creation method on a 
+     * "service starting class" that is supplied by the entity that
+     * invokes this method. The class supplied to this method must
+     * know how to instantiate the proxy that provides client-side
+     * access to the service instance. 
+     * <p>
+     * This version of the <code>create</code> method is intended as a
+     * programmatic API. Unlike the version of <code>create</code> that
+     * takes a <code>String</code> array containing items entered on the
+     * command line, this version expects that such arguments have
+     * already been parsed and will be passed in through an instance of
+     * the data structure class <code>ParsedArgs</code>.
+     * <p>
+     * Note that, as with the other version the <code>create</code>
+     * method, when an unshared service is destroyed, it will also destroy the 
+     * activation group to which the service belongs, regardless of
+     * whether or not there are other activatable objects still registered
+     * in that activation group. The destruction of a shared service will not
+     * destroy the associated activation group. (See <code>create<code> above
+     * for details on shared vs. unshared services.)
+     *
+     * @param parsedArgs       instance of <code>ParsedArgs</code> which is a
+     *                         data structure containing the arguments to use
+     *                         when starting the service. 
+     * @param starterClassName the fully-qualified path.classname of the
+     *                         class that defines the public static
+     *                         <code>create</code> method (and possibly an
+     *                         optional public static <code>createGroup</code>
+     *                         method that this method invokes through
+     *                         reflection in order to start a desired service
+     * @param implClassName    the fully-qualified path.classname of the
+     *                         class that defines the implementation of the
+     *                         backend server of the service that is to be
+     *                         started by this method
+     * @param resourcename     the name used to identify a particular 
+     *                         resource bundle from which usage/error
+     *                         message information may be retrieved for
+     *                         display (helpful with internationalization).
+     *                         Two common values for this parameter are:
+     *                         "lookup" and "service".
+     * 
+     * @return an instance of the service's interface which is a proxy that
+     *         provides client-side access to the created service
+     *
+     * @see com.sun.jini.compat.start.ParsedArgs
+     * @see com.sun.jini.compat.start.StartUtil
+     */
+    public static Object create(ParsedArgs parsedArgs,
+                                String     starterClassName,
+                                String     implClassName,
+                                String     resourcename)
+	throws ActivationException, IOException, ClassNotFoundException
+    {
+        String resource = ((resourcename == null) ? "service" : resourcename);
+        if (parsedArgs == null || starterClassName == null ||
+            implClassName == null) {
+            throw new IllegalArgumentException("parsedArgs, starterClassName, "
+                + "and implClassName must non-null");
+        }
+        String classpath = System.getProperty("java.class.path");
+	StartUtil util = new StartUtil(resource);
+        Created created = null;
+ 	if(parsedArgs.getSharedVMLog() != null) {
+            created = doCreateShared(parsedArgs,starterClassName,
+		  	             implClassName, classpath,
+			             util);
+	} else {              
+	    created = doCreate(parsedArgs,starterClassName,implClassName,
+                               classpath,util);
+        }
+        if(created == null) return null;
+        return created.proxy;      
+    }//end create
+
+    /**
+     * This method creates a shared, activatable service instance
+     * by invoking a static creation method on a 
+     * "service starting class" that is supplied by the entity that
+     * invokes this method. The class supplied to this method must
+     * know how to instantiate the proxy that provides client-side
+     * access to the service instance. 
+     * <p>
+     * Through the data structure returned by this version of the
+     * <code>create</code> method, entities are given access not only
+     * to a proxy to the created service, but to the service's
+     * <code>ActivationGroupID</code> as well.
+     *
+     * @param codebase         the RMI codebase with which to annotate all
+     *                         remote classes that are exported by the 
+     *                         service to be started
+     * @param policyFile       the path of the policy file that
+     *                         applies to the service to be started 
+     * @param logDir           the directory in which the state of the
+     *                         service to be started will be recorded for
+     *                         persistence
+     * @param groups           if the service to be started is a lookup
+     *                         service, this parameter contains the names
+     *                         of the groups to which that lookup service
+     *                         is to be a member; otherwise, this parameter
+     *                         contains the names of the groups whose members
+     *                         are the lookup services the service to be
+     *                         started should discover and join
+     * @param locators         if the service to be started is a lookup
+     *                         service, this parameter typically will not be
+     *                         when starting that lookup service; otherwise,
+     *                         this parameter contains references to the
+     *                         specific lookup services the service to be
+     *                         started should discover and join
+     * @param sharedvm_log     the fully-qualified path of the shared activation
+     *                         group directory. This directory contains the 
+     *                         necessary information to associate the service
+     *                         instance with the particular group.
+     * @param starterClassName the fully-qualified path.classname of the
+     *                         class that defines the public static
+     *                         <code>create</code> method (and possibly an
+     *                         optional public static <code>createGroup</code>
+     *                         method that this method invokes through
+     *                         reflection in order to start a desired service
+     * @param implClassName    the fully-qualified path.classname of the
+     *                         class that defines the implementation of the
+     *                         backend server of the service that is to be
+     *                         started by this method
+     * @param classpath        the classpath from which to load the various
+     *                         classes needed to start the service (this may
+     *                         be different from the classpath from which the
+     *                         the current class was loaded). Note that
+     *                         multi-component classpaths are not currently
+     *                         supported.
+     * @param resourcename     the name used to identify a particular 
+     *                         resource bundle from which usage/error
+     *                         message information may be retrieved for
+     *                         display (helpful with internationalization).
+     *                         Two common values for this parameter are:
+     *                         "lookup" and "service".
+     * 
+     * @return an instance of the static inner class <code>Created</code>,
+     *         which contains the service's <code>ActivationGroupID</code>
+     *         and an instance of the service's interface which is a proxy
+     *         that provides client-side access to the created service
+     *
+     * @throws java.rmi.activation.ActivationException this is a general
+     *         exception thrown by the activation system to indicate a
+     *         a number of exceptional conditions that prevent the
+     *         activation system from fulfilling a particular request.
+     * 
+     * @throws java.io.IOException 
+     * 
+     * @see com.sun.jini.compat.start.ParsedArgs
+     */
+    public static Created create(
+            String          codebase,
+            String          policyFile,
+            String          logDir,
+            String[]        groups,
+            LookupLocator[] locators,
+            String          sharedvm_log,
+            String          starterClassName,
+            String          implClassName,
+            String          classpath,
+            String          resourcename) 
+        throws ActivationException, IOException, ClassNotFoundException
+    {
+        String resource = ((resourcename == null) ? "service" : resourcename);
+        ParsedArgs parsedArgs = 
+	    new ParsedArgs(codebase, policyFile, logDir,
+                           groups, locators,
+		           null, null, null,
+			   sharedvm_log);
+        return doCreateShared(parsedArgs,starterClassName,implClassName,
+                              classpath, new StartUtil(resource));
+    }//end create
+    
+    /**
+     * This method creates an unshared activatable instance of a service's
+     * implementation by invoking a static creation method on a 
+     * "service starting class" that is supplied by the entity that
+     * invokes this method. The class supplied to this method must
+     * know how to instantiate the proxy that provides client-side
+     * access to the service instance. 
+     * <p>
+     * Through the data structure returned by this version of the
+     * <code>create</code> method, entities are given access not only
+     * to a proxy to the created service, but to the service's
+     * <code>ActivationGroupID</code> as well.
+     *
+     * @param codebase         the RMI codebase with which to annotate all
+     *                         remote classes that are exported by the 
+     *                         service to be started
+     * @param policyFile       the path and name of the policy file that
+     *                         applies to the VM in which the service to
+     *                         be started will run
+     * @param logDir           the directory in which the state of the
+     *                         service to be started will be recorded for
+     *                         persistence
+     * @param groups           if the service to be started is a lookup
+     *                         service, this parameter contains the names
+     *                         of the groups to which that lookup service
+     *                         is to be a member; otherwise, this parameter
+     *                         contains the names of the groups whose members
+     *                         are the lookup services the service to be
+     *                         started should discover and join
+     * @param locators         if the service to be started is a lookup
+     *                         service, this parameter typically will not be
+     *                         when starting that lookup service; otherwise,
+     *                         this parameter contains references to the
+     *                         specific lookup services the service to be
+     *                         started should discover and join
+     * @param javaProgram      specifies which VM implementation should be
+     *                         used for the backend server of the service to
+     *                         be started. When not <code>null</code>, the
+     *                         value of this parameter is an absolute path
+     *                         to an executable that accepts all the standard
+     *                         VM command-line options (for example, -D,
+     *                         -classpath, and so on), and which directly or
+     *                         indirectly starts a VM. The value contained in
+     *                         this parameter is useful for testing the 
+     *                         service to be started on alternate VM
+     *                         implementations
+     * @param properties       specifies the system properties with which to
+     *                         start the desired service
+     * @param options          specifies the command options (for example -cp
+     *                         <classpath>) to use when starting the desired
+     *                         service
+     * @param starterClassName the fully-qualified path.classname of the
+     *                         class that defines the public static
+     *                         <code>create</code> method (and possibly an
+     *                         optional public static <code>createGroup</code>
+     *                         method that this method invokes through
+     *                         reflection in order to start a desired service
+     * @param implClassName    the fully-qualified path.classname of the
+     *                         class that defines the implementation of the
+     *                         backend server of the service that is to be
+     *                         started by this method
+     * @param classpath        the classpath from which to load the various
+     *                         classes needed to start the service (this may
+     *                         be different from the classpath from which the
+     *                         the current class was loaded)
+     * @param resourcename     the name used to identify a particular 
+     *                         resource bundle from which usage/error
+     *                         message information may be retrieved for
+     *                         display (helpful with internationalization).
+     *                         Two common values for this parameter are:
+     *                         "lookup" and "service".
+     * 
+     * @return an instance of the static inner class <code>Created</code>,
+     *         which contains the service's <code>ActivationGroupID</code>
+     *         and an instance of the service's interface which is a proxy
+     *         that provides client-side access to the created service
+     *
+     * @throws java.rmi.activation.ActivationException this is a general
+     *         exception thrown by the activation system to indicate a
+     *         a number of exceptional conditions that prevent the
+     *         activation system from fulfilling a particular request.
+     * 
+     * @throws java.io.IOException 
+     * 
+     * @see com.sun.jini.compat.start.ParsedArgs
+     */
+    public static Created create(String          codebase,
+                                 String          policyFile,
+                                 String          logDir,
+                                 String[]        groups,
+                                 LookupLocator[] locators,
+                                 String          javaProgram,
+                                 Properties      properties,
+                                 String[]        options,
+                                 String          starterClassName,
+                                 String          implClassName,
+                                 String          classpath,
+                                 String          resourcename) 
+                                       throws ActivationException, IOException
+    {
+        String resource = ((resourcename == null) ? "service" : resourcename);
+        ParsedArgs parsedArgs = new ParsedArgs(codebase,policyFile,logDir,
+                                               groups,locators,javaProgram,
+                                               properties,options);
+        return  doCreate(parsedArgs,starterClassName,implClassName,
+                         classpath, new StartUtil(resource));
+	                 
+    }//end create
+
+
+    /**
+     * This method creates an unshared activatable instance of a service's
+     * implementation by invoking a static creation method on a 
+     * "service starting class" that is supplied by the entity that
+     * invokes this method. The class supplied to this method must
+     * know how to instantiate the proxy that provides client-side
+     * access to the service instance. 
+     * <p>
+     * Through the data structure returned by this version of the
+     * <code>create</code> method, entities are given access not only
+     * to a proxy to the created service, but to the service's
+     * <code>ActivationGroupID</code> as well. 
+     * <p>
+     * This version of the <code>create</code> method, provided as a
+     * convenience, is intended to be used in situations where both the
+     * service's proxy and its <code>ActivationGroupID</code> are desired,
+     * but where there is no need (or no desire) to start the service using
+     * an alternate VM implementation, or to employ properties and options
+     * that differ from the default values.
+     * 
+     *
+     * @param codebase         the RMI codebase with which to annotate all
+     *                         remote classes that are exported by the 
+     *                         service to be started
+     * @param policyFile       the path and name of the policy file that
+     *                         applies to the VM in which the service to
+     *                         be started will run
+     * @param logDir           the directory in which the state of the
+     *                         service to be started will be recorded for
+     *                         persistence
+     * @param groups           if the service to be started is a lookup
+     *                         service, this parameter contains the names
+     *                         of the groups to which that lookup service
+     *                         is to be a member; otherwise, this parameter
+     *                         contains the names of the groups whose members
+     *                         are the lookup services the service to be
+     *                         started should discover and join
+     * @param locators         if the service to be started is a lookup
+     *                         service, this parameter typically will not be
+     *                         when starting that lookup service; otherwise,
+     *                         this parameter contains references to the
+     *                         specific lookup services the service to be
+     *                         started should discover and join
+     * @param starterClassName the fully-qualified path.classname of the
+     *                         class that defines the public static
+     *                         <code>create</code> method (and possibly an
+     *                         optional public static <code>createGroup</code>
+     *                         method that this method invokes through
+     *                         reflection in order to start a desired service
+     * @param implClassName    the fully-qualified path.classname of the
+     *                         class that defines the implementation of the
+     *                         backend server of the service that is to be
+     *                         started by this method
+     * @param classpath        the classpath from which to load the various
+     *                         classes needed to start the service (this may
+     *                         be different from the classpath from which the
+     *                         the current class was loaded)
+     * @param resourcename     the name used to identify a particular 
+     *                         resource bundle from which usage/error
+     *                         message information may be retrieved for
+     *                         display (helpful with internationalization).
+     *                         Two common values for this parameter are:
+     *                         "lookup" and "service".
+     * 
+     * @return an instance of the static inner class <code>Created</code>,
+     *         which contains the service's <code>ActivationGroupID</code>
+     *         and an instance of the service's interface which is a proxy
+     *         that provides client-side access to the created service
+     *
+     * @throws java.rmi.activation.ActivationException this is a general
+     *         exception thrown by the activation system to indicate a
+     *         a number of exceptional conditions that prevent the
+     *         activation system from fulfilling a particular request.
+     * 
+     * @throws java.io.IOException 
+     * 
+     * @see com.sun.jini.compat.start.ParsedArgs
+     */
+    public static Created create(String          codebase,
+                                 String          policyFile,
+                                 String          logDir,
+                                 String[]        groups,
+                                 LookupLocator[] locators,
+                                 String          starterClassName,
+                                 String          implClassName,
+                                 String          classpath,
+                                 String          resourcename) 
+                                       throws ActivationException, IOException
+    {
+        return create(codebase,policyFile,logDir,groups,locators,
+                      null,null,null,
+                      starterClassName,implClassName,classpath,resourcename);
+    }//end create
+
+    /**
+     * This method creates a non-activatable (transient) instance of a
+     * service's implementation that logs its state to persistent storage.
+     * This method creates the desired service instance by parsing a set
+     * of arguments retrieved from the command line, and then invoking a
+     * static creation method on a "transient service starting class" that
+     * is supplied by the entity that invokes this method. The class supplied
+     * to this method must know how to instantiate the proxy that provides
+     * client-side access to the service instance.
+     * <p>
+     * This method provides a mechanism where a service may be started
+     * through a non-generic, custom-defined, class that knows the
+     * specific start requirements of the desired transient service. The 
+     * arguments input (through the <code>args</code> parameter) 
+     * must satisfy the argument list format for starting transient
+     * services as specified by the <code>StartUtil</code> class.
+     * <p>
+     * Although "transient" means that the service is not persisted for
+     * recovery from system outages and crashes, the transient version of
+     * services started by this method typically records its state to files 
+     * located in the directory entered on the command line. 
+     *
+     * @param args             <code>String</code> array containing the
+     *                         arguments (typically retrieved from the command
+     *                         line) to use when starting the service. The
+     *                         arguments contained in this parameter must
+     *                         satisfy the argument list format specified by
+     *                         the <code>StartUtil</code> class. 
+     * @param starterClassName the fully-qualified path.classname of the
+     *                         class that defines a public static
+     *                         <code>create</code> method that this method
+     *                         invokes through reflection in order to start
+     *                         the transient version of the desired service
+     * @param classpath        the classpath from which to load the various
+     *                         classes needed to start the service (this may
+     *                         be different from the classpath from which the
+     *                         the current class was loaded)
+     * @param codebase         the RMI codebase with which to annotate all
+     *                         remote classes that are exported by the 
+     *                         service to be started
+     * @param resourcename     the name used to identify a particular 
+     *                         resource bundle from which usage/error
+     *                         message information may be retrieved for
+     *                         display (helpful with internationalization)
+     * 
+     * @return an instance of the service's interface which is a proxy that
+     *         provides client-side access to the created service
+     * 
+     */
+    public static Object createTransient(String[] args,
+                                         String   starterClassName,
+                                         String   classpath,
+                                         String   codebase,
+                                         String   resourcename) 
+    {
+        /* Parse the input args */
+        StartUtil startUtil = new StartUtil(resourcename);
+        ParsedArgs parsedArgs = startUtil.parseTransientArgs(args);
+        if(parsedArgs == null) {
+            startUtil.printTransientUsage();
+            return null;
+        }
+        try {
+            return doCreateTransient(parsedArgs,starterClassName,
+                                     classpath,codebase,startUtil,
+                                     true);//log persistent state
+	} catch (Exception e) {
+            if(!invocationTargetException) e.printStackTrace();
+            return null;
+	}
+    }//end createTransient
+
+    /**
+     * This method creates a non-activatable (transient) instance of a
+     * service's implementation that does NOT log its state to persistent
+     * storage. This method creates the desired service instance by parsing
+     * a set of arguments retrieved from the command line, and then invoking
+     * a static creation method on a "transient service starting class" that
+     * is supplied by the entity that invokes this method. The class supplied
+     * to this method must know how to instantiate the proxy that provides
+     * client-side access to the service instance.
+     * <p>
+     * This method provides a mechanism where a service may be started
+     * through a non-generic, custom-defined, class that knows the
+     * specific start requirements of the desired transient service. The 
+     * arguments input (through the <code>args</code> parameter) 
+     * must satisfy the argument list format for starting such transient,
+     * non-persistent, services.
+     *
+     * @param args             <code>String</code> array containing the
+     *                         arguments (typically retrieved from the command
+     *                         line) to use when starting the service. The
+     *                         arguments contained in this parameter must
+     *                         satisfy the argument list format specified by
+     *                         the <code>StartUtil</code> class. 
+     * @param starterClassName the fully-qualified path.classname of the
+     *                         class that defines a public static
+     *                         <code>create</code> method that this method
+     *                         invokes through reflection in order to start
+     *                         the transient version of the desired service
+     * @param classpath        the classpath from which to load the various
+     *                         classes needed to start the service (this may
+     *                         be different from the classpath from which the
+     *                         the current class was loaded)
+     * @param codebase         the RMI codebase with which to annotate all
+     *                         remote classes that are exported by the 
+     *                         service to be started
+     * @param resourcename     the name used to identify a particular 
+     *                         resource bundle from which usage/error
+     *                         message information may be retrieved for
+     *                         display (helpful with internationalization)
+     * 
+     * @return an instance of the service's interface which is a proxy that
+     *         provides client-side access to the created service
+     * 
+     */
+    public static Object createNonPersistent(String[] args,
+                                             String   starterClassName,
+                                             String   classpath,
+                                             String   codebase,
+                                             String   resourcename) 
+    {
+        /* Parse the input args */
+        StartUtil startUtil = new StartUtil(resourcename);
+        ParsedArgs parsedArgs = startUtil.parseArgs(args);
+        if(parsedArgs == null) {
+            startUtil.printNonPersistentUsage();
+            return null;
+        }
+        try {
+            return doCreateTransient(parsedArgs,starterClassName,
+                                     classpath,codebase,startUtil,
+                                     false);//do not log persistent state
+        } catch(Exception e) {
+            if(!invocationTargetException) e.printStackTrace();
+            return null;
+        }
+    }//end createNonPersistent
+
+    /**
+     * Tests for the existence of the ActivationSystem (RMID). This method
+     * will always make at least one attempt to verify the existence of a
+     * a running ActivationSystem. 
+     *
+     * @param n <code>int</code> value representing the number of additional
+     *          attempts (beyond the initial attempt) to wait for the
+     *          activation system to come up. This values translates 
+     *          directly to the number of seconds to wait for the
+     *          activation system.
+     *
+     * @return <code>true</code> if the activation system is up and running;
+     *         <code>false</code> otherwise
+     */
+    public static boolean activationUp(int n) {
+        /* First attempt */
+        try {
+            ActivationGroup.getSystem();
+            return true;
+        } catch (ActivationException e) { }
+        /* Make a new attempt every second for n seconds */
+        for(int i=0; i<n; i++) {
+            try {
+                ActivationGroup.getSystem();
+                return true;
+            } catch (ActivationException e) { }
+            try {
+                Thread.sleep(1000); // wait 1 second
+            } catch (InterruptedException e) { }
+        }
+        return false;
+    }//end activationUp
+
+    /**
+     * Administratively replaces the current set of groups in which the lookup
+     * service (referenced by the <code>proxy</code> parameter) is a member
+     * with a new set of member groups represented by the group names 
+     * contained in the <code>groups</code> parameter.
+     *
+     * @param proxy   instance of the proxy to the lookup service whose
+     *                current member groups are to be replaced
+     * @param groups  String array whose elements are the names of the 
+     *                member groups with which to replace the current
+     *                member groups of the lookup service. If 
+     *                <code>null</code> is input, no attempt will be
+     *                made to replace the lookup service's current
+     *                set of member groups. 
+     *
+     * @return <code>true</code> if the lookup service's set of member
+     *         groups was successfully replaced; <code>false</code> otherwise.
+     * 
+     * @throws java.rmi.RemoteException typically, this exception occurs when
+     *         there is a communication failure between the proxy and the
+     *         service's backend. When this exception does occur, the
+     *         set of groups to which the lookup service is a member may or
+     *         may not have been successfully replaced.
+     * 
+     * @see net.jini.admin.Administrable
+     * @see net.jini.admin.Administrable#getAdmin
+     * @see net.jini.lookup.DiscoveryAdmin
+     * @see net.jini.lookup.DiscoveryAdmin#setMemberGroups
+     */
+    public static boolean setLookupMemberGroups(ServiceRegistrar proxy,
+                                                String[] groups)
+                                                        throws RemoteException
+    {
+        if( (proxy == null) || (groups == null) ) return false;
+        /* First, test that the lookup service implements both of the
+         * appropriate administration interfaces
+         */
+        DiscoveryAdmin discoveryAdmin = null;
+        if( !(proxy instanceof Administrable) ) return false;
+        Object admin = ((Administrable)proxy).getAdmin();
+        if( !(admin instanceof DiscoveryAdmin) ) return false;
+        discoveryAdmin = (DiscoveryAdmin)admin;
+        /* Set the member groups for the lookup service */
+        discoveryAdmin.setMemberGroups(groups);
+        return true;
+    }//end setLookupMemberGroups
+
+    /**
+     * Administratively replaces - with the respective sets represented by
+     * the <code>groups</code> and <code>locators</code> parameters - the
+     * set of groups and locators the service referenced by the
+     * <code>proxy</code> parameter is currently configured to join.
+     *
+     * @param proxy    instance of the proxy to the service whose current
+     *                 sets of groups and locators to join are to be replaced
+     * @param groups   <code>String</code> array whose elements are the
+     *                 names of the groups with which to replace the
+     *                 service's current set of groups to join
+     * @param locators array containing instances of the class
+     *                 <code>net.jini.core.discovery.LookupLocator</code>,
+     *                 each element corresponding to a specific lookup
+     *                 service the service should join, that will replace
+     *                 the set of locators with which the service is
+     *                 currently configured. If <code>null</code> is
+     *                 input, no attempt will be made to replace the
+     *                 set of locators.
+     *
+     * @return <code>true</code> if this method is successful in replacing
+     *         the appropriate join sets; <code>false</code> otherwise.
+     * 
+     * @throws java.rmi.RemoteException typically, this exception occurs when
+     *         there is a communication failure between the proxy and the
+     *         service's backend. When this exception does occur, the
+     *         set of groups the service should join may or may not have
+     *         been successfully replaced.
+     * 
+     * @see net.jini.core.discovery.LookupLocator
+     * @see net.jini.admin.Administrable
+     * @see net.jini.admin.Administrable#getAdmin
+     * @see net.jini.admin.JoinAdmin
+     */
+    public static boolean setGroupsAndLocators(Object proxy,
+                                               String[] groups,
+                                               LookupLocator[] locators)
+                                                        throws RemoteException
+    {
+        if(proxy == null) return false;
+        /* First, test that the service implements both of the appropriate
+         * administration interfaces
+         */
+        JoinAdmin joinAdmin = null;
+        if( !(proxy instanceof Administrable) ) return false;
+        Object admin = ((Administrable)proxy).getAdmin();
+        if( !(admin instanceof JoinAdmin) ) return false;
+        joinAdmin = (JoinAdmin)admin;
+        /* Set the groups and locators to join */
+        joinAdmin.setLookupGroups(groups);
+        if( (locators != null) && (locators.length > 0) ) {
+            joinAdmin.setLookupLocators(locators);
+        }
+        return true;
+    }//end setGroupsAndLocators
+
+    /**
+     * Creates an activation group suitable for an unshared service to be 
+     * registered with the activation system.
+     *
+     * @param classpath  the classpath for the service to be registered with
+     *                   the activation system
+     * @param codebase   the codebase for the service to be registered with
+     *                   the activation system
+     * @param policyFile the security policy file to apply to the service
+     *                   to be registered with the activation system
+     * @param properties if not <code>null</code>, this argument contains
+     *                   the properties for the activation group in which
+     *                   the service will be registered
+     * @param serverVM  if not <code>null</code>, the contents of this
+     *                   argument represents a path and command that allows
+     *                   parties that invoke this method to specify the
+     *                   VM implementation to use for the execution of the
+     *                   service's backend server. The value of this argument
+     *                   must be an absolute path to an executable entity and
+     *                   that executable entity must accept all of the
+     *                   standard VM command-line options (for example,
+     *                   '-D<property>, -classpath <classpath>', and so on),
+     *                   and directly or indirectly start a VM. Note that
+     *                   although the use of this argument is not necessary
+     *                   in most situations, it is useful for testing the
+     *                   service on alternate VM implementations. 
+     * @param options    if not <code>null</code>, this argument contains
+     *                   the command options for the VM in which the
+     *                   service's backend server executes
+     * 
+     * @throws java.rmi.activation.ActivationException this is a general
+     *         exception thrown by the activation system to indicate a
+     *         a number of exceptional conditions that prevent the
+     *         activation system from fulfilling a particular request.
+     * 
+     * @throws java.rmi.RemoteException typically, this exception occurs when
+     *         there is a communication failure between the activation 
+     *         system's VM and the VM in which this class is instantiated.
+     *         When this exception does occur, the ActivationGroup may
+     *         or may not have been created successfully.
+     */
+    private static ActivationGroupID createGroup(String     classpath,
+                                                 String     codebase,
+                                                 String     policyFile,
+                                                 Properties properties,
+                                                 String     serverVM,
+                                                 String[]   options)
+                                    throws ActivationException, RemoteException
+    {
+        if (properties == null) {
+            properties = new Properties();
+        } else {
+            properties = (Properties)properties.clone();
+        }
+        properties.put("java.security.policy", policyFile);
+        properties.put("java.rmi.server.codebase", codebase);
+        if (options == null) {
+            options = new String[]{"-cp", classpath};
+        } else {
+            String[] nOptions = new String[options.length + 2];
+            System.arraycopy(options, 0, nOptions, 0, options.length);
+            nOptions[options.length] = "-cp";
+            nOptions[options.length + 1] = classpath;
+            options = nOptions;
+        }
+        CommandEnvironment cmdToExecute
+                                  = new CommandEnvironment(serverVM,options);
+        return ActivationGroup.getSystem().registerGroup
+                           (new ActivationGroupDesc(properties,cmdToExecute));
+    }//end createGroup
+
+    /**
+     * Private method which does most of the work involved with starting an
+     * unshared, activatable service as advertised by the public 
+     * <code>create</code> methods of this class.
+     * <p>
+     * Before attempting to register the desired service with the activation
+     * system, this method analyzes the input arguments for illegal or invalid
+     * values, determines if the activation system is running, and determines
+     * if a class server that can serve the appropriate classes is running.
+     * This method then creates an <code>ActivationGroupID</code>, invokes
+     * the <code>create</code> method defined by the class referenced by
+     * the <code>starterClassName</code> parameter, and then sets the
+     * appropriate group and/or locator sets for the service.
+     *
+     * @param parsedArgs       instance of <code>ParsedArgs</code> which is a
+     *                         data structure containing the arguments to use
+     *                         when starting the service. 
+     * @param starterClassName the fully-qualified path.classname of the
+     *                         class that defines the public static
+     *                         <code>create</code> method (and possibly an
+     *                         optional public static <code>createGroup</code>
+     *                         method that this method invokes through
+     *                         reflection in order to start a desired service
+     * @param implClassName    the fully-qualified path.classname of the
+     *                         class that defines the implementation of the
+     *                         backend server of the service that is to be
+     *                         started by this method
+     * @param classpath        the classpath from which to load the various
+     *                         classes needed to start the service (this may
+     *                         be different from the classpath from which the
+     *                         the current class was loaded)
+     * @param startUtil        an instance of the <code>StartUtil</code> class,
+     *                         configured with the appropriate resource bundle
+     *                         information, which is used as a mechanism
+     *                         through which usage/error messages can be
+     *                         displayed
+     *
+     * @return an instance of the service's interface which is a proxy that
+     *         provides client-side access to the created service
+     *
+     * @throws java.rmi.activation.ActivationException this is a general
+     *         exception thrown by the activation system to indicate a
+     *         a number of exceptional conditions that prevent the
+     *         activation system from fulfilling a particular request.
+     * 
+     * @throws java.io.IOException 
+     * 
+     * @see com.sun.jini.compat.start.ParsedArgs
+     * @see com.sun.jini.compat.start.StartUtil
+     */
+    private static Created doCreate(ParsedArgs parsedArgs,
+                                    String     starterClassName,
+                                    String     implClassName,
+                                    String     classpath,
+                                    StartUtil  startUtil)
+                                       throws ActivationException, IOException
+    {
+        /* Do not proceed unless the required parameters are set */
+        String codebase = parsedArgs.getCodebase();
+        if(codebase == null) {
+            startUtil.print("codebase.null", null);
+            return null;
+        }//endif
+        if(parsedArgs.getPolicyFile() == null) {
+            startUtil.print("policy.null", null);
+            return null;
+        }//endif
+        if(parsedArgs.getLogDir() == null) {
+            startUtil.print("logdir.null", null);
+            return null;
+        }//endif
+        /* Do not proceed unless the Activation daemon (RMID) is up */
+        if (!activationUp(10)) {
+            startUtil.print("activation.notUp", null);
+            return null;
+        }//endif
+        /* If there are problems with any of the components of the RMI
+         * codebase parameter, display a warning, but allow the process
+         * to proceed.
+         */
+        HTTPDStatus.httpdStatus(codebase,startUtil);
+        /* Do not proceed unless the starter class can be loaded from the
+         * desired classpath
+         */
+        URLClassLoader newClassLoader = null;
+        Class starterClass = null;
+        try {
+	    // Expand classpath (Fix for Bug #4499358)
+	    File canonicalFile = new File(classpath);
+	    classpath = canonicalFile.getCanonicalPath();
+
+            /* Note that the class loader that is created here is created
+             * with the following characteristics:
+             *   -- parent class loader equal to the "extension class loader"
+             *   -- search path equal to the classpath
+             *   -- getURLs() method that returns the codebase (for annotation)
+             * Doing so will result in a class loader belonging to a delegation
+             * tree that has the following form:
+             *             'bootCL--extensionCL--classpathCL'
+             * That is, there will be no other class loaders between the
+             * extension class loader and the new class loader. This is 
+             * done to guarantee that when creating the desired service,
+             * the classes needed to create that service are loaded from
+             * only the classpath that is input to this method; not from
+             * the "system class loader" which may also have access to
+             * the necessary classes. Loading the classes from two different
+             * class loaders may result in delegation problems (class loader
+             * "interference"). Such problems typically manifest themselves
+             * through exceptions such as ClassNotFoundException.
+             */
+            newClassLoader = ClassLoaderUtil.getCodebaseClassLoader
+                                            (classpath,codebase,
+                                             ClassLoaderUtil.PARENT_EXTENSION);
+            starterClass   = newClassLoader.loadClass(starterClassName);
+        } catch (Exception e) {
+            startUtil.print("starterclass.loadproblem", starterClassName);
+            rethrowException(e);
+        }
+        /* In order to invoke the method(s) of the starter class, the
+         * ContextClassLoader of the current thread must be set to the
+         * new ClassLoader. This is necessary to provide the correct
+         * ClassLoader environment for Remote calls made by the service.
+         * Consider the case where the ContextClassLoader is NOT set 
+         * to the new ClassLoader:
+         *
+         *   When the service makes a Remote call, the URLClassLoader
+         *   created by the RMIClassLoader would have the old ClassLoader
+         *   as its parent. The object state will have been downloaded
+         *   by classes loaded by the new ClassLoader, but the class
+         *   associated with that state will not have the new ClassLoader
+         *   in its classloader delegation tree. As a result, 
+         *   IllegalAccessException will be thrown when RMI attempts to
+         *   unmarshal the remote object.
+         *
+         * After invoking the method(s) of the starter class, the current
+         * thread's ContextClassLoader is set back to its original ClassLoader
+         */
+        Thread curThread = Thread.currentThread();
+        ClassLoader oldClassLoader = curThread.getContextClassLoader();
+        curThread.setContextClassLoader(newClassLoader);
+        ActivationGroupID gid = null;
+	ActivationID aid      = null;
+        Method starterMethod  = null;
+        Object proxy          = null;
+        try {//invocation block
+            /* Create an ActivationGroupID for the desired service. If the
+             * starter class provides its own version of the createGroup()
+             * method, then invoke that method; otherwise, invoke the version
+             * of createGroup() provided in this class
+             */
+            try {//createGroup() block
+                starterMethod = starterClass.getMethod(createGroupMethod,
+		                                       createGroupArgTypes);
+                /* A version of the createGroup() method with the appropriate
+                 * arguments is provided by the starter class. Invoke that
+                 * method with the current values in the parsed arguments
+                 */
+                Object[] methodArgs = { classpath,
+                                        codebase,
+                                        parsedArgs.getPolicyFile(),
+                                        parsedArgs.getProperties(),
+                                        parsedArgs.getJavaProgram(),
+                                        parsedArgs.getOptions() };
+                /* Since method is static, use null for 1st parameter */
+                gid = (ActivationGroupID)starterMethod.invoke(null,methodArgs);
+            } catch (NoSuchMethodException e) {
+                /* Starter class does not provide a createGroup() method, so
+                 * use the one provided in this class
+                 */
+                gid = createGroup(classpath,
+                                  codebase,
+                                  parsedArgs.getPolicyFile(),
+                                  parsedArgs.getProperties(), 
+                                  parsedArgs.getJavaProgram(), 
+                                  parsedArgs.getOptions());
+            } //end try: createGroup() block
+            /* Create the activation descriptor for the desired service */
+            MarshalledObject params
+                              = new MarshalledObject(parsedArgs.getLogDir());
+            ActivationDesc desc = new ActivationDesc(gid, implClassName,
+                                                     null, params, true);
+
+            /* Register the desired service with the activation system */
+	    aid = ActivationGroup.getSystem().registerObject(desc);
+
+            Method m = ClassLoaderUtil.getActivateMethod();
+            Remote serverStub = 
+                (Remote) m.invoke(null, new Object[] { aid });
+		
+            /* Invoke the create() method provided by the starter class */
+            if(serverStub != null) {
+                Class[] argTypes = {Remote.class};
+                starterMethod = starterClass.getMethod("create",argTypes);
+                Object[] methodArgs = { serverStub };
+                proxy = starterMethod.invoke(null,methodArgs);
+            }//endif
+            /* Unlike the case above when the proxy was created using a 
+             * class loader having the extension class loader as its
+             * parent, it is important to return an object that is loaded 
+             * from a class loader that has the codebase (input to this
+             * method) in its search path, and whose parent class loader
+             * is the original "system class loader".
+             * Thus, a new instance of the proxy just created must be created
+             * with a class loader of the form:
+             *
+             * 'bootCL--extCL--parentCL0--...--parentCLn--systemCL--codebaseCL'
+             *
+             * (Note that the "system class loader", sometimes referred to
+             * as the "application class loader", is typically the class
+             * loader used to start the application; that is, the class 
+             * loader that was most likely used to load the class from which
+             * this method is invoked. It is also the default delegation
+             * parent used when creating new instances of ClassLoader.)
+             *
+             * Returning the proxy created above, rather than returning a 
+             * new proxy created using the class loader just described,
+             * can result in class loading problems that may only occur
+             * sometime after the service has been started. Such problems
+             * can occur when interactions with the service cause class
+             * loader "mismatches"; some classes being loaded by a class
+             * loader created by the RMI sub-system, some not.
+             * 
+             * In order return an instance of the desired object with the
+             * appropriate class loader, the following is done:
+             *
+             *   -- the current ContextClassLoader is re-set to the original
+             *      "system class loader"
+             *   -- the original proxy, created with the classpath class
+             *      loader, is marshalled and then un-marshalled; that is,
+             *             (new MarshalledObject(proxy).get())
+             *
+             *      The marshalling process causes the RMI sub-system to
+             *      call the getURLs() method of the original proxy's class
+             *      loader. That method returns the codebase -- not the
+             *      classpath -- since the class loader of the original proxy
+             *      is an instance of CodebaseClassLoader. The RMI sub-system
+             *      then uses the output of the getURLs() method to annotate
+             *      the serialization stream created by the marshalling
+             *      process.
+             *
+             *      The un-marshalling process causes the RMI sub-system to
+             *      create a new URLClassLoader with parent equal to the
+             *      current context class loader (the "system class loader"),
+             *      and with search path equal to the codebase with which the
+             *      serialization stream was annotated (which was the 
+             *      codebase of the original proxy's class loader).
+             *   -- member groups, or join groups and join locators are set
+             *   -- the result of the marshalling/un-marshalling process is
+             *
+             *      Note that the object (proxy) that is returned is
+             *      associated with a class loader that has the codebase
+             *      input to this method as its search path; which is as
+             *      described above.
+             */
+            curThread.setContextClassLoader(oldClassLoader);
+            proxy = (new MarshalledObject(proxy)).get();
+            if(proxy instanceof ServiceRegistrar) {
+                /* Set the groups the new lookup service should belong to */
+                try {
+                    setLookupMemberGroups((ServiceRegistrar)proxy,
+                                          parsedArgs.getGroups());
+                } catch(Exception e) {
+                    startUtil.print("membergroups.problem", null);
+                    /* Remove "zombie" public lookup service */
+                    destroyService(gid,proxy);
+                    rethrowException(e);
+                }
+            } else {
+                /* Set the groups and locators the new service should join */
+                try {
+                    setGroupsAndLocators(proxy,
+                                         parsedArgs.getGroups(),
+                                         parsedArgs.getLocators());
+                } catch(Exception e) {
+                    startUtil.print("joingroupslocs.problem", null);
+                    /* Remove "zombie" service that joins public lookups */
+                    destroyService(gid,proxy);
+                    rethrowException(e);
+                }
+            }//endif
+        } catch(Exception e) {
+            try {
+                ActivationGroup.getSystem().unregisterGroup(gid);
+            } catch (Exception ee) { 
+                // ignore -- did the best we could.  
+            }
+            handleReflectionException(e,starterClassName,"create",startUtil);
+            rethrowException(e);
+        } finally {
+            /* Re-set the current thread's ClassLoader */
+            curThread.setContextClassLoader(oldClassLoader);
+        }//end invocation bloc
+        return new Created(gid, aid, proxy);
+    }//end doCreate
+
+    /**
+     * This method creates an activation group that is intended to host
+     * multiple, shared service instances. It also registers a 
+     * <code>SharedGroup</code> object within that same activation group.
+     * The proxy to this <code>SharedGroup</code> object is returned to the
+     * caller.
+     * <p>
+     * This version of <code>createSharedGroup</code> defaults the classpath
+     * to the current setting of the <code>java.class.path</code> property and
+     * calls the other version of <code>createSharedGroup</code> that takes a 
+     * classpath argument. Classpaths with multiple components are not currently
+     * supported.
+     *
+     * @param args             <code>String[]</code> containing the arguments 
+     *                         used to create the activation group. 
+     * @param starterClassName the fully-qualified classname of the
+     *                         class that defines the public static
+     *                         <code>create</code> method (and optionally 
+     *                         a public static <code>createSharedGroup</code>
+     *                         method) that is invoked through
+     *                         the <code>ServiceStarter</code> framework 
+     *                         in order to create a 
+     *                         <code>SharedGroup</code> service
+     * @param implClassName    the fully-qualified classname of the
+     *                         class that defines the implementation of the
+     *                         <code>SharedGroup</code> service.
+     * @param resourcename     the name used to identify a particular 
+     *                         resource bundle from which usage/error
+     *                         message information may be retrieved for
+     *                         display (helpful with internationalization).
+     *                         Two common values for this parameter are:
+     *                         "lookup" and "service".
+     * 
+     * @return an instance of the <code>SharedGroup</code> interface which 
+     *         is a proxy that
+     *         provides client-side access to the created service (or null
+     *         if there was a problem obtaining the proxy).
+     *
+     * @see com.sun.jini.compat.start.SharedGroup
+     * @see com.sun.jini.compat.start.ServiceStarter
+     */
+    public static Object createSharedGroup(
+	String[]   args,
+        String     starterClassName,
+        String     implClassName,
+        String     resourcename)
+    {
+      
+        String classpath = System.getProperty("java.class.path");
+	boolean restart = true; // fix for bug 4526514 
+        return createSharedGroup(args, starterClassName, implClassName,
+                   classpath, restart, resourcename);
+    }
+    
+    /**
+     * This method creates an activation group that is intended to host
+     * multiple, shared service instances. It also registers a 
+     * <code>SharedGroup</code> object within that same activation group.
+     * The proxy to this <code>SharedGroup</code> object is returned to the
+     * caller.
+     *
+     * @param args             <code>String[]</code> containing the arguments used
+     *                         to create the activation group. 
+     * @param starterClassName the fully-qualified classname of the
+     *                         class that defines the public static
+     *                         <code>create</code> method (and optionally 
+     *                         a public static <code>createSharedGroup</code>
+     *                         method) that is invoked through
+     *                         the <code>ServiceStarter</code> framework 
+     *                         in order to create a 
+     *                         <code>SharedGroup</code> service
+     * @param implClassName    the fully-qualified classname of the
+     *                         class that defines the implementation of the
+     *                         <code>SharedGroup</code> service.
+     * @param classpath        the classpath from which to load the various
+     *                         classes needed to start the service (this may
+     *                         be different from the classpath from which the
+     *                         the current class was loaded). Classpaths with 
+     *                         multiple components are not currently supported.
+     * @param restart          Boolean used to set the <code>restart</code> 
+     *                         parameter of the 
+     *                         <code> java.rmi.activation.ActivationDesc</code>
+     *                         object used to register the group with the
+     *                         activation system. Please see 
+     *                         <code>ActivationDesc</code>'s documentation
+     *                         for details.
+     * @param resourcename     the name used to identify a particular 
+     *                         resource bundle from which usage/error
+     *                         message information may be retrieved for
+     *                         display (helpful with internationalization).
+     *                         Two common values for this parameter are:
+     *                         "lookup" and "service".
+     * 
+     * @return an instance of the <code>SharedGroup</code> interface which 
+     *         is a proxy that
+     *         provides client-side access to the created service (or null
+     *         if there was a problem obtaining the proxy).
+     *
+     * @see com.sun.jini.compat.start.SharedGroup
+     * @see com.sun.jini.compat.start.ServiceStarter
+     */
+    public static Object createSharedGroup(
+	String[]   args,
+        String     starterClassName,
+        String     implClassName,
+        String     classpath,
+	boolean    restart,
+        String     resourcename)
+    {
+        /* Parse the input args */
+        String resource = ((resourcename == null) ? "service" : resourcename);
+        StartUtil startUtil = new StartUtil(resource);
+        ParsedArgs parsedArgs = startUtil.parseActivatableArgs(args);
+        if(parsedArgs == null) {
+            startUtil.print("createGroup.usage", null);
+            return null;
+        }
+        SharedCreated sc = null;
+        try { 
+            sc = doCreateSharedGroup(parsedArgs, starterClassName, 
+                                     implClassName, classpath, restart, startUtil);
+	} catch (ActivationException ae) {
+            startUtil.print("createGroup.problem", ae.toString());
+            if (dbgCreate != null) {
+                ae.printStackTrace(dbgCreate);
+            }
+ 
+	} catch (IOException ioe) {
+            startUtil.print("createGroup.problem", ioe.toString());
+            if (dbgCreate != null) {
+                ioe.printStackTrace(dbgCreate);
+            }
+        }
+
+        Object proxy = null;
+        if (sc != null) {
+	    try {
+                // Note: SharedCreated has a marshalled object representation
+                // of the proxy with the desired class annotations. This
+                // representation will be stored to disk.
+                storeGroupCookie(parsedArgs.getLogDir(), sc);
+
+                // Note: The unmarshalled proxy object may no longer have
+                // have to appropriate class annotations since the required
+                // classes may be found in the classpath (and not dynamically
+                // downloaded.
+                proxy = sc.mProxy.get();
+	    } catch (ClassNotFoundException cnfe) { 
+                startUtil.print("createGroup.cast_problem", cnfe.toString());
+                if (dbgCreate != null) {
+                    cnfe.printStackTrace(dbgCreate);
+                }
+	    } catch (IOException ioe) {
+                startUtil.print("createGroup.store_problem", ioe.toString());
+                if (dbgCreate != null) {
+                    ioe.printStackTrace(dbgCreate);
+                }
+            }
+        } 
+
+        return proxy;
+    }
+
+    /**
+     * Private method which does most of the work involved with starting a
+     * shared activation group.
+     * <p>
+     * Before attempting to register the desired group with the activation
+     * system, this method analyzes the input arguments for illegal or invalid
+     * values, determines if the activation system is running, and determines
+     * if a class server that can serve the appropriate classes is running.
+     * This method then creates an <code>ActivationGroupID</code>, invokes
+     * the <code>create</code> method defined by the class referenced by
+     * the <code>starterClassName</code> parameter, and then returns the
+     * client-side proxy for interacting with the shared activation group
+     * (which was created in that same activation group).
+     *
+     * @param parsedArgs       instance of <code>ParsedArgs</code> which is a
+     *                         data structure containing the arguments to use
+     *                         when starting the shared group service. 
+     * @param starterClassName the fully-qualified classname of the
+     *                         class that defines the public static
+     *                         <code>create</code> method 
+     *                         that this method invokes through
+     *                         reflection in order to start a desired service
+     * @param implClassName    the fully-qualified classname of the
+     *                         class that defines the implementation of the
+     *                         backend server of the service that is to be
+     *                         started by this method
+     * @param classpath        the classpath from which to load the various
+     *                         classes needed to start the service (this may
+     *                         be different from the classpath from which the
+     *                         the current class was loaded)
+     * @param startUtil        an instance of the <code>StartUtil</code> class,
+     *                         configured with the appropriate resource bundle
+     *                         information, which is used as a mechanism
+     *                         through which usage/error messages can be
+     *                         displayed
+     *
+     * @return an instance of <code>SharedCreated</code> which contains 
+     *         a reference to the service's proxy and 
+     *         its associated <code>ActivationGroupID</code>
+     *
+     * @throws java.rmi.activation.ActivationException this is a general
+     *         exception thrown by the activation system to indicate a
+     *         a number of exceptional conditions that prevent the
+     *         activation system from fulfilling a particular request.
+     * 
+     * @throws java.io.IOException if an I/O related problem occured. 
+     * 
+     * @see com.sun.jini.compat.start.ParsedArgs
+     * @see com.sun.jini.compat.start.StartUtil
+     * @see com.sun.jini.compat.start.ActivateWrapper
+     */
+    private static SharedCreated doCreateSharedGroup(
+        ParsedArgs  parsedArgs,
+        String      starterClassName,
+        String      implClassName,
+        String      classpath,
+	boolean     restart,
+        StartUtil   startUtil)
+        throws ActivationException, IOException
+    {
+        /* Do not proceed unless the required parameters are set */
+        String codebase = parsedArgs.getCodebase();
+        if(codebase == null) {
+            startUtil.print("codebase.null", null);
+            return null; 
+        }//endif
+
+        String policy = parsedArgs.getPolicyFile();
+        if(policy == null) {
+            startUtil.print("policy.null", null);
+            return null;
+        }//endif
+
+        String log = parsedArgs.getLogDir();
+        if(log == null) {
+            startUtil.print("logdir.null", null);
+            return null;
+        }//endif    
+
+        /* Do not proceed unless the Activation daemon (RMID) is up */
+        if (!activationUp(10)) {
+            startUtil.print("activation.notUp", null);
+            return null;
+        }//endif
+
+        /* If there are problems with any of the components of the RMI
+         * codebase parameter, display a warning, but allow the process
+         * to proceed.
+         */
+        HTTPDStatus.httpdStatus(codebase,startUtil);
+
+        /* Do not proceed unless the starter class can be loaded from the
+         * desired classpath
+         */
+        URLClassLoader newClassLoader = null;
+        Class starterClass = null;
+        SharedCreated sc = null;
+        try {
+            // See corresponding note in doCreate()
+            newClassLoader = ClassLoaderUtil.getCodebaseClassLoader
+                                            (classpath,codebase,
+                                             ClassLoaderUtil.PARENT_EXTENSION);
+            starterClass   = newClassLoader.loadClass(starterClassName);
+            if (dbgCreate != null) {
+                dbgCreate.println("ServiceStarter::doCreateSharedGroup -"
+                    + " class loader tree for " + starterClass.toString()); 
+	        ClassLoaderUtil.displayClassLoaderTree(
+	            starterClass.getClassLoader());
+            }
+        } catch (Exception e) {
+            startUtil.print("starterclass.loadproblem", starterClassName);
+            if (dbgCreate != null) {
+	        e.printStackTrace(dbgCreate);
+	    }
+            rethrowException(e);
+        }
+
+        // See corresponding note in doCreate()
+        Thread curThread = Thread.currentThread();
+        ClassLoader oldClassLoader = curThread.getContextClassLoader();
+        curThread.setContextClassLoader(newClassLoader);
+        ActivationGroupID gid = null;
+        Method starterMethod  = null;
+        Object proxy          = null;
+
+        try {
+            /* Create an ActivationGroupID for the desired service. If the
+             * starter class provides its own version of the createSharedGroup()
+             * method, then invoke that method; otherwise, invoke the default
+             * default logic provided in this method.
+             */
+            try {// createSharedGroup() block
+
+                Class[] argTypes = { Properties.class,  //properties
+                                     String.class,      //javaProgram cmdPath
+                                     String[].class };  //options (argv)
+                starterMethod = starterClass.getMethod("createSharedGroup",
+						       argTypes);
+
+                /* A version of the createSharedGroup() method with the appropriate
+                 * arguments is provided by the starter class. Invoke that
+                 * method with the current values from the parsed arguments
+                 */
+                Object[] methodArgs = { parsedArgs.getProperties(),
+                                        parsedArgs.getJavaProgram(),
+                                        parsedArgs.getOptions() };
+
+                /* Since method is static, use null for 1st parameter */
+                if (dbgCreate != null) {
+	            dbgCreate.println("ServiceStarter::doCreateSharedGroup -"
+                        + " invoking custom createSharedGroup");
+	        }
+                gid = (ActivationGroupID)starterMethod.invoke(null,methodArgs);
+
+            } catch (NoSuchMethodException e) {
+                /* Starter class does not provide a createSharedGroup() method, so
+                 * use the default logic
+                 */
+                if (dbgCreate != null) {
+	            dbgCreate.println("ServiceStarter::doCreateSharedGroup -"
+                        + " invoking default createSharedGroup");
+	        }
+
+		// User defined properties
+                Properties properties = parsedArgs.getProperties();
+                // Avoid passing null properties
+                if (properties == null) {
+                    properties = new Properties();
+                }
+                
+                // Prepend custom options to any user defined options
+                String[] cmdOpts = customizeOpts(classpath, 
+                                                 parsedArgs.getOptions());
+		/* Note: would really like to setup a custom policy file
+		 * for the activation group, but there's no good way to 
+		 * derive the appropriate 
+		 * <code>SharedActivationPolicyPermission</code>s.
+		 */
+            
+                CommandEnvironment cmdToExecute
+                    = new CommandEnvironment(parsedArgs.getJavaProgram(), 
+                                             cmdOpts);
+                gid = ActivationGroup.getSystem().registerGroup(
+                      new ActivationGroupDesc(properties, cmdToExecute));
+            } //end try createSharedGroup() block 
+
+            /* Create the activation wrapper descriptor for the desired 
+             * "shared" group service */
+            MarshalledObject params = new MarshalledObject(log);
+	    File cp = new File(classpath);
+	    String cpURL = cp.toURL().toString();
+            ActivateWrapper.ActivateDesc adesc = 
+                new ActivateWrapper.ActivateDesc(
+                    implClassName, 
+                    cpURL,
+  		    codebase,
+  		    policy,
+  		    params);
+
+            if (dbgCreate != null) {
+	        dbgCreate.println("ServiceStarter::doCreateSharedGroup -"
+                    + " registering group object with activation system");
+	    }
+	    
+            ActivateWrapper.Created created = 
+		ActivateWrapper.register(gid, adesc, restart);
+            Remote serverStub = (Remote)created.stub;
+
+            /* Invoke the create() method provided by the starter class */
+            if(serverStub != null) {
+                Class[] argTypes = {Remote.class};
+                starterMethod = starterClass.getMethod("create",argTypes);
+                Object[] methodArgs = { serverStub };
+                proxy = starterMethod.invoke(null,methodArgs);
+                if (dbgCreate != null) {
+	            dbgCreate.println("ServiceStarter::doCreateSharedGroup -"
+                        + " class loader tree for " + proxy + ": ");
+	            ClassLoaderUtil.displayClassLoaderTree(
+		        proxy.getClass().getClassLoader());
+	            dbgCreate.println("ServiceStarter::doCreateSharedGroup -"
+                        + " proxy annotation: " 
+		        + java.rmi.server.RMIClassLoader.getClassAnnotation(
+			    proxy.getClass()) );
+	        }
+            }//endif
+

[... 862 lines stripped ...]