/*
 * 
 * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * The contents of this file are subject to the Sun Community Source
 * License v3.0/Jini Technology Specific Attachment v 1.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://wwws.sun.com/software/jini/licensing/SCSL3_JiniTSA1.html.
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Reference Code is Jini Technology Core Platform code, v2.0. The
 * Developer of the Reference Code is Sun Microsystems, Inc.
 * 
 * Contributor(s): Sun Microsystems, Inc.
 * 
 * The contents of this file comply with the Jini Technology Core
 * Platform Compatibility Kit, v2.0A.
 * 
 * Tester(s): Sun Microsystems, Inc.
 * 
 * Test Platform(s):
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Solaris(TM)SPARC/x86
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Linux (Intel x86)
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Windows (Intel Platform)
 * 
 */

package com.sun.jini.example.hello;

import java.io.Serializable;
import java.rmi.MarshalledObject;
import java.rmi.RemoteException;
import java.rmi.activation.ActivationDesc;
import java.rmi.activation.ActivationGroup;
import java.rmi.activation.ActivationGroupDesc.CommandEnvironment;
import java.rmi.activation.ActivationGroupDesc;
import java.rmi.activation.ActivationGroupID;
import java.rmi.activation.ActivationID;
import java.rmi.activation.ActivationSystem;
import java.security.PrivilegedExceptionAction;
import java.util.Properties;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import net.jini.config.Configuration;
import net.jini.config.ConfigurationException;
import net.jini.config.ConfigurationProvider;
import net.jini.core.lookup.ServiceID;
import net.jini.export.Exporter;
import net.jini.security.BasicProxyPreparer;
import net.jini.security.ProxyPreparer;

/**
 * Defines an activatable application server that provides an implementation of
 * the Hello interface.
 *
 * The application uses the following arguments:
 *
 * [all] - All arguments are used as options when getting the configuration
 *
 * The application uses the following configuration entries, with component
 * com.sun.jini.example.hello.Server:
 *
 * activationIdPreparer
 *   type: ProxyPreparer
 *   default: new BasicProxyPreparer()
 *   Proxy preparer for the service proxy's activation ID
 *
 * activationSystemPreparer
 *   type: ProxyPreparer
 *   default: new BasicProxyPreparer()
 *   Proxy preparer for the activation system proxy
 *
 * configOptions
 *   type: String[]
 *   default: none
 *   The options for the configuration used to configure the server
 *
 * discoveryManager
 *   type: DiscoveryManagement
 *   default: new LookupDiscovery(new String[] { "" }, config)
 *   Object used to discover lookup services to join.
 *
 * exporter
 *   type: Exporter
 *   default: none
 *   The object to use for exporting the server
 *
 * loginContext
 *   type: LoginContext
 *   default: null
 *   If non-null, specifies the JAAS login context to use for performing a JAAS
 *   login and supplying the Subject to use when starting the service. If null,
 *   no JAAS login is performed.
 *
 * javaOptions
 *   type: String[]
 *   default: new String[0]
 *   Command line options for the activation group executable
 *
 * javaProgram
 *   type: String
 *   default: null
 *   The executable for the activation group, or the default executable if null
 *
 * javaProperties
 *   type: String[]
 *   default: new String[0]
 *   System properties for the activation group executable, specified as pairs
 *   of keys and values
 *
 * @author Sun Microsystems, Inc.
 */
public class ActivatableServer extends Server {

    /** The activation ID for this server */
    private ActivationID aid;

    /** The service ID for this server */
    private final ServiceID serviceID;

    /**
     * Starts and registers an activatable server that implements the Hello
     * interface.
     */
    public static void main(String[] args) throws Exception {
	final Configuration config = ConfigurationProvider.getInstance(args);
	LoginContext loginContext = (LoginContext) config.getEntry(
	    "com.sun.jini.example.hello.Server", "loginContext",
	    LoginContext.class, null);
	if (loginContext == null) {
	    mainAsSubject(config);
	} else {
	    loginContext.login();
	    Subject.doAsPrivileged(
		loginContext.getSubject(),
		new PrivilegedExceptionAction() {
		    public Object run() throws Exception {
			mainAsSubject(config);
			return null;
		    }
		},
		null);
	}
    }

    /** Starts the server with the subject already set. */
    static void mainAsSubject(Configuration config) throws Exception {
	/* Get Java program */
	String program = (String) config.getEntry(
	    "com.sun.jini.example.hello.Server", "javaProgram",
	    String.class, null);

	/* Get options for Java program */
	String[] options = (String[]) config.getEntry(
	    "com.sun.jini.example.hello.Server", "javaOptions", String[].class,
	    new String[0]);

	/* Create command environment */
	CommandEnvironment cmd = new CommandEnvironment(program, options);

	/* Get system properties for Java program */
	String[] propValues = (String[]) config.getEntry(
	    "com.sun.jini.example.hello.Server", "javaProperties",
	    String[].class, new String[0]);
	Properties props = new Properties();
	for (int i = 0; i < propValues.length; i += 2) {
	    props.setProperty(propValues[i], propValues[i + 1]);
	}

	/* Create group description */
	ActivationGroupDesc groupDesc = new ActivationGroupDesc(props, cmd);

	/* Create the activation data -- configuration source and service ID */
	MarshalledObject data = new MarshalledObject(
	    new ActivationData(
		(String[]) config.getEntry(
		    "com.sun.jini.example.hello.Server", "configOptions",
		    String[].class),
		createServiceID()));

	/* Get the activation system */
	ProxyPreparer actSysPreparer = (ProxyPreparer) config.getEntry(
	    "com.sun.jini.example.hello.Server", "activationSystemPreparer",
	    ProxyPreparer.class, new BasicProxyPreparer());
	ActivationSystem actSys = 
	    (ActivationSystem) actSysPreparer.prepareProxy(
		ActivationGroup.getSystem());

	/* Create the activation group */
	ActivationGroupID gid = actSys.registerGroup(groupDesc);

	/* Create the activation descriptor */
	ActivationDesc actDesc =
	    new ActivationDesc(
		gid, ActivatableServer.class.getName(),
		null /* location */, data, true /* restart */);

	/* Register the activation descriptor */
	ProxyPreparer actIdPreparer = (ProxyPreparer) config.getEntry(
	    "com.sun.jini.example.hello.Server", "activationIdPreparer",
	    ProxyPreparer.class, new BasicProxyPreparer());
	ActivationID aid = (ActivationID) actIdPreparer.prepareProxy(
	    actSys.registerObject(actDesc));

	/* Activate the server */
	aid.activate(true);

	System.out.println("Activated server");
    }

    /**
     * Creates the server
     *
     * @param aid the activation ID for the server
     * @param data the data for the activatable server, which should be the
     *        options to use when getting the Configuration
     * @throws Exception if a problem occurs
     */
    public ActivatableServer(ActivationID aid, MarshalledObject data) 
        throws Exception
    {
        super(((ActivationData) data.get()).configOptions);
        this.aid = aid;
	serviceID = ((ActivationData) data.get()).serviceID;
        init();
    }

    /**
     * Stores the configuration options and service ID for the activatable
     * server.
     */
    static class ActivationData implements Serializable {
	private static final long serialVersionUid = 1;
	final String[] configOptions;
	final ServiceID serviceID;

	ActivationData(String[] configOptions, ServiceID serviceID) {
	    this.configOptions = configOptions;
	    this.serviceID = serviceID;
	}
    }

    /**
     * Returns the exporter for exporting the server. Prepares the activation
     * ID and makes it available to the exporter through the getActivationID
     * method.
     *
     * @throws ConfigurationException if a problem occurs getting the exporter
     *	       from the configuration
     * @throws RemoteException if a remote communication problem occurs
     */
    protected Exporter getExporter()
	throws ConfigurationException, RemoteException
    {
	/* Prepare the activation ID */
	ProxyPreparer actIdPreparer = (ProxyPreparer) config.getEntry(
	    "com.sun.jini.example.hello.Server", "activationIdPreparer",
	    ProxyPreparer.class, new BasicProxyPreparer());
	aid = (ActivationID) actIdPreparer.prepareProxy(aid);

	/* Provide the activation ID to the exporter */
	return (Exporter) config.getEntry(
	    "com.sun.jini.example.hello.Server", "exporter", Exporter.class,
	    Configuration.NO_DEFAULT, aid);
    }

    /** Returns the service ID for this server. */
    protected ServiceID getServiceID() {
	return serviceID;
    }
}
