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 this(arg, check(arg));
443 }
444
445 ConstrainableFiddlerProxy(GetArg arg, MethodConstraints constraints) throws IOException{
446 super(arg);
447 this.methodConstraints = constraints;
448 }
449
450 /**
451 * Validate invariants before calling superclass constructor.
452 * @param arg
453 * @return
454 * @throws IOException
455 */
456 private static MethodConstraints check(GetArg arg) throws IOException {
457 FiddlerProxy fp = new FiddlerProxy(arg);
458 MethodConstraints methodConstraints
459 = (MethodConstraints) arg.get("methodConstraints", null);
460 /* Verify the server and its constraints */
461 MethodConstraints proxyCon = null;
462 if (fp.server instanceof RemoteMethodControl &&
463 (proxyCon = ((RemoteMethodControl)fp.server).getConstraints()) != null) {
464 // Constraints set during proxy deserialization.
465 return ConstrainableProxyUtil.reverseTranslateConstraints(
466 proxyCon, methodMapArray);
467 }
468 ConstrainableProxyUtil.verifyConsistentConstraints
469 (methodConstraints,
470 fp.server,
471 methodMapArray);
472 return methodConstraints;
473 }
474
475 /** Returns a copy of the given server proxy having the client method
476 * constraints that result after the specified method mapping is
477 * applied to the given client method constraints.
478 */
479 private static Fiddler constrainServer( Fiddler server,
480 MethodConstraints constraints )
481 {
482 MethodConstraints newConstraints
483 = ConstrainableProxyUtil.translateConstraints(constraints,
484 methodMapArray);
485 RemoteMethodControl constrainedServer =
486 ((RemoteMethodControl)server).setConstraints(newConstraints);
487
488 return ((Fiddler)constrainedServer);
489 }//end constrainServer
490
491 /** Returns a new copy of this proxy class
492 * (<code>ConstrainableFiddlerProxy</code>) with its client
493 * constraints set to the specified constraints. A <code>null</code>
494 * value is interpreted as mapping all methods to empty constraints.
495 */
496 public RemoteMethodControl setConstraints
497 (MethodConstraints constraints)
498 {
499 return ( new ConstrainableFiddlerProxy
500 (server, proxyID, constraints) );
501 }//end setConstraints
502
503 /** Returns the client constraints placed on the current instance
504 * of this proxy class (<code>ConstrainableFiddlerProxy</code>).
505 * The value returned by this method can be <code>null</code>,
506 * which is interpreted as mapping all methods to empty constraints.
507 */
508 public MethodConstraints getConstraints() {
509 return methodConstraints;
510 }//end getConstraints
511
512 /** Returns a proxy trust iterator that is used in
513 * <code>ProxyTrustVerifier</code> to retrieve this object's
514 * trust verifier.
515 */
516 private ProxyTrustIterator getProxyTrustIterator() {
517 return new SingletonProxyTrustIterator(server);
518 }//end getProxyTrustIterator
519
520 /** Performs various functions related to the trust verification
521 * process for the current instance of this proxy class, as
522 * detailed in the description for this class.
523 *
524 * @throws <code>InvalidObjectException</code> if any of the
525 * requirements for trust verification (as detailed in the
526 * class description) are not satisfied.
527 */
528 private void readObject(ObjectInputStream s)
529 throws IOException, ClassNotFoundException
530 {
531 /* Note that basic validation of the fields of this class was
532 * already performed in the readObject() method of this class'
533 * super class.
534 */
535 s.defaultReadObject();
536 /* Verify the server and its constraints */
537 ConstrainableProxyUtil.verifyConsistentConstraints
538 (methodConstraints,
539 server,
540 methodMapArray);
541 }//end readObject
542
543 }//end class ConstrainableFiddlerProxy
544
545 }//end class FiddlerProxy