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

import java.lang.reflect.Method;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.rmi.MarshalledObject;

import net.jini.core.entry.Entry;
import net.jini.core.event.RemoteEventListener;
import net.jini.core.transaction.Transaction;
import net.jini.space.JavaSpace;
import net.jini.admin.Administrable;

import net.jini.id.Uuid;
import net.jini.core.constraint.MethodConstraints;
import net.jini.core.constraint.RemoteMethodControl;
import net.jini.security.proxytrust.ProxyTrustIterator;
import net.jini.security.proxytrust.SingletonProxyTrustIterator;
import com.sun.jini.proxy.ConstrainableProxyUtil;

/**
 * Constrainable subclass of <code>SpaceProxy</code>
 */
final class ConstrainableSpaceProxy extends SpaceProxy
    implements RemoteMethodControl 
{
    static final long serialVersionUID = 2L;

    /**
     * Array containing element pairs in which each pair of elements
     * represents a mapping between two methods having the following
     * characteristics:
     * <ul>
     * <li> the first element in the pair is one of the public, remote
     *      method(s) that may be invoked by the client through 
     *      <code>SpaceProxy</code>.
     * <li> the second element in the pair is the method, implemented
     *      in the backend server class, that is ultimately executed in
     *      the server's backend when the client invokes the corresponding
     *      method in this proxy.
     * </ul>
     */
    private static final Method[] methodMapArray =  {
	ProxyUtil.getMethod(Administrable.class, "getAdmin", new Class[] {}),
	ProxyUtil.getMethod(Administrable.class, "getAdmin", new Class[] {}),

	ProxyUtil.getMethod(JavaSpace.class, "write", 
			    new Class[] {Entry.class,
					 Transaction.class,
					 long.class}),
	ProxyUtil.getMethod(OutriggerServer.class, "write",
			    new Class[] {EntryRep.class,
					 Transaction.class,
					 long.class}),


	ProxyUtil.getMethod(JavaSpace.class, "read", 
			    new Class[] {Entry.class,
					 Transaction.class,
					 long.class}),
	ProxyUtil.getMethod(OutriggerServer.class, "read",
			    new Class[] {EntryRep.class,
					 Transaction.class,
					 long.class,
					 OutriggerServer.QueryCookie.class}),


	ProxyUtil.getMethod(JavaSpace.class, "take", 
			    new Class[] {Entry.class,
					 Transaction.class,
					 long.class}),
	ProxyUtil.getMethod(OutriggerServer.class, "take",
			    new Class[] {EntryRep.class,
					 Transaction.class,
					 long.class,
					 OutriggerServer.QueryCookie.class}),


	ProxyUtil.getMethod(JavaSpace.class, "readIfExists", 
			    new Class[] {Entry.class,
					 Transaction.class,
					 long.class}),
	ProxyUtil.getMethod(OutriggerServer.class, "readIfExists",
			    new Class[] {EntryRep.class,
					 Transaction.class,
					 long.class,
					 OutriggerServer.QueryCookie.class}),


	ProxyUtil.getMethod(JavaSpace.class, "takeIfExists", 
			    new Class[] {Entry.class,
					 Transaction.class,
					 long.class}),
	ProxyUtil.getMethod(OutriggerServer.class, "takeIfExists",
			    new Class[] {EntryRep.class,
					 Transaction.class,
					 long.class,
					 OutriggerServer.QueryCookie.class}),


	ProxyUtil.getMethod(JavaSpace.class, "notify", 
			    new Class[] {Entry.class,
					 Transaction.class,
					 RemoteEventListener.class,
					 long.class,
					 MarshalledObject.class}),
	ProxyUtil.getMethod(OutriggerServer.class, "notify",
			    new Class[] {EntryRep.class,
					 Transaction.class,
					 RemoteEventListener.class,
					 long.class,
					 MarshalledObject.class})
    };//end methodMapArray

    /** 
     * Client constraints placed on this proxy (may be <code>null</code> 
     * @serial
     */
    private final MethodConstraints methodConstraints;
    
    /**
     * Create a new <code>ConstrainableSpaceProxy</code>.
     * @param space The <code>OutriggerServer</code> for the 
     *              space.
     * @param spaceUuid The universally unique ID for the
     *              space
     * @param serverMaxServerQueryTimeout The value this proxy
     *              should use for the <code>maxServerQueryTimeout</code>
     *              if no local value is provided.
     * @param methodConstraints the client method constraints to place on
     *                          this proxy (may be <code>null</code>).
     * @throws NullPointerException if <code>space</code> or
     *         <code>spaceUuid</code> is <code>null</code>.
     * @throws IllegalArgumentException if 
     *         <code>serverMaxServerQueryTimeout</code> is not
     *         larger than zero.     
     * @throws ClassCastException if <code>server</code>
     *         does not implement <code>RemoteMethodControl</code>.
     */
    ConstrainableSpaceProxy(OutriggerServer space, Uuid spaceUuid, 
			    long serverMaxServerQueryTimeout, 
			    MethodConstraints methodConstraints)
    {
	super(constrainServer(space, methodConstraints),
	      spaceUuid, serverMaxServerQueryTimeout);
	this.methodConstraints = methodConstraints;
    }

    /**
     * Returns a copy of the given <code>OutriggerServer</code> proxy
     * having the client method constraints that result after
     * mapping defined by methodMapArray is applied.
     * @param server The proxy to attach constrains too.
     * @param constraints The source method constraints.
     * @throws NullPointerException if <code>server</code> is 
     *         <code>null</code>.
     * @throws ClassCastException if <code>server</code>
     *         does not implement <code>RemoteMethodControl</code>.
     */
    private static OutriggerServer constrainServer(OutriggerServer server,
        MethodConstraints constraints)
    {
	final MethodConstraints serverRefConstraints 
	    = ConstrainableProxyUtil.translateConstraints(constraints,
							  methodMapArray);
	final RemoteMethodControl constrainedServer = 
	    ((RemoteMethodControl)server).
	    setConstraints(serverRefConstraints);

	return (OutriggerServer)constrainedServer;
    }

    public RemoteMethodControl setConstraints(MethodConstraints constraints)
    {
	return new ConstrainableSpaceProxy(space, spaceUuid, 
					   serverMaxServerQueryTimeout,
					   constraints);
    }

    public MethodConstraints getConstraints() {
	return methodConstraints;
    }

    /** 
     * 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(space);
    }

    private void readObject(ObjectInputStream s)  
	throws IOException, ClassNotFoundException
    {
	s.defaultReadObject();

	/* Basic validation of space, spaceUuid, and 
	 * serverMaxServerQueryTimeout was performed by
	 * SpaceProxy.readObject(), we just need to verify than
	 * space implements RemoteMethodControl and that it has
	 * appropriate constraints. 
	 */
	ConstrainableProxyUtil.verifyConsistentConstraints(
	    methodConstraints, space, methodMapArray);
    }
}

