View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    * 
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.river.fiddler.proxy;
19  
20  import java.io.IOException;
21  import java.io.InvalidObjectException;
22  import java.io.ObjectInputStream;
23  import java.io.Serializable;
24  import java.lang.reflect.Method;
25  import java.rmi.MarshalledObject;
26  import java.rmi.RemoteException;
27  import net.jini.admin.Administrable;
28  import net.jini.core.constraint.MethodConstraints;
29  import net.jini.core.constraint.RemoteMethodControl;
30  import net.jini.core.discovery.LookupLocator;
31  import net.jini.core.event.RemoteEventListener;
32  import net.jini.discovery.LookupDiscoveryRegistration;
33  import net.jini.discovery.LookupDiscoveryService;
34  import net.jini.export.ProxyAccessor;
35  import net.jini.id.ReferentUuid;
36  import net.jini.id.ReferentUuids;
37  import net.jini.id.Uuid;
38  import net.jini.security.proxytrust.ProxyTrustIterator;
39  import net.jini.security.proxytrust.SingletonProxyTrustIterator;
40  import org.apache.river.api.io.AtomicSerial;
41  import org.apache.river.api.io.AtomicSerial.GetArg;
42  import org.apache.river.proxy.ConstrainableProxyUtil;
43  
44  /**
45   * This class is a proxy for a lookup discovery service. Clients only see
46   * instances of this class via the LookupDiscoveryService interface (and
47   * the FiddlerAdmin interface if needed).
48   *
49   * @author Sun Microsystems, Inc.
50   *
51   */
52  @AtomicSerial
53  public class FiddlerProxy implements Administrable, LookupDiscoveryService,
54                                ReferentUuid, ProxyAccessor, Serializable
55  {
56      private static final long serialVersionUID = 2L;
57      /**
58       * The reference through which communication occurs between the
59       * client-side and the server-side of the lookup discovery service
60       *
61       * @serial
62       */
63      final Fiddler server;
64      /** 
65       * The unique identifier assigned to the current instance of this 
66       * proxy class by the lookup discovery service. This ID is used to
67       * determine equality between proxies.
68       *
69       * @serial
70       */
71      final Uuid proxyID;
72  
73      /**
74       * Public static factory method that creates and returns an instance of 
75       * <code>FiddlerProxy</code>. If the server associated with this proxy
76       * implements <code>RemoteMethodControl</code>, then the object returned by
77       * this method will also implement <code>RemoteMethodControl</code>.
78       * 
79       * @param server  reference to the server object through which 
80       *                communication occurs between the client-side and
81       *                server-side of the associated service.
82       * @param proxyID the unique identifier assigned by the service to each
83       *                instance of this proxy
84       * 
85       * @return an instance of <code>FiddlerProxy</code> that implements
86       *         <code>RemoteMethodControl</code> if the given <code>server</code>
87       *         does.
88       */
89      public static FiddlerProxy createServiceProxy(Fiddler server,
90                                                    Uuid proxyID)
91      {
92          if(server instanceof RemoteMethodControl) {
93              return new ConstrainableFiddlerProxy(server, proxyID, null);
94          } else {
95              return new FiddlerProxy(server, proxyID);
96          }//endif
97      }//end createServiceProxy
98  
99      /**
100      * {@link AtomicSerial} constructor.
101      * @param arg
102      * @throws IOException 
103      */
104     FiddlerProxy(GetArg arg) throws IOException {
105 	this(check((Fiddler) arg.get("server", null),
106 		   (Uuid) arg.get("proxyID", null)),
107 	     (Uuid) arg.get("proxyID", null));
108     }
109 
110     /**
111      * Constructs a new instance of FiddlerProxy.
112      *
113      * @param server  reference to the server object through which 
114      *                communication occurs between the client-side and
115      *                server-side of the associated service
116      * @param proxyID the unique identifier assigned by the service to each
117      *                instance of this proxy
118      */
119     private FiddlerProxy(Fiddler server, Uuid proxyID) {
120 	this.server  = server;
121 	this.proxyID = proxyID;
122     }//end constructor
123 
124     /* From net.jini.admin.Administrable */
125     /** 
126      * Returns a proxy object through which the lookup discovery service
127      * for which the object on which this method is invoked serves as
128      * proxy may be administered
129      *
130      * @return a proxy object through which the lookup discovery service
131      *         may be administered.
132      * 
133      * @throws java.rmi.RemoteException typically, this exception occurs when
134      *         there is a communication failure between the client and the
135      *         server.
136      *
137      * @see net.jini.admin.Administrable
138      */
139     public Object getAdmin() throws RemoteException {
140         return server.getAdmin();
141     }
142 
143     /* From net.jini.discovery.LookupDiscoveryService */
144     /**
145      * Registers with the lookup discovery service. When a client invokes
146      * this method, it requests that the lookup discovery service perform
147      * discovery processing on its behalf.
148      *
149      * @param groups        String array, none of whose elements may be null,
150      *                      consisting of zero or more names of groups to
151      *                      which lookup services to discover belong.
152      *                      A null value or an empty array
153      *                      (net.jini.discovery.LookupDiscovery.ALL_GROUPS
154      *                      or net.jini.discovery.LookupDiscovery.NO_GROUPS)
155      *                      are both acceptable.
156      *                      
157      * @param locators      array of zero or more non-null LookupLocator
158      *                      objects, each corresponding to a specific lookup
159      *                      service to discover. If either the empty array
160      *                      or null is passed to this argument, then no
161      *                      locator discovery will be performed for the
162      *                      associated registration.
163      *                      
164      * @param listener      a non-null instance of RemoteEventListener. This 
165      *                      argument specifies the entity that will receive
166      *                      events notifying the registration that a lookup
167      *                      service of interest has been discovered. A 
168      *                      non-null value must be passed to this argument,
169      *                      otherwise a NullPointerException will be thrown
170      *                      and the registration.
171      *                      
172      * @param handback      null or an instance of MarshalledObject. This
173      *                      argument specifies an object that will be 
174      *                      included in the notification event that the
175      *                      lookup discovery service sends to the registered
176      *                      listener.
177      *                      
178      * @param leaseDuration long value representing the amount of time (in
179      *                      milliseconds) for which the resources of the
180      *                      lookup discovery service are being requested.
181      *                      
182      * @return an instance of the LookupDiscoveryRegistration interface.
183      * 
184      * @throws java.rmi.RemoteException typically, this exception occurs when
185      *         there is a communication failure between the client and the
186      *         server. When this exception does occur, the registration may
187      *         or may not have completed successfully.
188      *
189      * @throws java.lang.NullPointerException this exception occurs when
190      *         null is input to the listener parameter.
191      *
192      * @see net.jini.discovery.LookupDiscoveryService
193      */
194     public LookupDiscoveryRegistration register(String[] groups,
195                                                 LookupLocator[] locators,
196                                                 RemoteEventListener listener,
197                                                 MarshalledObject handback,
198                                                 long leaseDuration)
199                                                         throws RemoteException
200     {
201 	return server.register(groups,
202                                locators,
203                                listener,
204                                handback,
205                                leaseDuration);
206     }
207 
208     /* From net.jini.id.ReferentUuid */
209     /** 
210      * Returns the universally unique identifier that has been assigned to the
211      * resource this proxy represents.
212      *
213      * @return the instance of <code>Uuid</code> that is associated with the
214      *         resource this proxy represents. This method will not return
215      *         <code>null</code>.
216      *
217      * @see net.jini.id.ReferentUuid
218      */
219     public Uuid getReferentUuid() {
220         return proxyID;
221     }
222 
223     /** 
224      * For any instance of this class, returns the hashcode value generated
225      * by the hashCode method of the proxy ID associated with the current
226      * instance of this proxy.
227      *
228      * @return <code>int</code> value representing the hashcode for an
229      *         instance of this class.
230      */
231     public int hashCode() {
232 	return proxyID.hashCode();
233     }
234 
235     /** 
236      * For any instance of this class, indicates whether the object input
237      * to this method is equal to the current instance of this class; where
238      * equality of proxies to a lookup discovery service is defined by
239      * reference equality. That is, two proxies are equal if they reference
240      * (are proxies to) the same backend server.
241      *
242      * @param obj reference to the object that is to be compared to the
243      *            object on which this method is invoked.
244      *
245      * @return <code>true</code> if the object input is referentially
246      *         equal to the object on which this method is invoked;
247      *         <code>false</code> otherwise.
248      */
249     public boolean equals(Object obj) {
250 	return  ReferentUuids.compare(this,obj);
251     }
252 
253     /** When an instance of this class is deserialized, this method is
254      *  automatically invoked. This implementation of this method validates
255      *  the state of the deserialized instance.
256      *
257      * @throws InvalidObjectException if the state of the
258      *         deserialized instance of this class is found to be invalid.
259      */
260     private void readObject(ObjectInputStream s)  
261                                throws IOException, ClassNotFoundException
262     {
263         s.defaultReadObject();
264         check(server, proxyID);
265     }//end readObject
266     
267     private static Fiddler check(Fiddler server, Uuid proxyID) throws InvalidObjectException{
268         /* Verify server */
269         if(server == null) {
270             throw new InvalidObjectException("FiddlerProxy.readObject "
271                                              +"failure - server "
272                                              +"field is null");
273         }//endif
274         /* Verify proxyID */
275         if(proxyID == null) {
276             throw new InvalidObjectException("FiddlerProxy.readObject "
277                                              +"failure - proxyID "
278                                              +"field is null");
279         }//endif
280 	return server;
281     }
282 
283     /** During deserialization of an instance of this class, if it is found
284      *  that the stream contains no data, this method is automatically
285      *  invoked. Because it is expected that the stream should always 
286      *  contain data, this implementation of this method simply declares
287      *  that something must be wrong.
288      *
289      * @throws InvalidObjectException to indicate that there
290      *         was no data in the stream during deserialization of an
291      *         instance of this class; declaring that something is wrong.
292      */
293     private void readObjectNoData() throws InvalidObjectException {
294         throw new InvalidObjectException("no data found when attempting to "
295                                          +"deserialize FiddlerProxy instance");
296     }//end readObjectNoData
297 
298     @Override
299     public Object getProxy() {
300 	return server;
301     }
302 
303     /** The constrainable version of the class <code>FiddlerProxy</code>. 
304      *  <p>
305      *  When a client obtains an instance of this proxy class, the client
306      *  should not attempt to use the proxy until the client is assured
307      *  that the proxy can be trusted. In addition to implementing the
308      *  methods and mechanisms required by <code>RemoteMethodControl</code>, 
309      *  this class - in conjunction with the service's
310      *  <code>ProxyVerifier</code> class, helps provide a mechanism
311      *  for verifying trust in the proxy on behalf of a client.
312      *  <p>
313      *  In order to verify that an instance of this class is trusted, 
314      *  trust must be verified in all subsidiary objects (contained in that
315      *  instance) through which the client ultimately makes calls (local or
316      *  remote).  With respect to this class, the <code>server</code> field
317      *  is a proxy object through which the client makes remote calls to the 
318      *  service's backend. Therefore, trust in that object must be
319      *  verified. Additionally, this class also contains a field of type
320      *  <code>Uuid</code> (<code>proxyID</code> which should be
321      *  tested for trust. Consider the following diagram:
322      *  <p>
323      *  <pre>
324      *    FiddlerProxy {
325      *        Fiddler server
326      *        Uuid proxyID
327      *    }//end FiddlerProxy
328      *  </pre>
329      *  <p>
330      *  Thus, in order to verify that an instance of this class is trusted,
331      *  trust must be verified in the following objects from the diagram
332      *  above:
333      *  <ul><li> server
334      *      <li> proxyID
335      *  </ul>
336      *
337      *  When a client obtains an instance of this proxy class, the
338      *  deserialization process which delivers the proxy to the client
339      *  invokes the <code>readObject</code> method of this class. Part of
340      *  trust verification is performed in the <code>readObject</code> method,
341      *  and part is performed when the client prepares the proxy. Thus, this
342      *  class' participation in the trust verification process can be
343      *  summarized as follows:
344      *  <p>
345      *  <ul>
346      *    <li> server
347      *      <ul>
348      *        <li> readObject
349      *          <ul>
350      *            <li> verify server != null
351      *            <li> verify server implements RemoteMethodControl
352      *            <li> verify server's method constraints are the same
353      *                 as those placed on the corresponding public Remote
354      *                 methods of its outer proxy class
355      *          </ul>
356      *        <li> proxy preparation
357      *          <ul>
358      *            <li> Security.verifyObjectTrust() which calls
359      *            <li> ProxyVerifier.isTrustedObject() which calls
360      *            <li> canonicalServerObject.checkTrustEquivalence(server)
361      *                 (whose implementation is supplied by the particular 
362      *                 RMI implementation that was used to export the server)
363      *          </ul>
364      *      </ul>
365      *    <li> proxyID
366      *      <ul><li> readObject
367      *          <ul><li> verify proxyID != null</ul>
368      *      </ul>
369      *  </ul>
370      *
371      * @since 2.0
372      */
373     @AtomicSerial
374     static final class ConstrainableFiddlerProxy extends FiddlerProxy
375                                                  implements RemoteMethodControl
376     {
377         static final long serialVersionUID = 2L;
378 
379         /* Array containing element pairs in which each pair of elements
380          * represents a correspondence 'mapping' between two methods having
381          * the following characteristics:
382          *  - the first element in the pair is one of the public, remote
383          *    method(s) that may be invoked by the client through the proxy
384          *    class that this class extends
385          *  - the second element in the pair is the method, implemented
386          *    in the backend server class, that is ultimately executed in
387          *    the server's backend when the client invokes the corresponding
388          *    method in this proxy
389          */
390         private static final Method[] methodMapArray = 
391         {
392             ProxyUtil.getMethod(Administrable.class,
393                                 "getAdmin", new Class[] {} ),
394             ProxyUtil.getMethod(Administrable.class,
395                                 "getAdmin", new Class[] {} ),
396 
397             ProxyUtil.getMethod(LookupDiscoveryService.class,
398                                 "register", 
399                                 new Class[] {String[].class,
400                                              LookupLocator[].class,
401                                              RemoteEventListener.class,
402                                              MarshalledObject.class,
403                                              long.class} ),
404             ProxyUtil.getMethod(Fiddler.class,
405                                 "register",
406                                 new Class[] {String[].class,
407                                              LookupLocator[].class,
408                                              RemoteEventListener.class,
409                                              MarshalledObject.class,
410                                              long.class} )
411         };//end methodMapArray
412 
413         /** Client constraints placed on this proxy (may be <code>null</code>).
414          *
415          * @serial
416          */
417         private MethodConstraints methodConstraints;
418 
419         /** Constructs a new <code>ConstrainableFiddlerProxy</code> instance.
420          *  <p>
421          *  For a description of all but the <code>methodConstraints</code>
422          *  argument (provided below), refer to the description for the
423          *  constructor of this class' super class.
424          *
425          *  @param methodConstraints the client method constraints to place on
426          *                           this proxy (may be <code>null</code>).
427          */
428         private ConstrainableFiddlerProxy(Fiddler server, 
429                                           Uuid proxyID,
430                                           MethodConstraints methodConstraints)
431         {
432             super( constrainServer(server, methodConstraints), proxyID);
433 	    this.methodConstraints = methodConstraints;
434         }//end constructor
435 
436 	/**
437 	 * {@link AtomicSerial} constructor.
438 	 * @param arg
439 	 * @throws IOException 
440 	 */
441 	ConstrainableFiddlerProxy(GetArg arg) throws IOException {
442 	    super(check(arg));
443 	    this.methodConstraints 
444 		    = (MethodConstraints) arg.get("methodConstraints", null);
445 	}
446 	
447 	/**
448 	 * Validate invariants before calling superclass constructor.
449 	 * @param arg
450 	 * @return
451 	 * @throws IOException 
452 	 */
453 	private static GetArg check(GetArg arg) throws IOException {
454 	    FiddlerProxy fp = new FiddlerProxy(arg);
455 	    MethodConstraints methodConstraints 
456 		    = (MethodConstraints) arg.get("methodConstraints", null);
457 	    /* Verify the server and its constraints */
458             ConstrainableProxyUtil.verifyConsistentConstraints
459                                                        (methodConstraints,
460                                                         fp.server,
461                                                         methodMapArray);
462 	    return arg;
463 	}
464 
465         /** Returns a copy of the given server proxy having the client method
466          *  constraints that result after the specified method mapping is
467          *  applied to the given client method constraints.
468          */
469         private static Fiddler constrainServer( Fiddler server,
470                                                 MethodConstraints constraints )
471         {
472             MethodConstraints newConstraints 
473                = ConstrainableProxyUtil.translateConstraints(constraints,
474                                                              methodMapArray);
475             RemoteMethodControl constrainedServer = 
476                 ((RemoteMethodControl)server).setConstraints(newConstraints);
477 
478             return ((Fiddler)constrainedServer);
479         }//end constrainServer
480 
481         /** Returns a new copy of this proxy class 
482          *  (<code>ConstrainableFiddlerProxy</code>) with its client
483          *  constraints set to the specified constraints. A <code>null</code>
484          *  value is interpreted as mapping all methods to empty constraints.
485          */
486         public RemoteMethodControl setConstraints
487                                               (MethodConstraints constraints)
488         {
489             return ( new ConstrainableFiddlerProxy
490                                              (server, proxyID, constraints) );
491         }//end setConstraints
492 
493         /** Returns the client constraints placed on the current instance
494          *  of this proxy class (<code>ConstrainableFiddlerProxy</code>).
495          *  The value returned by this method can be <code>null</code>,
496          *  which is interpreted as mapping all methods to empty constraints.
497          */
498         public MethodConstraints getConstraints() {
499             return methodConstraints;
500         }//end getConstraints
501 
502         /** Returns a proxy trust iterator that is used in 
503          *  <code>ProxyTrustVerifier</code> to retrieve this object's
504          *  trust verifier.
505          */
506         private ProxyTrustIterator getProxyTrustIterator() {
507 	    return new SingletonProxyTrustIterator(server);
508         }//end getProxyTrustIterator
509 
510         /** Performs various functions related to the trust verification
511          *  process for the current instance of this proxy class, as
512          *  detailed in the description for this class.
513          *
514          * @throws <code>InvalidObjectException</code> if any of the
515          *         requirements for trust verification (as detailed in the 
516          *         class description) are not satisfied.
517          */
518         private void readObject(ObjectInputStream s)  
519                                    throws IOException, ClassNotFoundException
520         {
521             /* Note that basic validation of the fields of this class was
522              * already performed in the readObject() method of this class'
523              * super class.
524              */
525             s.defaultReadObject();
526             /* Verify the server and its constraints */
527             ConstrainableProxyUtil.verifyConsistentConstraints
528                                                        (methodConstraints,
529                                                         server,
530                                                         methodMapArray);
531         }//end readObject
532 
533     }//end class ConstrainableFiddlerProxy
534 
535 }//end class FiddlerProxy