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