/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.start;

import com.sun.jini.config.Config;

import net.jini.config.Configuration;
import net.jini.export.ProxyAccessor;
import net.jini.security.BasicProxyPreparer;
import net.jini.security.ProxyPreparer;
import net.jini.security.policy.DynamicPolicy;
import net.jini.security.policy.DynamicPolicyProvider;
import net.jini.security.policy.PolicyFileProvider;

import java.io.InvalidObjectException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.net.URLClassLoader;
import java.rmi.MarshalledObject;
import java.security.Permission;
import java.security.Policy;
import java.security.AllPermission;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Class used to launch shared, non-activatable, in-process 
 * services. Clients construct this object with the details
 * of the service to be launched, then call 
 * {@link #create(net.jini.config.Configuration) create(Configuration config) }
 * to launch the service in the invoking object's VM.
 * <P>
 * This class provides separation of the import codebase 
 * (where the service implementation classes are loaded from) 
 * from the export codebase 
 * (where service clients should load classes from, for example stubs). 
 * as well as providing an independent security policy file for each 
 * service object. This functionality allows multiple service objects 
 * to be placed in the same VM, with each object maintaining distinct 
 * export codebase and security policy settings. 
 * <P>
 * <a name="serviceConstructor">
 * Services need to implement the following "non-activatable
 * constructor":
 * <blockquote><pre>&lt;impl&gt;(String[] args, {@link LifeCycle LifeCycle} lc)</blockquote></pre>
 * where,
 * <UL>
 * <LI>args - are the service configuration arguments
 * <LI>lc   - is the hosting environment's 
 *     {@link LifeCycle} reference.
 * </UL>
 *
 * <a name="serviceProxy">
 * A service implementation can return its service proxy 
 * (via the <code>proxy</code> field of the 
 * {@link Created Created} object returned by
 * {@link #create(net.jini.config.Configuration) create}) 
 * in the following order of precedence:
 * <OL>
 * <LI> if the service class implements 
 *      {@link ServiceProxyAccessor ServiceProxyAccessor} then return value of 
 *      {@link ServiceProxyAccessor#getServiceProxy() getServiceProxy} 
 *      will be used as the service proxy.
 * <LI> if the service class implements
 *      {@link net.jini.export.ProxyAccessor ProxyAccessor} 
 *       then the return value of 
 *      {@link net.jini.export.ProxyAccessor#getProxy() getProxy} will be used
 *      as the service proxy.
 * <LI> Otherwise <code>null</code> will be returned as the service proxy.
 * </OL>
 *
 * The following items are discussed below:
 * <ul>
 * <li><a href="#configEntries">Configuring NonActivatableServiceDescriptor</a>
 * <li><a href="#logging">Logging</a>
 * </ul>
 *
 * <a name="configEntries">
 * <h3>Configuring NonActivatableServiceDescriptor</h3>
 * </a>
 *
 * <code>NonActivatableServiceDescriptor</code> 
 * depends on {@link ActivateWrapper}, which can itself be configured. See
 * {@linkplain ActivateWrapper}'s 
 * <A HREF="ActivateWrapper.html#configEntries">configuration</A>
 * information for details.
 *<p>
 * This implementation obtains its configuration entries from the 
 * {@link net.jini.config.Configuration Configuration} object passed into 
 * the {@link #create(net.jini.config.Configuration) create} method.
 * The following configuration entries use the
 * component prefix "<code>com.sun.jini.start</code>": <p>
 *
 * <a name="servicePreparer">
 * <table summary="Describes the default service preparer configuration entry"
 *        border="0" cellpadding="2">
 *    <tr valign="top">
 *      <th scope="col" summary="layout"> <font size="+1">&#X2022;</font>
 *      <th scope="col" align="left" colspan="2"> <font size="+1"><code>
 *      servicePreparer</code></font>
 *    <tr valign="top"> <td> &nbsp <th scope="row" align="right">
 *      Type: <td> {@link net.jini.security.ProxyPreparer}
 *    <tr valign="top"> <td> &nbsp <th scope="row" align="right">
 *      Default: <td> <code>
 *        new {@link net.jini.security.BasicProxyPreparer}()
 *        </code>
 *    <tr valign="top"> <td> &nbsp <th scope="row" align="right">
 *      Description: <td> The default proxy preparer used to prepare
 *      the <a href="#serviceProxy">service proxy</a>.
 *      This value should not be <code>null</code>. This entry is
 *      obtained during the invocation of 
 *      {@link #create(net.jini.config.Configuration) create} and is used
 *      to prepare the service proxy returned by 
 *      the service implementation 
 *      (see <a href="#serviceProxy">service proxy</a> section for details).
 *      This entry is superseded by explicitly passing a 
 *      {@link ProxyPreparer ProxyPreparer} to one of the constructors that
 *      accept a {@linkplain ProxyPreparer proxy preparer} argument. 
 *  </table>
 *
 * <a name="logging">
 * <h3>Loggers and Logging Levels</h3>
 * </a>
 *
 * This implementation uses the {@link
 * java.util.logging.Logger}, named 
 * <code>com.sun.jini.start.service.starter</code>. 
 * The following table describes the
 * type of information logged as well as the levels of information logged.
 * <p>
 *
 *  <table border="1" cellpadding="5"
 *	 summary="Describes logging performed by the non-activatable,
 *       service descriptor at different logging levels">
 *
 *  <caption halign="center" valign="top"><b><code>
 *	   com.sun.jini.start.service.starter</code></b></caption>
 *
 *  <tr> <th scope="col"> Level <th scope="col"> Description
 *
 *  <tr> <td> {@link java.util.logging.Level#SEVERE SEVERE} <td> 
 *    for significant service creation problems
 *  <tr> <td> {@link java.util.logging.Level#FINER FINER} <td> 
 *    for high level
 *    service operation tracing
 *  <tr> <td> {@link java.util.logging.Level#FINEST FINEST} <td> 
 *    for low level
 *    service operation tracing
 *
 *  </table> <p>
 * 
 * 
 *
 * @since 2.0
 */
// TODO - add discussion about how to provide a service's proxy

public class NonActivatableServiceDescriptor
    implements ServiceDescriptor, Serializable
{

    private static final long serialVersionUID = 1L;

    /**
     * The parameter types for the "activation constructor".
     */
    private static final Class[] actTypes = {
        String[].class, LifeCycle.class
    };

    /**
     * @serial <code>String</code> containing export codebase. 
     *     That is, location where clients can download required
     *     service-related classes (for example, stubs, proxies, etc.).
     */
    private final String codebase;

    /**
     * @serial <code>String</code> containing server policy filename or URL
     */
    private final String policy;

    /**
     * @serial <code>String</code> containing import codebase location. 
     *     That is, location where server implementation
     *     classes can be found. 
     */
    private final String classpath;

    /**
     * @serial  <code>String</code> containing the 
     *     name of server implementation class
     */
    private final String implClassName;

    /**
     * @serial <code>String[]</code> containing server 
     *     configuration arguments
     */
    private final String[] serverConfigArgs;

    /**
     * serverConfigArgs override.
     */
    private final Configuration configuration ;

    /*
     * <code>LifeCycle</code> reference for hosting environment
     */
    private /*final*/ transient LifeCycle lifeCycle;

    /*
     * <code>ProxyPreparer</code> reference for preparing the service's proxy
     */
    private /*final*/ transient ProxyPreparer servicePreparer;    
    
    /** Flag indicating when create() has been called */
    protected transient boolean descCreated = false;
    
    /** Lock object for <code>descCreated</code> flag */
    protected transient Object descCreatedLock = new Object();
    
    private static LifeCycle NoOpLifeCycle = 
        new LifeCycle() { // default, no-op object
	     public boolean unregister(Object impl) { return false; }
	};

    private static AggregatePolicyProvider globalPolicy = null;
    private static Policy initialGlobalPolicy = null;
    private static final Logger logger = ServiceStarter.logger;

    /** 
     * Object returned by 
     * {@link NonActivatableServiceDescriptor#create(net.jini.config.Configuration) 
     * NonActivatableServiceDescriptor.create()}
     * method that returns the proxy and implementation references
     * for the created service.
     */
    public static class Created {
        /** The reference to the proxy of the created service */
        public final Object proxy;
        /** The reference to the implementation of the created service */
        public final Object impl;
        /** Constructs an instance of this class.
         * @param impl reference to the implementation of the created service
         * @param proxy reference to the proxy of the created service
         */
        public Created(Object impl, Object proxy) {
            this.proxy = proxy;
            this.impl = impl;
        }//end constructor
    }//end class Created

    /**
     * Main constructor. Simply assigns given parameters to 
     * their associated, internal fields unless otherwise noted.
     * 
     * @param exportCodebase location where clients can download required
     *     service-related classes (for example, stubs, proxies, etc.).
     *     Codebase components must be separated by spaces in which 
     *     each component is in <code>URL</code> format. 
     * @param policy server policy filename or URL
     * @param importCodebase location where server implementation
     *     classes can be found. 
     *     This <code>String</code> assumed (in order) to be either
     *     1) a space delimited set of <code>URL</code>(s)
     *     representing a codebase or
     *     2) a <code>File.pathSeparator</code> delimited set 
     *     of class paths.
     * @param implClassName name of server implementation class
     * @param serverConfigArgs service configuration arguments. This parameter
     *     is passed as the <code>String[]</code> argument to the 
     *     implementation's  
     *     <a href="#serviceConstructor">constructor</a>.
     * @param lifeCycle <code>LifeCycle</code> reference
     *     for hosting environment. This parameter
     *     is passed as the <code>LifeCycle</code> argument to the 
     *     implementation's  
     *     <a href="#serviceConstructor">constructor</a>. If this argument
     *     is null, then a default, no-op <code>LifeCycle</code> object will
     *     be assigned.
     * @param preparer <code>ProxyPreparer</code> reference. This object
     *     will be used to prepare the service's proxy object, if any 
     *     (see <a href="#serviceProxy">service proxy</a> section for details).
     *     If this argument is null, then the default 
     *     <a href="#servicePreparer">service preparer</a> will be used.
     */
    public NonActivatableServiceDescriptor(
	// Required Args
	String exportCodebase,
	String policy,
	String importCodebase,
	String implClassName,
	// Optional Args
	String[] serverConfigArgs,
	LifeCycle lifeCycle,
        ProxyPreparer preparer)
    {
        if (exportCodebase == null || policy == null ||
	    importCodebase == null || implClassName == null)
	    throw new NullPointerException(
		"export codebase, policy, import codebase, and"
		+ " implementation cannot be null");
        this.codebase = exportCodebase;
	this.policy = policy;
	this.classpath = importCodebase;
	this.implClassName = implClassName;
	this.serverConfigArgs = serverConfigArgs;
        this.configuration = null ;
	this.lifeCycle =
	    (lifeCycle == null)?NoOpLifeCycle:lifeCycle;
        this.servicePreparer = preparer;    
    }

    public NonActivatableServiceDescriptor(
	// Required Args
	String exportCodebase,
	String policy,
	String importCodebase,
	String implClassName,
	// Optional Args
	Configuration config,
	LifeCycle lifeCycle,
        ProxyPreparer preparer)
    {
        if (exportCodebase == null || policy == null ||
	    importCodebase == null || implClassName == null)
	    throw new NullPointerException(
		"export codebase, policy, import codebase, and"
		+ " implementation cannot be null");
        this.codebase = exportCodebase;
	this.policy = policy;
	this.classpath = importCodebase;
	this.implClassName = implClassName;
	this.serverConfigArgs = null;
        this.configuration = config ;
	this.lifeCycle =
	    (lifeCycle == null)?NoOpLifeCycle:lifeCycle;
        this.servicePreparer = preparer;
    }

    /**
     * Convenience constructor. Equivalent to calling this 
     * {@link #NonActivatableServiceDescriptor(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String[], com.sun.jini.start.LifeCycle, net.jini.security.ProxyPreparer) contructor}
     * with <code>null</code> for the <code>preparer</code> 
     * reference.
     */    
    public NonActivatableServiceDescriptor(
	// Required Args
	String exportCodebase,
	String policy,
	String importCodebase,
	String implClassName,
	// Optional Args
	String[] serverConfigArgs,
	LifeCycle lifeCycle)
    {
        this(exportCodebase, policy, importCodebase, implClassName,
             serverConfigArgs, lifeCycle, null);
    }
    
    /**
     * Convenience constructor. Equivalent to calling this 
     * {@link #NonActivatableServiceDescriptor(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String[], com.sun.jini.start.LifeCycle, net.jini.security.ProxyPreparer) contructor}
     * with <code>null</code> for the <code>lifeCycle</code> and <code>preparer</code> 
     * references.
     */
    public NonActivatableServiceDescriptor(
	// Required Args
	String exportCodebase,
	String policy,
	String importCodebase,
	String implClassName,
	// Optional Args
	String[] serverConfigArgs)
    {
	this(exportCodebase, policy, importCodebase, implClassName, 
	     serverConfigArgs, null, null);
    }
    /**
     * Convenience constructor. Equivalent to calling this 
     * {@link #NonActivatableServiceDescriptor(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String[], com.sun.jini.start.LifeCycle, net.jini.security.ProxyPreparer) contructor}
     * with <code>null</code> for the <code>lifeCycle</code>
     * reference.
     */
    public NonActivatableServiceDescriptor(
	// Required Args
	String exportCodebase,
	String policy,
	String importCodebase,
	String implClassName,
	// Optional Args
	String[] serverConfigArgs,
        ProxyPreparer preparer)
    {
	this(exportCodebase, policy, importCodebase, implClassName, 
	     serverConfigArgs, null, preparer);
    }    
    /**
     * Codebase accessor method.
     *
     * @return the export codebase string associated with this service descriptor.
     */
    final public String getExportCodebase() { return codebase; }
    
    /**
     * Policy accessor method.
     *
     * @return the policy string associated with this service descriptor.
     */
    final public String getPolicy() { return policy; }
    
    /**
     * Classpath accessor method.
     *
     * @return the import codebase string associated with this service descriptor.
     */
    final public String getImportCodebase() { return classpath; }
    
    /**
     * Implementation class accessor method.
     *
     * @return the implementation class string associated with 
     *     this service descriptor.
     */
    final public String getImplClassName() { return implClassName; }
    
    /**
     * Service configuration arguments accessor method.
     *
     * @return the service configuration arguments associated with 
     *     this service descriptor.
     */
    final public String[] getServerConfigArgs() { 
	return (serverConfigArgs != null) 
	        ? (String[])serverConfigArgs.clone()
		: null;
    }
    
    /**
     * <code>LifeCycle</code> accessor method.
     *
     * @return the <code>LifeCycle</code> object associated with 
     * this service descriptor.
     */
    final public LifeCycle getLifeCycle() { return lifeCycle; }

    /**
     * Sets the <code>LifeCycle</code> object for this
     * descriptor. This needs to 
     * be called on the service descriptor prior to calling 
     * <code>create()</code>. Useful for (re-)setting the
     * the associated <code>LifeCycle</code> upon deserialization
     * of this descriptor.
     *
     * @param lc The <code>LifeCycle</code> object to be
     * associated with this service descriptor. 
     *
     * @throws IllegalStateException if called after <code>create()</code> is invoked
     */
     final public void setLifeCycle(LifeCycle lc) { 
        synchronized (descCreatedLock) {
	    if (descCreated) {
	        throw new IllegalStateException("Can't set LifeCycle after descriptor creation");
	    } else {
		lifeCycle = lc; 
	    }
	}
    }

    /**
     * <code>ProxyPreparer</code> accessor method.
     *
     * @return the <code>ProxyPreparer</code> object associated with 
     * this service descriptor.
     */
    final public ProxyPreparer getServicePreparer() { return servicePreparer; }

    /**
     * Sets the <code>ProxyPreparer</code> for this descriptor.
     * This needs to 
     * be called on the service descriptor prior to calling 
     * <code>create()</code>. Useful for (re-)setting the
     * the associated <code>ProxyPreparer</code> upon deserialization 
     * of this descriptor.
     *
     * @param serviceProxyPreparer 
     * The <code>ProxyPreparer</code> object to be
     * associated with this service descriptor. 
     *
     * @throws IllegalStateException if called after <code>create()</code> is invoked
     */
     final public void setServicePreparer(ProxyPreparer serviceProxyPreparer) { 
        synchronized (descCreatedLock) {
	    if (descCreated) {
	        throw new IllegalStateException("Can't set ProxyPreparer after descriptor creation");
	    } else {
		servicePreparer = serviceProxyPreparer; 
	    }
	}
    }
     
    /**
     * Attempts to create a service instance based on the service 
     * description information provided via constructor parameters.
     * <P>
     * This method:
     * <UL>
     * <LI> installs an {@link  java.rmi.RMISecurityManager RMISecurityManager}
     *      if no security manager is already in place
     * <LI> installs an {@link AggregatePolicyProvider AggregatePolicyProvider}
     *      as the VM-global policy object 
     *      (upon the first invocation of this method)
     * <LI> creates an 
     *      <code>ActivateWrapper.ExportClassLoader</code> with
     *      the associated service's import codebase, export codebase and 
     *      the current thread's context class loader as its arguments
     * <LI> associates the newly created 
     *      <code>ExportClassLoader</code> and the associated service's 
     *      policy file with the  
     *      {@link AggregatePolicyProvider AggregatePolicyProvider}
     * <LI> sets the newly created <code>ExportClassLoader</code> as 
     *      the current thread's context class loader
     * <LI> loads the service object's class and calls a constructor 
     *      with the following signature:
     * <blockquote><pre>&lt;impl&gt;(String[], LifeCycle)</blockquote></pre>
     * <LI> obtains the service proxy by calling
     *      {@link ServiceProxyAccessor#getServiceProxy() ServiceProxyAccessor.getServiceProxy()}
     *      or
     *      {@link net.jini.export.ProxyAccessor#getProxy() ProxyAccessor.getProxy()},
     *      respectively, on the implementation instance. 
     *      If neither interface is supported, the 
     *      proxy reference is set to <code>null</code>
     * <LI> resets the context class loader to the  
     *      context classloader in effect when the method was called
     * </UL>
     *
     * @return a 
     * {@link com.sun.jini.start.NonActivatableServiceDescriptor.Created
     * Created} instance with the service's proxy and implementation
     * references.
     * @throws java.lang.Exception Thrown if there was any problem 
     *     creating the object.
     */
    public Object create(Configuration config) throws Exception {
        ServiceStarter.ensureSecurityManager();
        logger.entering(NonActivatableServiceDescriptor.class.getName(),
	    "create", new Object[] {config});
        if (config == null) {
           throw new NullPointerException(
               "Configuration argument cannot be null");
        }
        synchronized (descCreatedLock) {
	    descCreated = true;
	}
	
        if (servicePreparer == null) {
            servicePreparer =
            (ProxyPreparer) Config.getNonNullEntry(config,
                ServiceStarter.START_PACKAGE,
                "servicePreparer", ProxyPreparer.class,
                new BasicProxyPreparer());
        }
            
        /* Warn user of inaccessible codebase(s) */
        HTTPDStatus.httpdWarning(getExportCodebase());

        Object proxy = null;
        Object impl = null;
	//Create custom class loader that preserves codebase annotations
        Thread curThread = Thread.currentThread();
        ClassLoader oldClassLoader = curThread.getContextClassLoader();
        logger.log(Level.FINEST, "Saved current context class loader: {0}",
           oldClassLoader);
        URLClassLoader newClassLoader = null;
        try {
            newClassLoader = 
	        new ActivateWrapper.ExportClassLoader(
		    ClassLoaderUtil.getImportCodebaseURLs(getImportCodebase()),
		    ClassLoaderUtil.getCodebaseURLs(getExportCodebase()),
		    oldClassLoader);
	    logger.log(Level.FINEST, "Created ExportClassLoader: {0}", 
		newClassLoader);
        } catch (IOException ioe) {
            logger.log(Level.SEVERE, "classloader.problem", 
	        new Object[] {getImportCodebase(), getExportCodebase()});
            throw ioe;
        } 
	    
	synchronized (NonActivatableServiceDescriptor.class) {
	    // supplant global policy 1st time through
	    if (globalPolicy == null) { 
		initialGlobalPolicy = Policy.getPolicy();
                if (!(initialGlobalPolicy instanceof DynamicPolicy)) {
                    initialGlobalPolicy = 
                        new DynamicPolicyProvider(initialGlobalPolicy);
                }
		globalPolicy = 
		    new AggregatePolicyProvider(initialGlobalPolicy);
		Policy.setPolicy(globalPolicy);
		logger.log(Level.FINEST,
		    "Global policy set: {0}", globalPolicy);
	    }
	    
	    Policy service_policy =
		ActivateWrapper.getServicePolicyProvider(
		     new PolicyFileProvider(getPolicy()));
	    Policy backstop_policy =
		ActivateWrapper.getServicePolicyProvider(initialGlobalPolicy);
	    LoaderSplitPolicyProvider split_service_policy =
		new LoaderSplitPolicyProvider(
		    newClassLoader, service_policy, backstop_policy);
	    /* Grant "this" code enough permission to do its work
	     * under the service policy, which takes effect (below)
	     * after the context loader is (re)set.
	     * Note: Throws UnsupportedOperationException if dynamic grants
	     * aren't supported (because underlying policies don't support it).
	     */
	    split_service_policy.grant(
		this.getClass(),
		null, /* Principal[] */
		new Permission[] { new AllPermission() } );
	    globalPolicy.setPolicy(newClassLoader, split_service_policy);
 	}
    
        logger.finest("Attempting to get implementation class");
        Class implClass = null;
        implClass = 	
            Class.forName(getImplClassName(), false, newClassLoader);
        logger.finest("Setting context class loader");
        curThread.setContextClassLoader(newClassLoader);

	try {
            logger.finest("Attempting to get implementation constructor");

            Object argParms[] = new Object[]{getServerConfigArgs(), lifeCycle} ;
            Class argTypes[] = actTypes ;

            if( configuration != null ) {
                if( getServerConfigArgs() != null ) {
                    logger.severe("both configArgs and configuration specified, using configuration");
                }
                argParms = new Object[]{configuration, lifeCycle} ;
                argTypes = new Class[]{Configuration.class,  LifeCycle.class }; // TODO:make static
            }

	    Constructor constructor =
                implClass.getDeclaredConstructor(argTypes);
            logger.log(Level.FINEST,
                "Obtained implementation constructor: {0}",
                constructor);
            constructor.setAccessible(true);
            impl = constructor.newInstance(argParms);
            
            logger.log(Level.FINEST,
                "Obtained implementation instance: {0}", impl);
            if (impl instanceof ServiceProxyAccessor) {
                proxy = ((ServiceProxyAccessor)impl).getServiceProxy();
	    } else if (impl instanceof ProxyAccessor) {
                proxy = ((ProxyAccessor)impl).getProxy();
	    } else { 
		proxy = null; // just for insurance
	    }
	                
            logger.log(Level.FINEST, "Proxy =  {0}", proxy);
	    curThread.setContextClassLoader(oldClassLoader);
//TODO - factor in code integrity for MO
            proxy = (new MarshalledObject(proxy)).get();
	} finally {
	    curThread.setContextClassLoader(oldClassLoader);
	}
        
        if(proxy != null) {
            proxy = servicePreparer.prepareProxy(proxy);
        }
	Created created = new Created(impl, proxy);   
        logger.exiting(NonActivatableServiceDescriptor.class.getName(), 
	    "create", created);
        return created;
    }

    /** Prints out a field summary */
    public String toString() {
        ArrayList fields = new ArrayList(7);
	fields.add(codebase);
	fields.add(policy);
	fields.add(classpath);
	fields.add(implClassName);
	fields.add(
            ((serverConfigArgs == null) 
                ? null : Arrays.asList(serverConfigArgs)));
	fields.add(lifeCycle);
        fields.add(servicePreparer);
        return fields.toString();
    }
    
    /**
     * Reads the default serializable field values for this object
     * and resets the tranisient fields to legal values.  
     * Also, verifies that the deserialized values are legal.
     */
    private void readObject(ObjectInputStream in) 
        throws IOException, ClassNotFoundException 
    {
        in.defaultReadObject();
	// Verify that serialized fields aren't null
	if (codebase == null) {
	    throw new InvalidObjectException("null export codebase");
	}
	if (policy == null) {
	    throw new InvalidObjectException("null policy");
	}
	if (classpath == null) {
	    throw new InvalidObjectException("null import codebase");
	}
	if (implClassName == null) {
	    throw new InvalidObjectException("null implementation class name");
	}

	//Reinitialize transient fields upon de-serialization.
        lifeCycle = NoOpLifeCycle;
        servicePreparer = null;
        descCreated = false;
        descCreatedLock = new Object();
    }
    
    /**
     * Throws InvalidObjectException, since data for this class is required.
     */
    private void readObjectNoData() throws ObjectStreamException {
	throw new InvalidObjectException("no data");
    }
} 

