/*
 * 
 * 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.mercury;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.rmi.RemoteException;

import javax.security.auth.Subject;

import com.sun.jini.landlord.ConstrainableLandlordLease;
import com.sun.jini.proxy.ConstrainableProxyUtil; 
import com.sun.jini.proxy.ThrowThis;
import net.jini.core.constraint.MethodConstraints;
import net.jini.core.constraint.RemoteMethodControl;
import net.jini.id.ReferentUuid;
import net.jini.id.ReferentUuids;
import net.jini.id.Uuid;
import net.jini.security.proxytrust.ProxyTrustIterator;
import net.jini.security.proxytrust.SingletonProxyTrustIterator;
import net.jini.security.TrustVerifier;

import net.jini.event.MailboxRegistration;
import net.jini.core.event.RemoteEventListener;
import net.jini.core.lease.Lease;

/**
 * The <tt>Registration</tt> class is the client-side proxy
 * returned to event mailbox clients as the result of the 
 * registration process. It implements the <tt>MailboxRegistration</tt>
 * interface and delegates functionality to the mailbox service
 * where necessary.
 *
 * @author Sun Microsystems, Inc.
 *
 * @since 1.1
 */ 
class Registration implements MailboxRegistration, Serializable, ReferentUuid {

    private static final long serialVersionUID = 2L;

    /** Unique identifier for this registration */
    final Uuid registrationID;

    /** Reference to service implementation */
    final MailboxBackEnd mailbox;

    /** Reference to service provided RemoteEventListener implementation */
    final ListenerProxy listener;

    /** The service's registration lease */ 
    final Lease lease;
 
    /**
     * Creates a mailbox registration proxy, returning an instance 
     * that implements RemoteMethodControl if the server does too.
     *
     * @param server the server proxy
     * @param id the ID of the lease set
     * @param lease the lease set's lease
     */
    static Registration create(Uuid id, MailboxBackEnd server, Lease lease) {
	if (server instanceof RemoteMethodControl) {
	    return new ConstrainableRegistration(id, server, lease, null);
	} else {
	    return new Registration(id, server, lease);
	}
    }

    /** Convenience constructor */
    private Registration(Uuid id, MailboxBackEnd srv, Lease l) {
        if (id == null || srv == null || l == null)
            throw new IllegalArgumentException("Cannot accept null arguments");
        registrationID = id;
        mailbox = srv;
        listener = ListenerProxy.create(id, srv);
        lease = l;
    }

    // inherit javadoc from supertype
    public Lease getLease() {
	return lease;
    }
    
    // inherit javadoc from supertype
    public RemoteEventListener getListener() {
	return listener;
    }

    // inherit javadoc from supertype
    public void enableDelivery(RemoteEventListener target) 
	throws RemoteException
    {
        // Prevent resubmission of this registration's listener
	if ((target instanceof ListenerProxy) &&
	    (listener.equals((ListenerProxy)target))) {
	    throw new IllegalArgumentException("Cannot resubmit " +
		"a target that was provided by the EventMailbox service");
	} else { // OK to make the call, now
	    try {
	        mailbox.enableDelivery(registrationID, target);
	    } catch (ThrowThis tt) { 
	        tt.throwRemoteException();
	    }
	}
    }

    // inherit javadoc from supertype
    public void disableDelivery() throws RemoteException {
	try { 
            mailbox.disableDelivery(registrationID);
	} catch (ThrowThis tt) { 
	    tt.throwRemoteException();
	}
    }

    /* From net.jini.id.ReferentUuid */
    /**
     * Returns the universally unique identifier that has been assigned to the
     * resource this proxy represents.
     *
     * @return the instance of <code>Uuid</code> that is associated with the
     *         resource this proxy represents. This method will not return
     *         <code>null</code>.
     *
     * @see net.jini.id.ReferentUuid
     */
    public Uuid getReferentUuid() {
        return registrationID;
    }

    /** Proxies with the same registrationID have the same hash code. */
    public int hashCode() {
        return registrationID.hashCode();
    }

    /** Proxies with the same registrationID are considered equal. */
    public boolean equals(Object o) {
        return ReferentUuids.compare(this,o);
    }

   /** When an instance of this class is deserialized, this method is
     *  automatically invoked. This implementation of this method validates
     *  the state of the deserialized instance.
     *
     * @throws <code>InvalidObjectException</code> if the state of the
     *         deserialized instance of this class is found to be invalid.
     */
    private void readObject(ObjectInputStream s)
                               throws IOException, ClassNotFoundException
    {
        s.defaultReadObject();
        /* Verify server */
        if(mailbox == null) {
            throw new InvalidObjectException("Registration.readObject "
                                             +"failure - mailbox "
                                             +"field is null");
        }//endif
        /* Verify registrationID */
        if(registrationID == null) {
            throw new InvalidObjectException
                                  ("Registration.readObject "
                                   +"failure - registrationID field is null");
        }//endif
        /* Verify regLease */
        if(lease == null) {
            throw new InvalidObjectException
                                        ("Registration.readObject "
                                         +"failure - lease field is null");
        }//endif
        /* Verify listener */
        if(listener == null) {
            throw new InvalidObjectException
                                        ("Registration.readObject "
                                         +"failure - listener field is null");
        }//endif
    }//end readObject

    /** During deserialization of an instance of this class, if it is found
     *  that the stream contains no data, this method is automatically
     *  invoked. Because it is expected that the stream should always
     *  contain data, this implementation of this method simply declares
     *  that something must be wrong.
     *
     * @throws <code>InvalidObjectException</code> to indicate that there
     *         was no data in the stream during deserialization of an
     *         instance of this class; declaring that something is wrong.
     */
    private void readObjectNoData() throws ObjectStreamException {
        throw new InvalidObjectException("no data found when attempting to "
                                         +"deserialize Registration instance");
    }//end readObjectNoData

    /** A subclass of Registration that implements RemoteMethodControl. */
    final static class ConstrainableRegistration extends Registration 
        implements RemoteMethodControl 
    {
	private static final long serialVersionUID = 1L;

	// Mappings from client to server methods, 
	private static final Method[] methodMap1 = {
	    ProxyUtil.getMethod(MailboxRegistration.class,
	        "enableDelivery", new Class[] {RemoteEventListener.class}),
	    ProxyUtil.getMethod(MailboxBackEnd.class, 
	        "enableDelivery", new Class[] {Uuid.class, 
		RemoteEventListener.class}), 
	    ProxyUtil.getMethod(MailboxRegistration.class,
	        "disableDelivery", new Class[] {}),
	    ProxyUtil.getMethod(MailboxBackEnd.class, 
	        "disableDelivery", new Class[] {Uuid.class})
	};
	/**
	 * The client constraints placed on this proxy or <code>null</code>.
	 *
	 * @serial
	 */
	private MethodConstraints methodConstraints;

	/** Creates an instance of this class. */
	private ConstrainableRegistration(Uuid id, MailboxBackEnd server,
            Lease lease, MethodConstraints methodConstraints)
	{
	    super(id, constrainServer(server, methodConstraints),
	          lease);
	    this.methodConstraints = methodConstraints;
	}
	
	/**
	 * Returns a copy of the server proxy with the specified client
	 * constraints and methods mapping.
	 */
	private static MailboxBackEnd constrainServer(
	    MailboxBackEnd server,
	    MethodConstraints methodConstraints)
	{
	    return (MailboxBackEnd) 
	        ((RemoteMethodControl)server).setConstraints(
		    ConstrainableProxyUtil.translateConstraints(
		        methodConstraints, methodMap1));
	}
	/** {@inheritDoc} */
	public RemoteMethodControl setConstraints(
	    MethodConstraints constraints)
	{
	    return new ConstrainableRegistration(registrationID, mailbox, lease, 
	        constraints);
	}

	/** {@inheritDoc} */
	public MethodConstraints getConstraints() {
	    return methodConstraints;
	}

        /* Note that the superclass's hashCode method is OK as is. */
        /* Note that the superclass's equals method is OK as is. */

	/* Note that the superclass's hashCode method is OK as is. */
        /**
         * Returns a proxy trust iterator that is used in
         * <code>ProxyTrustVerifier</code> to retrieve this object's
         * trust verifier.
         */
        private ProxyTrustIterator getProxyTrustIterator() {
            return new SingletonProxyTrustIterator(mailbox);
        }//end getProxyTrustIterator

	/**
	 * Verifies that the registrationID, lease and mailbox fields are 
	 * not null, that mailbox implements RemoteMethodControl, and that the 
	 * mailbox proxy has the appropriate method constraints.
	 *
	 * @throws InvalidObjectException if registrationID, lease or mailbox
	 *         is null, if mailbox does not implement RemoteMethodControl, 
	 *         or if server has the wrong constraints
	 */
	private void readObject(ObjectInputStream s)
	    throws IOException, ClassNotFoundException
	{
            /* Note that basic validation of the fields of this class was
             * already performed in the readObject() method of this class'
             * super class.
             */
            s.defaultReadObject();
            /* Verify the server and its constraints */
            ConstrainableProxyUtil.verifyConsistentConstraints(methodConstraints,
                                                        mailbox,
                                                        methodMap1);
            if( !(lease instanceof ConstrainableLandlordLease) ) {
                throw new InvalidObjectException
                                ("Registration.readObject failure - "
                                 +"lease is not an instance of "
                                 +"ConstrainableLandlordLease");
            }//endif

            if( !(listener instanceof ListenerProxy.ConstrainableListenerProxy) ) {
                throw new InvalidObjectException
                                ("Registration.readObject failure - "
                                 +"listener is not an instance of "
                                 +"ListenerProxy.ConstrainableListenerProxy");
            }//endif

            /* Verify listener's ID */
            if(registrationID !=
	       ((ListenerProxy.ConstrainableListenerProxy)listener).registrationID) 
            {
                throw new InvalidObjectException
                                        ("Registration.readObject "
                                         +"failure - listener ID "
                                         +"is not equal to "
                                         +"proxy ID");
            }            
	}
    }
}
