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 ...]