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 org.apache.river.admin.DestroyAdmin;
21  import org.apache.river.proxy.ConstrainableProxyUtil;
22  import org.apache.river.admin.FiddlerAdmin;
23  import java.io.IOException;
24  import java.io.InvalidObjectException;
25  import java.io.ObjectInputStream;
26  import java.io.Serializable;
27  import java.lang.reflect.Method;
28  import java.rmi.RemoteException;
29  import net.jini.admin.JoinAdmin;
30  import net.jini.core.constraint.MethodConstraints;
31  import net.jini.core.constraint.RemoteMethodControl;
32  import net.jini.core.discovery.LookupLocator;
33  import net.jini.core.entry.Entry;
34  import net.jini.id.ReferentUuid;
35  import net.jini.id.ReferentUuids;
36  import net.jini.id.Uuid;
37  import net.jini.security.proxytrust.ProxyTrustIterator;
38  import net.jini.security.proxytrust.SingletonProxyTrustIterator;
39  import org.apache.river.api.io.AtomicSerial;
40  import org.apache.river.api.io.AtomicSerial.GetArg;
41  
42  /**
43   * This class is a proxy providing access to the methods of an implementation
44   * of the lookup discovery service which allow the administration of the
45   * service. Clients only see instances of this class via the FiddlerAdmin
46   * interface.
47   *
48   * @author Sun Microsystems, Inc.
49   *
50   */
51  @AtomicSerial
52  public class FiddlerAdminProxy implements FiddlerAdmin, ReferentUuid, Serializable {
53  
54      private static final long serialVersionUID = 2L;
55  
56      /**
57       * The reference through which communication occurs between the
58       * client-side and the server-side of the lookup discovery service
59       *
60       * @serial
61       */
62      final Fiddler server;
63      /** 
64       * The unique identifier assigned to the current instance of this 
65       * proxy class by the lookup discovery service. This ID is used to
66       * determine equality between proxies.
67       *
68       * @serial
69       */
70      final Uuid proxyID;
71  
72      /**
73       * Public static factory method that creates and returns an instance of 
74       * <code>FiddlerAdminProxy</code>. If the server associated with this proxy
75       * implements <code>RemoteMethodControl</code>, then the object returned by
76       * this method will also implement <code>RemoteMethodControl</code>.
77       * 
78       * @param server  reference to the server object through which 
79       *                communication occurs between the client-side and
80       *                server-side of the associated service.
81       * @param proxyID the unique identifier assigned by the service to each
82       *                instance of this proxy
83       * 
84       * @return an instance of <code>FiddlerAdminProxy</code> that implements
85       *         <code>RemoteMethodControl</code> if the given <code>server</code>
86       *         does.
87       */
88      public static FiddlerAdminProxy createAdminProxy(Fiddler server,
89                                                       Uuid proxyID)
90      {
91          if(server instanceof RemoteMethodControl) {
92              return new ConstrainableFiddlerAdminProxy(server, proxyID, null);
93          } else {
94              return new FiddlerAdminProxy(server, proxyID);
95          }//endif
96      }//end createAdminProxy
97  
98      /**
99       * Constructs a new instance of FiddlerAdminProxy.
100      *
101      * @param server  reference to the server object through which 
102      *                communication occurs between the client-side and
103      *                server-side of the associated service
104      * @param proxyID the unique identifier assigned by the service to each
105      *                instance of this proxy
106      */
107     private FiddlerAdminProxy(Fiddler server, Uuid proxyID) {
108 	this.server  = server;
109 	this.proxyID = proxyID;
110     }//end constructor
111 
112     /**
113      * {@link AtomicSerial} constructor.
114      * @param arg
115      * @throws IOException 
116      */
117     FiddlerAdminProxy(GetArg arg) throws IOException {
118 	this((Fiddler)arg.get("server", null), (Uuid) arg.get("proxyID", null));
119     }
120     
121     /* *** Methods of org.apache.river.fiddler.FiddlerAdmin *** */
122 
123     /**
124      * Changes the least upper bound applied to all lease durations granted
125      * by the lookup discovery service.
126      * <p>
127      * This method is a mechanism for an entity with the appropriate
128      * privileges to administratively change the value of the least upper
129      * bound that will be applied by the Fiddler implementation of the lookup
130      * discovery service when determining the duration to assign to the lease
131      * on a requested registration.
132      *
133      * @param newBound <code>long</code> value representing the new least
134      *        upper bound (in milliseconds) on the set of all possible
135      *        lease durations that may be granted
136      * 
137      * @throws java.rmi.RemoteException typically, this exception occurs when
138      *         there is a communication failure between the client and the
139      *         lookup discovery service. When this exception does occur, the
140      *         bound value may or may not have been changed successfully.
141      *
142      * @see FiddlerAdmin#setLeaseBound
143      */
144     public void setLeaseBound(long newBound) throws RemoteException {
145         server.setLeaseBound(newBound);
146     }
147     /**
148      * Retrieves the least upper bound applied to all lease durations granted
149      * by the lookup discovery service.
150      *
151      * @return <code>long</code> value representing the current least
152      *         upper bound (in milliseconds) on the set of all possible
153      *         lease durations that may be granted
154      * 
155      * @throws java.rmi.RemoteException typically, this exception occurs when
156      *         there is a communication failure between the client and the
157      *         lookup discovery service.
158      *
159      * @see FiddlerAdmin#setLeaseBound
160      */
161     public long getLeaseBound() throws RemoteException {
162         return server.getLeaseBound();
163     }
164 
165     /**
166      * Change the weight factor applied by the lookup discovery service
167      * to the snapshot size during the test to determine whether or not
168      * to take a "snapshot" of the system state.
169      *
170      * @param weight weight factor for snapshot size
171      * 
172      * @throws java.rmi.RemoteException typically, this exception occurs when
173      *         there is a communication failure between the client and the
174      *         lookup discovery service. When this exception does occur, the
175      *         weight factor may or may not have been changed successfully.
176      *
177      * @see FiddlerAdmin#setPersistenceSnapshotWeight
178      */
179     public void setPersistenceSnapshotWeight(float weight)
180                                                      throws RemoteException
181     {
182         server.setPersistenceSnapshotWeight(weight);
183     }
184 
185     /**
186      * Retrieve the weight factor applied by the lookup discovery service
187      * to the snapshot size during the test to determine whether or not to
188      * take a "snapshot" of the system state.
189      * 
190      * @return float value corresponding to the weight factor for snapshot
191      *         size
192      * 
193      * @throws java.rmi.RemoteException typically, this exception occurs when
194      *         there is a communication failure between the client and the
195      *         lookup discovery service.
196      *
197      * @see FiddlerAdmin#getPersistenceSnapshotWeight
198      */
199     public float getPersistenceSnapshotWeight() throws RemoteException {
200         return server.getPersistenceSnapshotWeight();
201     }
202 
203     /**
204      * Change the value of the size threshold of the snapshot; which is
205      * employed by the lookup discovery service in the test to determine
206      * whether or not to take a "snapshot" of the system state.
207      *
208      * @param threshold size threshold for taking a snapshot
209      * 
210      * @throws java.rmi.RemoteException typically, this exception occurs when
211      *         there is a communication failure between the client and the
212      *         lookup discovery service. When this exception does occur, the
213      *         threshold may or may not have been changed successfully.
214      *
215      * @see FiddlerAdmin#setPersistenceSnapshotThreshold
216      */
217     public void setPersistenceSnapshotThreshold(int threshold)
218                                                         throws RemoteException
219     {
220         server.setPersistenceSnapshotThreshold(threshold);
221     }
222 
223     /**
224      * Retrieve the value of the size threshold of the snapshot; which is
225      * employed by the lookup discovery service in the test to determine
226      * whether or not to take a "snapshot" of the system state.
227      * 
228      * @return int value corresponding to the size threshold of the snapshot
229      * 
230      * @throws java.rmi.RemoteException typically, this exception occurs when
231      *         there is a communication failure between the client and the
232      *         lookup discovery service.
233      *
234      * @see FiddlerAdmin#getPersistenceSnapshotThreshold
235      */
236     public int getPersistenceSnapshotThreshold() throws RemoteException {
237         return server.getPersistenceSnapshotThreshold();
238     }
239 
240     /* *** Methods of net.jini.admin.JoinAdmin *** */
241 
242     /** 
243      * Get the current attribute sets for the lookup discovery service. 
244      * 
245      * @return array of net.jini.core.entry.Entry containing the current
246      *         attribute sets for the lookup discovery service
247      * 
248      * @throws java.rmi.RemoteException typically, this exception occurs when
249      *         there is a communication failure between the client and the
250      *         lookup discovery service.
251      *
252      * @see net.jini.admin.JoinAdmin#getLookupAttributes
253      */
254     public Entry[] getLookupAttributes() throws RemoteException {
255 	return server.getLookupAttributes();
256     }
257 
258     /** 
259      * Add attribute sets to the current set of attributes associated
260      * with the lookup discovery service. The resulting set will be used
261      * for all future registrations with lookup services. The new attribute
262      * sets are also added to the lookup discovery service's attributes
263      * on each lookup service with which the lookup discovery service
264      * is currently registered.
265      *
266      * @param  attrSets array of net.jini.core.entry.Entry containing the
267      *         attribute sets to add
268      * 
269      * @throws java.rmi.RemoteException typically, this exception occurs when
270      *         there is a communication failure between the client and the
271      *         lookup discovery service. When this exception does occur, the
272      *         attributes may or may not have been added successfully.
273      *
274      * @see net.jini.admin.JoinAdmin#addLookupAttributes
275      */
276     public void addLookupAttributes(Entry[] attrSets) throws RemoteException {
277 	server.addLookupAttributes(attrSets);
278     }
279 
280     /** 
281      * Modify the current set of attributes associated with the lookup
282      * discovery service. The resulting set will be used for all future
283      * registrations with lookup services. The same modifications are 
284      * also made to the lookup discovery service's attributes on each
285      * lookup service with which the lookup discovery service is currently
286      * registered.
287      *
288      * @param  attrSetTemplates  array of net.jini.core.entry.Entry containing
289      *         the templates for matching attribute sets
290      * @param  attrSets array of net.jini.core.entry.Entry containing the
291      *         modifications to make to matching sets
292      * 
293      * @throws java.rmi.RemoteException typically, this exception occurs when
294      *         there is a communication failure between the client and the
295      *         lookup discovery service. When this exception does occur, the
296      *         attributes may or may not have been modified successfully.
297      *
298      * @see net.jini.admin.JoinAdmin#modifyLookupAttributes
299      */
300     public void modifyLookupAttributes(Entry[] attrSetTemplates,
301 				       Entry[] attrSets)
302 	throws RemoteException
303     {
304 	server.modifyLookupAttributes(attrSetTemplates, attrSets);
305     }
306 
307     /** 
308      * Get the names of the groups whose members are lookup services the
309      * lookup discovery services wishes to register with (join).
310      * 
311      * @return String array containing the names of the groups whose members
312      *         are lookup services the lookup discovery service wishes to
313      *         join.
314      * <p>
315      *         If the array returned is empty, the lookup discovery service
316      *         is configured to join no groups. If null is returned, the
317      *         lookup discovery service is configured to join all groups.
318      * 
319      * @throws java.rmi.RemoteException typically, this exception occurs when
320      *         there is a communication failure between the client and the
321      *         lookup discovery service.
322      *
323      * @see net.jini.admin.JoinAdmin#getLookupGroups
324      */
325     public String[] getLookupGroups() throws RemoteException {
326 	return server.getLookupGroups();
327     }
328 
329     /** 
330      * Add new names to the set consisting of the names of groups whose
331      * members are lookup services the lookup discovery service wishes
332      * to register with (join). Any lookup services belonging to the
333      * new groups that the lookup discovery service has not yet registered
334      * with, will be discovered and joined.
335      *
336      * @param  groups String array containing the names of the groups to add
337      * 
338      * @throws java.rmi.RemoteException typically, this exception occurs when
339      *         there is a communication failure between the client and the
340      *         lookup discovery service. When this exception does occur, the
341      *         group names may or may not have been added successfully.
342      *
343      * @see net.jini.admin.JoinAdmin#addLookupGroups
344      */
345     public void addLookupGroups(String[] groups) throws RemoteException {
346 	server.addLookupGroups(groups);
347     }
348 
349     /** 
350      * Remove a set of group names from lookup discovery service's managed
351      * set of groups (the set consisting of the names of groups whose
352      * members are lookup services the lookup discovery service wishes
353      * to join). Any leases granted to the lookup discovery service by
354      * lookup services that are not members of the groups whose names 
355      * remain in the managed set will be cancelled at those lookup services.
356      *
357      * @param  groups String array containing the names of the groups to remove
358      * 
359      * @throws java.rmi.RemoteException typically, this exception occurs when
360      *         there is a communication failure between the client and the
361      *         lookup discovery service. When this exception does occur, the
362      *         group names may or may not have been removed successfully.
363      *
364      * @see net.jini.admin.JoinAdmin#removeLookupGroups
365      */
366     public void removeLookupGroups(String[] groups) throws RemoteException {
367 	server.removeLookupGroups(groups);
368     }
369 
370     /** 
371      * Replace the lookup discovery service's managed set of groups with a
372      * new set of group names. Any leases granted to the lookup discovery
373      * service by lookup services that are not members of the groups whose
374      * names are in the new managed set will be cancelled at those lookup
375      * services. Lookup services that are members of groups reflected in
376      * the new managed set will be discovered and joined.
377      *
378      * @param  groups String array containing the names of the new groups
379      * 
380      * @throws java.rmi.RemoteException typically, this exception occurs when
381      *         there is a communication failure between the client and the
382      *         lookup discovery service. When this exception does occur, the
383      *         group names may or may not have been replaced successfully.
384      *
385      * @see net.jini.admin.JoinAdmin#setLookupGroups
386      */
387     public void setLookupGroups(String[] groups) throws RemoteException {
388 	server.setLookupGroups(groups);
389     }
390 
391     /** 
392      * Get the lookup discovery service's managed set of locators. The
393      * managed set of locators is the set of LookupLocator objects
394      * corresponding to the specific lookup services with which the lookup
395      * discovery service wishes to register (join).
396      * 
397      * @return array of objects of type net.jini.core.discovery.LookupLocator,
398      *         each of which corresponds to a specific lookup service the
399      *         lookup discovery service wishes to join.
400      * 
401      * @throws java.rmi.RemoteException typically, this exception occurs when
402      *         there is a communication failure between the client and the
403      *         lookup discovery service.
404      *
405      * @see net.jini.admin.JoinAdmin#getLookupLocators
406      */
407     public LookupLocator[] getLookupLocators() throws RemoteException {
408 	return server.getLookupLocators();
409     }
410 
411     /** 
412      * Add a set of LookupLocator objects to the lookup discovery service's
413      * managed set of locators. The managed set of locators is the set of
414      * LookupLocator objects corresponding to the specific lookup services
415      * with which the lookup discovery service wishes to register (join).
416      * <p>
417      * Any lookup services corresponding to the new locators that the lookup
418      * discovery service has not yet joined, will be discovered and joined.
419      *
420      * @param  locators array of net.jini.core.discovery.LookupLocator objects to add
421      *         to the managed set of locators
422      * 
423      * @throws java.rmi.RemoteException typically, this exception occurs when
424      *         there is a communication failure between the client and the
425      *         lookup discovery service. When this exception does occur, the
426      *         new locators may or may not have been added successfully.
427      *
428      * @see net.jini.admin.JoinAdmin#addLookupLocators
429      */
430     public void addLookupLocators(LookupLocator[] locators)
431 	throws RemoteException
432     {
433 	server.addLookupLocators(locators);
434     }
435 
436     /** 
437      * Remove a set of LookupLocator objects from the lookup discovery
438      * service's managed set of locators. The managed set of locators is the
439      * set of LookupLocator objects corresponding to the specific lookup
440      * services with which the lookup discovery service wishes to register
441      * (join).
442      * <p>
443      * Note that any leases granted to the lookup discovery service by
444      * lookup services that do not correspond to any of the locators
445      * remaining in the managed set will be cancelled at those lookup
446      * services.
447      *
448      * @param  locators array of net.jini.core.discovery.LookupLocator objects to
449      *         remove from the managed set of locators
450      * 
451      * @throws java.rmi.RemoteException typically, this exception occurs when
452      *         there is a communication failure between the client and the
453      *         lookup discovery service. When this exception does occur, the
454      *         new locators may or may not have been removed successfully.
455      *
456      * @see net.jini.admin.JoinAdmin#removeLookupLocators
457      */
458     public void removeLookupLocators(LookupLocator[] locators)
459 	throws RemoteException
460     {
461 	server.removeLookupLocators(locators);
462     }
463 
464     /** 
465      * Replace the lookup discovery service's managed set of locators with
466      * a new set of locators. The managed set of locators is the set of
467      * LookupLocator objects corresponding to the specific lookup services
468      * with which the lookup discovery service wishes to register (join).
469      * <p>
470      * Note that any leases granted to the lookup discovery service by
471      * lookup services whose corresponding locator is removed from the
472      * managed set will be cancelled at those lookup services. The lookup
473      * services corresponding to the new locators in the managed set
474      * will be discovered and joined.
475      *
476      * @param  locators array of net.jini.core.discovery.LookupLocator objects with
477      *         which to replace the current managed set of locators
478      *         remove from the managed set of locators
479      * 
480      * @throws java.rmi.RemoteException typically, this exception occurs when
481      *         there is a communication failure between the client and the
482      *         lookup discovery service. When this exception does occur, the
483      *         locators in the managed set may or may not have been replaced
484      *         successfully.
485      *
486      * @see net.jini.admin.JoinAdmin#setLookupLocators
487      */
488     public void setLookupLocators(LookupLocator[] locators)
489 	throws RemoteException
490     {
491 	server.setLookupLocators(locators);
492     }
493 
494     /* From net.jini.id.ReferentUuid */
495 
496     /** 
497      * Returns the universally unique identifier that has been assigned to the
498      * resource this proxy represents.
499      *
500      * @return the instance of <code>Uuid</code> that is associated with the
501      *         resource this proxy represents. This method will not return
502      *         <code>null</code>.
503      *
504      * @see net.jini.id.ReferentUuid
505      */
506     public Uuid getReferentUuid() {
507         return proxyID;
508     }
509 
510     /* *** Methods of org.apache.river.admin.DestroyAdmin *** */
511 
512     /**
513      * Destroy the lookup discovery service, if possible, including its
514      * persistent storage. This method will typically spawn a separate
515      * thread to do the actual work asynchronously, so a successful
516      * return from this method usually does not mean that the service
517      * has been destroyed.
518      * 
519      * @throws java.rmi.RemoteException typically, this exception occurs when
520      *         there is a communication failure between the client and the
521      *         lookup discovery service. When this exception does occur, the
522      *         lookup discovery service may or may not have been successfully
523      *         destroyed.
524      *
525      * @see org.apache.river.admin.DestroyAdmin#destroy
526      */
527     public void destroy() throws RemoteException {
528 	server.destroy();
529     }
530 
531     /* *** HashCode and Equals for this class *** */
532 
533     /** 
534      * For any instance of this class, returns the hashcode value generated
535      * by the hashCode method of the proxy ID associated with the current
536      * instance of this proxy.
537      *
538      * @return <code>int</code> value representing the hashcode for an
539      *         instance of this class.
540      */
541     public int hashCode() {
542 	return proxyID.hashCode();
543     }
544 
545     /** 
546      * For any instance of this class, indicates whether the object input
547      * to this method is equal to the current instance of this class; where
548      * equality of administrative proxies to a lookup discovery service is
549      * defined by reference equality. That is, two proxies are equal if they
550      * reference (are proxies to) the same backend server.
551      *
552      * @param obj reference to the object that is to be compared to the
553      *            object on which this method is invoked.
554      *
555      * @return <code>true</code> if the object input is referentially
556      *         equal to the object on which this method is invoked;
557      *         <code>false</code> otherwise.
558      */
559     public boolean equals(Object obj) {
560 	return ReferentUuids.compare(this,obj);
561     }
562 
563     /** When an instance of this class is deserialized, this method is
564      *  automatically invoked. This implementation of this method validates
565      *  the state of the deserialized instance.
566      *
567      * @throws InvalidObjectException if the state of the
568      *         deserialized instance of this class is found to be invalid.
569      */
570     private void readObject(ObjectInputStream s)  
571                                throws IOException, ClassNotFoundException
572     {
573         s.defaultReadObject();
574         /* Verify server */
575         if(server == null) {
576             throw new InvalidObjectException("FiddlerAdminProxy.readObject "
577                                              +"failure - server "
578                                              +"field is null");
579         }//endif
580         /* Verify proxyID */
581         if(proxyID == null) {
582             throw new InvalidObjectException("FiddlerAdminProxy.readObject "
583                                              +"failure - proxyID "
584                                              +"field is null");
585         }//endif
586     }//end readObject
587 
588     /** During deserialization of an instance of this class, if it is found
589      *  that the stream contains no data, this method is automatically
590      *  invoked. Because it is expected that the stream should always 
591      *  contain data, this implementation of this method simply declares
592      *  that something must be wrong.
593      *
594      * @throws InvalidObjectException to indicate that there
595      *         was no data in the stream during deserialization of an
596      *         instance of this class; declaring that something is wrong.
597      */
598     private void readObjectNoData() throws InvalidObjectException {
599         throw new InvalidObjectException("no data found when attempting to "
600                                          +"deserialize FiddlerAdminProxy "
601                                          +"instance");
602     }//end readObjectNoData
603 
604     /** The constrainable version of the class <code>FiddlerAdminProxy</code>. 
605      *  <p>
606      *  When a client obtains an instance of this proxy class, the client
607      *  should not attempt to use the proxy until the client is assured
608      *  that the proxy can be trusted. In addition to implementing the
609      *  methods and mechanisms required by <code>RemoteMethodControl</code>, 
610      *  this class - in conjunction with the service's
611      *  <code>ProxyVerifier</code> class, helps provide a mechanism
612      *  for verifying trust in the proxy on behalf of a client.
613      *  <p>
614      *  In order to verify that an instance of this class is trusted, 
615      *  trust must be verified in all subsidiary objects (contained in that
616      *  instance) through which the client ultimately makes calls (local or
617      *  remote).  With respect to this class, the <code>server</code> field
618      *  is a proxy object through which the client makes remote calls to the 
619      *  service's backend. Therefore, trust in that object must be
620      *  verified. Additionally, this class also contains a field of type
621      *  <code>Uuid</code> (<code>proxyID</code> which should be
622      *  tested for trust. Consider the following diagram:
623      *  <p>
624      *  <pre>
625      *    FiddlerAdminProxy {
626      *        Fiddler server
627      *        Uuid proxyID
628      *    }//end FiddlerAdminProxy
629      *  </pre>
630      *  <p>
631      *  Thus, in order to verify that an instance of this class is trusted,
632      *  trust must be verified in the following objects from the diagram
633      *  above:
634      *  <ul><li> server
635      *      <li> proxyID
636      *  </ul>
637      *
638      *  When a client obtains an instance of this proxy class, the
639      *  deserialization process which delivers the proxy to the client
640      *  invokes the <code>readObject</code> method of this class. Part of
641      *  trust verification is performed in the <code>readObject</code> method,
642      *  and part is performed when the client prepares the proxy. Thus, this
643      *  class' participation in the trust verification process can be
644      *  summarized as follows:
645      *  <p>
646      *  <ul>
647      *    <li> server
648      *      <ul>
649      *        <li> readObject
650      *          <ul>
651      *            <li> verify server != null
652      *            <li> verify server implements RemoteMethodControl
653      *            <li> verify server's method constraints are the same
654      *                 as those placed on the corresponding public Remote
655      *                 methods of its outer proxy class
656      *          </ul>
657      *        <li> proxy preparation
658      *          <ul>
659      *            <li> Security.verifyObjectTrust() which calls
660      *            <li> ProxyVerifier.isTrustedObject() which calls
661      *            <li> canonicalServerObject.checkTrustEquivalence(server)
662      *                 (whose implementation is supplied by the particular 
663      *                 RMI implementation that was used to export the server)
664      *          </ul>
665      *      </ul>
666      *    <li> proxyID
667      *      <ul><li> readObject
668      *          <ul><li> verify proxyID != null</ul>
669      *      </ul>
670      *  </ul>
671      *
672      * @since 2.0
673      */
674     @AtomicSerial
675     static final class ConstrainableFiddlerAdminProxy 
676                                                 extends FiddlerAdminProxy
677                                                 implements RemoteMethodControl
678     {
679         static final long serialVersionUID = 2L;
680 
681         /* Array containing element pairs in which each pair of elements
682          * represents a correspondence 'mapping' between two methods having
683          * the following characteristics:
684          *  - the first element in the pair is one of the public, remote
685          *    method(s) that may be invoked by the client through the proxy
686          *    class that this class extends
687          *  - the second element in the pair is the method, implemented
688          *    in the backend server class, that is ultimately executed in
689          *    the server's backend when the client invokes the corresponding
690          *    method in this proxy
691          */
692         private static final Method[] methodMapArray = 
693         {
694             ProxyUtil.getMethod(JoinAdmin.class,
695                                 "getLookupAttributes", new Class[] {} ),
696             ProxyUtil.getMethod(JoinAdmin.class,
697                                 "getLookupAttributes", new Class[] {} ),
698 
699             ProxyUtil.getMethod(JoinAdmin.class,
700                                 "addLookupAttributes",
701                                 new Class[] {Entry[].class} ),
702             ProxyUtil.getMethod(JoinAdmin.class,
703                                 "addLookupAttributes",
704                                 new Class[] {Entry[].class} ),
705 
706             ProxyUtil.getMethod(JoinAdmin.class,
707                                 "modifyLookupAttributes",
708                                 new Class[] {Entry[].class,
709                                              Entry[].class} ),
710             ProxyUtil.getMethod(JoinAdmin.class,
711                                 "modifyLookupAttributes",
712                                 new Class[] {Entry[].class,
713                                              Entry[].class} ),
714 
715             ProxyUtil.getMethod(JoinAdmin.class,
716                                 "getLookupGroups", new Class[] {} ),
717             ProxyUtil.getMethod(JoinAdmin.class,
718                                 "getLookupGroups", new Class[] {} ),
719 
720             ProxyUtil.getMethod(JoinAdmin.class,
721                                 "addLookupGroups",
722                                 new Class[] {String[].class} ),
723             ProxyUtil.getMethod(JoinAdmin.class,
724                                 "addLookupGroups",
725                                 new Class[] {String[].class} ),
726 
727             ProxyUtil.getMethod(JoinAdmin.class,
728                                 "removeLookupGroups",
729                                 new Class[] {String[].class} ),
730             ProxyUtil.getMethod(JoinAdmin.class,
731                                 "removeLookupGroups",
732                                 new Class[] {String[].class} ),
733 
734             ProxyUtil.getMethod(JoinAdmin.class,
735                                 "setLookupGroups",
736                                 new Class[] {String[].class} ),
737             ProxyUtil.getMethod(JoinAdmin.class,
738                                 "setLookupGroups",
739                                 new Class[] {String[].class} ),
740 
741             ProxyUtil.getMethod(JoinAdmin.class,
742                                 "getLookupLocators", new Class[] {} ),
743             ProxyUtil.getMethod(JoinAdmin.class,
744                                 "getLookupLocators", new Class[] {} ),
745 
746             ProxyUtil.getMethod(JoinAdmin.class,
747                                 "addLookupLocators",
748                                 new Class[] {LookupLocator[].class} ),
749             ProxyUtil.getMethod(JoinAdmin.class,
750                                 "addLookupLocators",
751                                 new Class[] {LookupLocator[].class} ),
752 
753             ProxyUtil.getMethod(JoinAdmin.class,
754                                 "removeLookupLocators",
755                                 new Class[] {LookupLocator[].class} ),
756             ProxyUtil.getMethod(JoinAdmin.class,
757                                 "removeLookupLocators",
758                                 new Class[] {LookupLocator[].class} ),
759 
760             ProxyUtil.getMethod(JoinAdmin.class,
761                                 "setLookupLocators",
762                                 new Class[] {LookupLocator[].class} ),
763             ProxyUtil.getMethod(JoinAdmin.class,
764                                 "setLookupLocators",
765                                 new Class[] {LookupLocator[].class} ),
766 
767 
768             ProxyUtil.getMethod(DestroyAdmin.class,
769                                 "destroy", new Class[] {} ),
770             ProxyUtil.getMethod(DestroyAdmin.class,
771                                 "destroy", new Class[] {} ),
772 
773             ProxyUtil.getMethod(FiddlerAdmin.class,
774                                 "setLeaseBound",
775                                 new Class[] {long.class} ),
776             ProxyUtil.getMethod(FiddlerAdmin.class,
777                                 "setLeaseBound",
778                                 new Class[] {long.class} ),
779 
780             ProxyUtil.getMethod(FiddlerAdmin.class,
781                                 "getLeaseBound",
782                                 new Class[] {} ),
783             ProxyUtil.getMethod(FiddlerAdmin.class,
784                                 "getLeaseBound",
785                                 new Class[] {} ),
786 
787             ProxyUtil.getMethod(FiddlerAdmin.class,
788                                 "setPersistenceSnapshotWeight",
789                                 new Class[] {float.class} ),
790             ProxyUtil.getMethod(FiddlerAdmin.class,
791                                 "setPersistenceSnapshotWeight",
792                                 new Class[] {float.class} ),
793 
794             ProxyUtil.getMethod(FiddlerAdmin.class,
795                                 "getPersistenceSnapshotWeight",
796                                 new Class[] {} ),
797             ProxyUtil.getMethod(FiddlerAdmin.class,
798                                 "getPersistenceSnapshotWeight",
799                                 new Class[] {} ),
800 
801             ProxyUtil.getMethod(FiddlerAdmin.class,
802                                 "setPersistenceSnapshotThreshold",
803                                 new Class[] {int.class} ),
804             ProxyUtil.getMethod(FiddlerAdmin.class,
805                                 "setPersistenceSnapshotThreshold",
806                                 new Class[] {int.class} ),
807 
808             ProxyUtil.getMethod(FiddlerAdmin.class,
809                                 "getPersistenceSnapshotThreshold",
810                                 new Class[] {} ),
811             ProxyUtil.getMethod(FiddlerAdmin.class,
812                                 "getPersistenceSnapshotThreshold",
813                                 new Class[] {} )
814         };//end methodMapArray
815 
816         /** Client constraints placed on this proxy (may be <code>null</code>).
817          *
818          * @serial
819          */
820         private MethodConstraints methodConstraints;
821 
822         /** Constructs a new <code>ConstrainableFiddlerAdminProxy</code>
823          *  instance.
824          *  <p>
825          *  For a description of all but the <code>methodConstraints</code>
826          *  argument (provided below), refer to the description for the
827          *  constructor of this class' super class.
828          *
829          *  @param methodConstraints the client method constraints to place on
830          *                           this proxy (may be <code>null</code>).
831          */
832         private ConstrainableFiddlerAdminProxy
833                                        (Fiddler server, 
834                                         Uuid proxyID,
835                                         MethodConstraints methodConstraints)
836         {
837             super( constrainServer(server, methodConstraints), proxyID);
838 	    this.methodConstraints = methodConstraints;
839         }//end constructor
840 
841 	private static GetArg check(GetArg arg) throws IOException {
842 	    FiddlerAdminProxy fap = new FiddlerAdminProxy(arg);
843 	    MethodConstraints methodConstraints = (MethodConstraints) 
844 		    arg.get("methodConstraints", null);
845 	    /* Verify the server and its constraints */
846             ConstrainableProxyUtil.verifyConsistentConstraints
847                                                        (methodConstraints,
848                                                         fap.server,
849                                                         methodMapArray);
850 	    return arg;
851 	}		       
852 	ConstrainableFiddlerAdminProxy(GetArg arg) throws IOException {
853 	    super(check(arg));
854 	    methodConstraints = (MethodConstraints) 
855 		    arg.get("methodConstraints", null);
856 	}
857 
858         /** Returns a copy of the given server proxy having the client method
859          *  constraints that result after the specified method mapping is
860          *  applied to the given client method constraints.
861          */
862         private static Fiddler constrainServer( Fiddler server,
863                                                 MethodConstraints constraints )
864         {
865             MethodConstraints newConstraints 
866                = ConstrainableProxyUtil.translateConstraints(constraints,
867                                                              methodMapArray);
868             RemoteMethodControl constrainedServer = 
869                 ((RemoteMethodControl)server).setConstraints(newConstraints);
870 
871             return ((Fiddler)constrainedServer);
872         }//end constrainServer
873 
874         /** Returns a new copy of this proxy class 
875          *  (<code>ConstrainableFiddlerAdminProxy</code>) with its client
876          *  constraints set to the specified constraints. A <code>null</code>
877          *  value is interpreted as mapping all methods to empty constraints.
878          */
879         public RemoteMethodControl setConstraints
880                                               (MethodConstraints constraints)
881         {
882             return (new ConstrainableFiddlerAdminProxy
883                                               (server, proxyID, constraints));
884         }//end setConstraints
885 
886         /** Returns the client constraints placed on the current instance
887          *  of this proxy class (<code>ConstrainableFiddlerAdminProxy</code>).
888          *  The value returned by this method can be <code>null</code>,
889          *  which is interpreted as mapping all methods to empty constraints.
890          */
891         public MethodConstraints getConstraints() {
892             return methodConstraints;
893         }//end getConstraints
894 
895         /** Returns a proxy trust iterator that is used in 
896          *  <code>ProxyTrustVerifier</code> to retrieve this object's
897          *  trust verifier.
898          */
899         private ProxyTrustIterator getProxyTrustIterator() {
900 	    return new SingletonProxyTrustIterator(server);
901         }//end getProxyTrustIterator
902 
903         /** Performs various functions related to the trust verification
904          *  process for the current instance of this proxy class, as
905          *  detailed in the description for this class.
906          *
907          * @throws <code>InvalidObjectException</code> if any of the
908          *         requirements for trust verification (as detailed in the 
909          *         class description) are not satisfied.
910          */
911         private void readObject(ObjectInputStream s)  
912                                    throws IOException, ClassNotFoundException
913         {
914             /* Note that basic validation of the fields of this class was
915              * already performed in the readObject() method of this class'
916              * super class.
917              */
918             s.defaultReadObject();
919             /* Verify the server and its constraints */
920             ConstrainableProxyUtil.verifyConsistentConstraints
921                                                        (methodConstraints,
922                                                         server,
923                                                         methodMapArray);
924         }//end readObject
925 
926     }//end class ConstrainableFiddlerAdminProxy
927 
928 }//end class FiddlerAdminProxy