/* * 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.fiddler; import com.sun.jini.fiddler.Fiddler; import com.sun.jini.fiddler.ProxyUtil; import com.sun.jini.proxy.ConstrainableProxyUtil; import net.jini.admin.Administrable; import net.jini.discovery.LookupDiscoveryService; import net.jini.discovery.LookupDiscoveryRegistration; 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.core.constraint.MethodConstraints; import net.jini.core.constraint.RemoteMethodControl; import net.jini.core.discovery.LookupLocator; import net.jini.core.event.RemoteEventListener; import javax.security.auth.Subject; import java.lang.reflect.Method; import java.io.InvalidObjectException; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.rmi.RemoteException; import java.rmi.MarshalledObject; /** * This class is a proxy for a lookup discovery service. Clients only see * instances of this class via the LookupDiscoveryService interface (and * the FiddlerAdmin interface if needed). * * @author Sun Microsystems, Inc. * */ class FiddlerProxy implements Administrable, LookupDiscoveryService, ReferentUuid, Serializable { private static final long serialVersionUID = 2L; /** * The reference through which communication occurs between the * client-side and the server-side of the lookup discovery service * * @serial */ final Fiddler server; /** * The unique identifier assigned to the current instance of this * proxy class by the lookup discovery service. This ID is used to * determine equality between proxies. * * @serial */ final Uuid proxyID; /** * Public static factory method that creates and returns an instance of * <code>FiddlerProxy</code>. If the server associated with this proxy * implements <code>RemoteMethodControl</code>, then the object returned by * this method will also implement <code>RemoteMethodControl</code>. * * @param server reference to the server object through which * communication occurs between the client-side and * server-side of the associated service. * @param proxyID the unique identifier assigned by the service to each * instance of this proxy * * @return an instance of <code>FiddlerProxy</code> that implements * <code>RemoteMethodControl</code> if the given <code>server</code> * does. */ public static FiddlerProxy createServiceProxy(Fiddler server, Uuid proxyID) { if(server instanceof RemoteMethodControl) { return new ConstrainableFiddlerProxy(server, proxyID, null); } else { return new FiddlerProxy(server, proxyID); }//endif }//end createServiceProxy /** * Constructs a new instance of FiddlerProxy. * * @param server reference to the server object through which * communication occurs between the client-side and * server-side of the associated service * @param proxyID the unique identifier assigned by the service to each * instance of this proxy */ private FiddlerProxy(Fiddler server, Uuid proxyID) { this.server = server; this.proxyID = proxyID; }//end constructor /* From net.jini.admin.Administrable */ /** * Returns a proxy object through which the lookup discovery service * for which the object on which this method is invoked serves as * proxy may be administered * * @return a proxy object through which the lookup discovery service * may be administered. * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * server. * * @see net.jini.Administrable */ public Object getAdmin() throws RemoteException { return server.getAdmin(); } /* From net.jini.discovery.LookupDiscoveryService */ /** * Registers with the lookup discovery service. When a client invokes * this method, it requests that the lookup discovery service perform * discovery processing on its behalf. * * @param groups String array, none of whose elements may be null, * consisting of zero or more names of groups to * which lookup services to discover belong. * A null value or an empty array * (net.jini.discovery.LookupDiscovery.ALL_GROUPS * or net.jini.discovery.LookupDiscovery.NO_GROUPS) * are both acceptable. * * @param locators array of zero or more non-null LookupLocator * objects, each corresponding to a specific lookup * service to discover. If either the empty array * or null is passed to this argument, then no * locator discovery will be performed for the * associated registration. * * @param listener a non-null instance of RemoteEventListener. This * argument specifies the entity that will receive * events notifying the registration that a lookup * service of interest has been discovered. A * non-null value must be passed to this argument, * otherwise a NullPointerException will be thrown * and the registration. * * @param handback null or an instance of MarshalledObject. This * argument specifies an object that will be * included in the notification event that the * lookup discovery service sends to the registered * listener. * * @param leaseDuration long value representing the amount of time (in * milliseconds) for which the resources of the * lookup discovery service are being requested. * * @return an instance of the LookupDiscoveryRegistration interface. * * @throws java.rmi.RemoteException typically, this exception occurs when * there is a communication failure between the client and the * server. When this exception does occur, the registration may * or may not have completed successfully. * * @throws java.lang.NullPointerException this exception occurs when * null is input to the listener parameter. * * @see net.jini.discovery.LookupDiscoveryService */ public LookupDiscoveryRegistration register(String[] groups, LookupLocator[] locators, RemoteEventListener listener, MarshalledObject handback, long leaseDuration) throws RemoteException { return server.register(groups, locators, listener, handback, leaseDuration); } /* 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 proxyID; } /** * For any instance of this class, returns the hashcode value generated * by the hashCode method of the proxy ID associated with the current * instance of this proxy. * * @return <code>int</code> value representing the hashcode for an * instance of this class. */ public int hashCode() { return proxyID.hashCode(); } /** * For any instance of this class, indicates whether the object input * to this method is equal to the current instance of this class; where * equality of proxies to a lookup discovery service is defined by * reference equality. That is, two proxies are equal if they reference * (are proxies to) the same backend server. * * @param obj reference to the object that is to be compared to the * object on which this method is invoked. * * @return <code>true</code> if the object input is referentially * equal to the object on which this method is invoked; * <code>false</code> otherwise. */ public boolean equals(Object obj) { return ReferentUuids.compare(this,obj); } /** 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(server == null) { throw new InvalidObjectException("FiddlerProxy.readObject " +"failure - server " +"field is null"); }//endif /* Verify proxyID */ if(proxyID == null) { throw new InvalidObjectException("FiddlerProxy.readObject " +"failure - proxyID " +"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 InvalidObjectException { throw new InvalidObjectException("no data found when attempting to " +"deserialize FiddlerProxy instance"); }//end readObjectNoData /** The constrainable version of the class <code>FiddlerProxy</code>. * <p> * When a client obtains an instance of this proxy class, the client * should not attempt to use the proxy until the client is assured * that the proxy can be trusted. In addition to implementing the * methods and mechanisms required by <code>RemoteMethodControl</code>, * this class - in conjunction with the service's * <code>ProxyVerifier</code> class, helps provide a mechanism * for verifying trust in the proxy on behalf of a client. * <p> * In order to verify that an instance of this class is trusted, * trust must be verified in all subsidiary objects (contained in that * instance) through which the client ultimately makes calls (local or * remote). With respect to this class, the <code>server</code> field * is a proxy object through which the client makes remote calls to the * service's backend. Therefore, trust in that object must be * verified. Additionally, this class also contains a field of type * <code>Uuid</code> (<code>proxyID</code> which should be * tested for trust. Consider the following diagram: * <p> * <pre> * FiddlerProxy { * Fiddler server * Uuid proxyID * }//end FiddlerProxy * </pre> * <p> * Thus, in order to verify that an instance of this class is trusted, * trust must be verified in the following objects from the diagram * above: * <ul><li> server * <li> proxyID * </ul> * * When a client obtains an instance of this proxy class, the * deserialization process which delivers the proxy to the client * invokes the <code>readObject</code> method of this class. Part of * trust verification is performed in the <code>readObject</code> method, * and part is performed when the client prepares the proxy. Thus, this * class' participation in the trust verification process can be * summarized as follows: * <p> * <ul> * <li> server * <ul> * <li> readObject * <ul> * <li> verify server != null * <li> verify server implements RemoteMethodControl * <li> verify server's method constraints are the same * as those placed on the corresponding public Remote * methods of its outer proxy class * </ul> * <li> proxy preparation * <ul> * <li> Security.verifyObjectTrust() which calls * <li> ProxyVerifier.isTrustedObject() which calls * <li> canonicalServerObject.checkTrustEquivalence(server) * (whose implementation is supplied by the particular * RMI implementation that was used to export the server) * </ul> * </ul> * <li> proxyID * <ul><li> readObject * <ul><li> verify proxyID != null</ul> * </ul> * </ul> * * @since 2.0 */ static final class ConstrainableFiddlerProxy extends FiddlerProxy implements RemoteMethodControl { static final long serialVersionUID = 2L; /* Array containing element pairs in which each pair of elements * represents a correspondence 'mapping' between two methods having * the following characteristics: * - the first element in the pair is one of the public, remote * method(s) that may be invoked by the client through the proxy * class that this class extends * - 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 */ private static final Method[] methodMapArray = { ProxyUtil.getMethod(Administrable.class, "getAdmin", new Class[] {} ), ProxyUtil.getMethod(Administrable.class, "getAdmin", new Class[] {} ), ProxyUtil.getMethod(LookupDiscoveryService.class, "register", new Class[] {String[].class, LookupLocator[].class, RemoteEventListener.class, MarshalledObject.class, long.class} ), ProxyUtil.getMethod(Fiddler.class, "register", new Class[] {String[].class, LookupLocator[].class, RemoteEventListener.class, MarshalledObject.class, long.class} ) };//end methodMapArray /** Client constraints placed on this proxy (may be <code>null</code>). * * @serial */ private MethodConstraints methodConstraints; /** Constructs a new <code>ConstrainableFiddlerProxy</code> instance. * <p> * For a description of all but the <code>methodConstraints</code> * argument (provided below), refer to the description for the * constructor of this class' super class. * * @param methodConstraints the client method constraints to place on * this proxy (may be <code>null</code>). */ private ConstrainableFiddlerProxy(Fiddler server, Uuid proxyID, MethodConstraints methodConstraints) { super( constrainServer(server, methodConstraints), proxyID); this.methodConstraints = methodConstraints; }//end constructor /** Returns a copy of the given server proxy having the client method * constraints that result after the specified method mapping is * applied to the given client method constraints. */ private static Fiddler constrainServer( Fiddler server, MethodConstraints constraints ) { MethodConstraints newConstraints = ConstrainableProxyUtil.translateConstraints(constraints, methodMapArray); RemoteMethodControl constrainedServer = ((RemoteMethodControl)server).setConstraints(newConstraints); return ((Fiddler)constrainedServer); }//end constrainServer /** Returns a new copy of this proxy class * (<code>ConstrainableFiddlerProxy</code>) with its client * constraints set to the specified constraints. A <code>null</code> * value is interpreted as mapping all methods to empty constraints. */ public RemoteMethodControl setConstraints (MethodConstraints constraints) { return ( new ConstrainableFiddlerProxy (server, proxyID, constraints) ); }//end setConstraints /** Returns the client constraints placed on the current instance * of this proxy class (<code>ConstrainableFiddlerProxy</code>). * The value returned by this method can be <code>null</code>, * which is interpreted as mapping all methods to empty constraints. */ public MethodConstraints getConstraints() { return methodConstraints; }//end getConstraints /** 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(server); }//end getProxyTrustIterator /** Performs various functions related to the trust verification * process for the current instance of this proxy class, as * detailed in the description for this class. * * @throws <code>InvalidObjectException</code> if any of the * requirements for trust verification (as detailed in the * class description) are not satisfied. */ 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, server, methodMapArray); }//end readObject }//end class ConstrainableFiddlerProxy }//end class FiddlerProxy