1 /* 2 * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.rmi.rmic.newrmic.jrmp; 27 28 import com.sun.javadoc.ClassDoc; 29 import com.sun.javadoc.MethodDoc; 30 import com.sun.javadoc.Parameter; 31 import com.sun.javadoc.Type; 32 import java.io.IOException; 33 import java.io.ByteArrayOutputStream; 34 import java.io.DataOutputStream; 35 import java.security.MessageDigest; 36 import java.security.DigestOutputStream; 37 import java.security.NoSuchAlgorithmException; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.Comparator; 41 import java.util.List; 42 import java.util.HashMap; 43 import java.util.Map; 44 import sun.rmi.rmic.newrmic.BatchEnvironment; 45 46 import static sun.rmi.rmic.newrmic.Constants.*; 47 import static sun.rmi.rmic.newrmic.jrmp.Constants.*; 48 49 /** 50 * Encapsulates RMI-specific information about a remote implementation 51 * class (a class that implements one or more remote interfaces). 52 * 53 * WARNING: The contents of this source file are not part of any 54 * supported API. Code that depends on them does so at its own risk: 55 * they are subject to change or removal without notice. 56 * 57 * @author Peter Jones 58 **/ 59 final class RemoteClass { 60 61 /** rmic environment for this object */ 62 private final BatchEnvironment env; 63 64 /** the remote implementation class this object represents */ 65 private final ClassDoc implClass; 66 67 /** remote interfaces implemented by this class */ 68 private ClassDoc[] remoteInterfaces; 69 70 /** the remote methods of this class */ 71 private Method[] remoteMethods; 72 73 /** stub/skeleton "interface hash" for this class */ 74 private long interfaceHash; 75 76 /** 77 * Creates a RemoteClass instance that represents the RMI-specific 78 * information about the specified remote implementation class. 79 * 80 * If the class is not a valid remote implementation class or if 81 * some other error occurs, the return value will be null, and 82 * errors will have been reported to the supplied 83 * BatchEnvironment. 84 **/ 85 static RemoteClass forClass(BatchEnvironment env, ClassDoc implClass) { 86 RemoteClass remoteClass = new RemoteClass(env, implClass); 87 if (remoteClass.init()) { 88 return remoteClass; 89 } else { 90 return null; 91 } 92 } 93 94 /** 95 * Creates a RemoteClass instance for the specified class. The 96 * resulting object is not yet initialized. 97 **/ 98 private RemoteClass(BatchEnvironment env, ClassDoc implClass) { 99 this.env = env; 100 this.implClass = implClass; 101 } 102 103 /** 104 * Returns the ClassDoc for this remote implementation class. 105 **/ 106 ClassDoc classDoc() { 107 return implClass; 108 } 109 110 /** 111 * Returns the remote interfaces implemented by this remote 112 * implementation class. 113 * 114 * A remote interface is an interface that is a subinterface of 115 * java.rmi.Remote. The remote interfaces of a class are the 116 * direct superinterfaces of the class and all of its superclasses 117 * that are remote interfaces. 118 * 119 * The order of the array returned is arbitrary, and some elements 120 * may be superfluous (i.e., superinterfaces of other interfaces 121 * in the array). 122 **/ 123 ClassDoc[] remoteInterfaces() { 124 return remoteInterfaces.clone(); 125 } 126 127 /** 128 * Returns an array of RemoteClass.Method objects representing all 129 * of the remote methods of this remote implementation class (all 130 * of the member methods of the class's remote interfaces). 131 * 132 * The methods in the array are ordered according to a comparison 133 * of strings consisting of their name followed by their 134 * descriptor, so each method's index in the array corresponds to 135 * its "operation number" in the JDK 1.1 version of the JRMP 136 * stub/skeleton protocol. 137 **/ 138 Method[] remoteMethods() { 139 return remoteMethods.clone(); 140 } 141 142 /** 143 * Returns the "interface hash" used to match a stub/skeleton pair 144 * for this remote implementation class in the JDK 1.1 version of 145 * the JRMP stub/skeleton protocol. 146 **/ 147 long interfaceHash() { 148 return interfaceHash; 149 } 150 151 /** 152 * Validates this remote implementation class and computes the 153 * RMI-specific information. Returns true if successful, or false 154 * if an error occurred. 155 **/ 156 private boolean init() { 157 /* 158 * Verify that it is really a class, not an interface. 159 */ 160 if (implClass.isInterface()) { 161 env.error("rmic.cant.make.stubs.for.interface", 162 implClass.qualifiedName()); 163 return false; 164 } 165 166 /* 167 * Find all of the remote interfaces of our remote 168 * implementation class-- for each class up the superclass 169 * chain, add each directly-implemented interface that somehow 170 * extends Remote to a list. 171 */ 172 List<ClassDoc> remotesImplemented = new ArrayList<ClassDoc>(); 173 for (ClassDoc cl = implClass; cl != null; cl = cl.superclass()) { 174 for (ClassDoc intf : cl.interfaces()) { 175 /* 176 * Add interface to the list if it extends Remote and 177 * it is not already there. 178 */ 179 if (!remotesImplemented.contains(intf) && 180 intf.subclassOf(env.docRemote())) 181 { 182 remotesImplemented.add(intf); 183 if (env.verbose()) { 184 env.output("[found remote interface: " + 185 intf.qualifiedName() + "]"); 186 } 187 } 188 } 189 190 /* 191 * Verify that the candidate remote implementation class 192 * implements at least one remote interface directly. 193 */ 194 if (cl == implClass && remotesImplemented.isEmpty()) { 195 if (implClass.subclassOf(env.docRemote())) { 196 /* 197 * This error message is used if the class does 198 * implement a remote interface through one of its 199 * superclasses, but not directly. 200 */ 201 env.error("rmic.must.implement.remote.directly", 202 implClass.qualifiedName()); 203 } else { 204 /* 205 * This error message is used if the class does 206 * not implement a remote interface at all. 207 */ 208 env.error("rmic.must.implement.remote", 209 implClass.qualifiedName()); 210 } 211 return false; 212 } 213 } 214 215 /* 216 * Convert list of remote interfaces to an array 217 * (order is not important for this array). 218 */ 219 remoteInterfaces = 220 remotesImplemented.toArray( 221 new ClassDoc[remotesImplemented.size()]); 222 223 /* 224 * Collect the methods from all of the remote interfaces into 225 * a table, which maps from method name-and-descriptor string 226 * to Method object. 227 */ 228 Map<String,Method> methods = new HashMap<String,Method>(); 229 boolean errors = false; 230 for (ClassDoc intf : remotesImplemented) { 231 if (!collectRemoteMethods(intf, methods)) { 232 /* 233 * Continue iterating despite errors in order to 234 * generate more complete error report. 235 */ 236 errors = true; 237 } 238 } 239 if (errors) { 240 return false; 241 } 242 243 /* 244 * Sort table of remote methods into an array. The elements 245 * are sorted in ascending order of the string of the method's 246 * name and descriptor, so that each elements index is equal 247 * to its operation number in the JDK 1.1 version of the JRMP 248 * stub/skeleton protocol. 249 */ 250 String[] orderedKeys = 251 methods.keySet().toArray(new String[methods.size()]); 252 Arrays.sort(orderedKeys); 253 remoteMethods = new Method[methods.size()]; 254 for (int i = 0; i < remoteMethods.length; i++) { 255 remoteMethods[i] = methods.get(orderedKeys[i]); 256 if (env.verbose()) { 257 String msg = "[found remote method <" + i + ">: " + 258 remoteMethods[i].operationString(); 259 ClassDoc[] exceptions = remoteMethods[i].exceptionTypes(); 260 if (exceptions.length > 0) { 261 msg += " throws "; 262 for (int j = 0; j < exceptions.length; j++) { 263 if (j > 0) { 264 msg += ", "; 265 } 266 msg += exceptions[j].qualifiedName(); 267 } 268 } 269 msg += "\n\tname and descriptor = \"" + 270 remoteMethods[i].nameAndDescriptor(); 271 msg += "\n\tmethod hash = " + 272 remoteMethods[i].methodHash() + "]"; 273 env.output(msg); 274 } 275 } 276 277 /* 278 * Finally, pre-compute the interface hash to be used by 279 * stubs/skeletons for this remote class in the JDK 1.1 280 * version of the JRMP stub/skeleton protocol. 281 */ 282 interfaceHash = computeInterfaceHash(); 283 284 return true; 285 } 286 287 /** 288 * Collects and validates all methods from the specified interface 289 * and all of its superinterfaces as remote methods. Remote 290 * methods are added to the supplied table. Returns true if 291 * successful, or false if an error occurred. 292 **/ 293 private boolean collectRemoteMethods(ClassDoc intf, 294 Map<String,Method> table) 295 { 296 if (!intf.isInterface()) { 297 throw new AssertionError( 298 intf.qualifiedName() + " not an interface"); 299 } 300 301 boolean errors = false; 302 303 /* 304 * Search interface's declared methods. 305 */ 306 nextMethod: 307 for (MethodDoc method : intf.methods()) { 308 309 /* 310 * Verify that each method throws RemoteException (or a 311 * superclass of RemoteException). 312 */ 313 boolean hasRemoteException = false; 314 for (ClassDoc ex : method.thrownExceptions()) { 315 if (env.docRemoteException().subclassOf(ex)) { 316 hasRemoteException = true; 317 break; 318 } 319 } 320 321 /* 322 * If this method did not throw RemoteException as required, 323 * generate the error but continue, so that multiple such 324 * errors can be reported. 325 */ 326 if (!hasRemoteException) { 327 env.error("rmic.must.throw.remoteexception", 328 intf.qualifiedName(), 329 method.name() + method.signature()); 330 errors = true; 331 continue nextMethod; 332 } 333 334 /* 335 * Verify that the implementation of this method throws only 336 * java.lang.Exception or its subclasses (fix bugid 4092486). 337 * JRMP does not support remote methods throwing 338 * java.lang.Throwable or other subclasses. 339 */ 340 MethodDoc implMethod = findImplMethod(method); 341 if (implMethod != null) { // should not be null 342 for (ClassDoc ex : implMethod.thrownExceptions()) { 343 if (!ex.subclassOf(env.docException())) { 344 env.error("rmic.must.only.throw.exception", 345 implMethod.name() + implMethod.signature(), 346 ex.qualifiedName()); 347 errors = true; 348 continue nextMethod; 349 } 350 } 351 } 352 353 /* 354 * Create RemoteClass.Method object to represent this method 355 * found in a remote interface. 356 */ 357 Method newMethod = new Method(method); 358 359 /* 360 * Store remote method's representation in the table of 361 * remote methods found, keyed by its name and descriptor. 362 * 363 * If the table already contains an entry with the same 364 * method name and descriptor, then we must replace the 365 * old entry with a Method object that represents a legal 366 * combination of the old and the new methods; 367 * specifically, the combined method must have a throws 368 * clause that contains (only) all of the checked 369 * exceptions that can be thrown by both the old and the 370 * new method (see bugid 4070653). 371 */ 372 String key = newMethod.nameAndDescriptor(); 373 Method oldMethod = table.get(key); 374 if (oldMethod != null) { 375 newMethod = newMethod.mergeWith(oldMethod); 376 } 377 table.put(key, newMethod); 378 } 379 380 /* 381 * Recursively collect methods for all superinterfaces. 382 */ 383 for (ClassDoc superintf : intf.interfaces()) { 384 if (!collectRemoteMethods(superintf, table)) { 385 errors = true; 386 } 387 } 388 389 return !errors; 390 } 391 392 /** 393 * Returns the MethodDoc for the method of this remote 394 * implementation class that implements the specified remote 395 * method of a remote interface. Returns null if no matching 396 * method was found in this remote implementation class. 397 **/ 398 private MethodDoc findImplMethod(MethodDoc interfaceMethod) { 399 String name = interfaceMethod.name(); 400 String desc = Util.methodDescriptorOf(interfaceMethod); 401 for (MethodDoc implMethod : implClass.methods()) { 402 if (name.equals(implMethod.name()) && 403 desc.equals(Util.methodDescriptorOf(implMethod))) 404 { 405 return implMethod; 406 } 407 } 408 return null; 409 } 410 411 /** 412 * Computes the "interface hash" of the stub/skeleton pair for 413 * this remote implementation class. This is the 64-bit value 414 * used to enforce compatibility between a stub class and a 415 * skeleton class in the JDK 1.1 version of the JRMP stub/skeleton 416 * protocol. 417 * 418 * It is calculated using the first 64 bits of an SHA digest. The 419 * digest is of a stream consisting of the following data: 420 * (int) stub version number, always 1 421 * for each remote method, in order of operation number: 422 * (UTF-8) method name 423 * (UTF-8) method descriptor 424 * for each declared exception, in alphabetical name order: 425 * (UTF-8) name of exception class 426 * (where "UTF-8" includes a 16-bit length prefix as written by 427 * java.io.DataOutput.writeUTF). 428 **/ 429 private long computeInterfaceHash() { 430 long hash = 0; 431 ByteArrayOutputStream sink = new ByteArrayOutputStream(512); 432 try { 433 MessageDigest md = MessageDigest.getInstance("SHA"); 434 DataOutputStream out = new DataOutputStream( 435 new DigestOutputStream(sink, md)); 436 437 out.writeInt(INTERFACE_HASH_STUB_VERSION); 438 439 for (Method method : remoteMethods) { 440 MethodDoc methodDoc = method.methodDoc(); 441 442 out.writeUTF(methodDoc.name()); 443 out.writeUTF(Util.methodDescriptorOf(methodDoc)); 444 // descriptors already use binary names 445 446 ClassDoc exceptions[] = methodDoc.thrownExceptions(); 447 Arrays.sort(exceptions, new ClassDocComparator()); 448 for (ClassDoc ex : exceptions) { 449 out.writeUTF(Util.binaryNameOf(ex)); 450 } 451 } 452 out.flush(); 453 454 // use only the first 64 bits of the digest for the hash 455 byte hashArray[] = md.digest(); 456 for (int i = 0; i < Math.min(8, hashArray.length); i++) { 457 hash += ((long) (hashArray[i] & 0xFF)) << (i * 8); 458 } 459 } catch (IOException e) { 460 throw new AssertionError(e); 461 } catch (NoSuchAlgorithmException e) { 462 throw new AssertionError(e); 463 } 464 465 return hash; 466 } 467 468 /** 469 * Compares ClassDoc instances according to the lexicographic 470 * order of their binary names. 471 **/ 472 private static class ClassDocComparator implements Comparator<ClassDoc> { 473 public int compare(ClassDoc o1, ClassDoc o2) { 474 return Util.binaryNameOf(o1).compareTo(Util.binaryNameOf(o2)); 475 } 476 } 477 478 /** 479 * Encapsulates RMI-specific information about a particular remote 480 * method in the remote implementation class represented by the 481 * enclosing RemoteClass. 482 **/ 483 final class Method implements Cloneable { 484 485 /** 486 * MethodDoc for this remove method, from one of the remote 487 * interfaces that this method was found in. 488 * 489 * Note that this MethodDoc may be only one of multiple that 490 * correspond to this remote method object, if multiple of 491 * this class's remote interfaces contain methods with the 492 * same name and descriptor. Therefore, this MethodDoc may 493 * declare more exceptions thrown that this remote method 494 * does. 495 **/ 496 private final MethodDoc methodDoc; 497 498 /** java.rmi.server.Operation string for this remote method */ 499 private final String operationString; 500 501 /** name and descriptor of this remote method */ 502 private final String nameAndDescriptor; 503 504 /** JRMP "method hash" for this remote method */ 505 private final long methodHash; 506 507 /** 508 * Exceptions declared to be thrown by this remote method. 509 * 510 * This list may include superfluous entries, such as 511 * unchecked exceptions and subclasses of other entries. 512 **/ 513 private ClassDoc[] exceptionTypes; 514 515 /** 516 * Creates a new Method instance for the specified method. 517 **/ 518 Method(MethodDoc methodDoc) { 519 this.methodDoc = methodDoc; 520 exceptionTypes = methodDoc.thrownExceptions(); 521 /* 522 * Sort exception types to improve consistency with 523 * previous implementations. 524 */ 525 Arrays.sort(exceptionTypes, new ClassDocComparator()); 526 operationString = computeOperationString(); 527 nameAndDescriptor = 528 methodDoc.name() + Util.methodDescriptorOf(methodDoc); 529 methodHash = computeMethodHash(); 530 } 531 532 /** 533 * Returns the MethodDoc object corresponding to this method 534 * of a remote interface. 535 **/ 536 MethodDoc methodDoc() { 537 return methodDoc; 538 } 539 540 /** 541 * Returns the parameter types declared by this method. 542 **/ 543 Type[] parameterTypes() { 544 Parameter[] parameters = methodDoc.parameters(); 545 Type[] paramTypes = new Type[parameters.length]; 546 for (int i = 0; i < paramTypes.length; i++) { 547 paramTypes[i] = parameters[i].type(); 548 } 549 return paramTypes; 550 } 551 552 /** 553 * Returns the exception types declared to be thrown by this 554 * remote method. 555 * 556 * For methods with the same name and descriptor inherited 557 * from multiple remote interfaces, the array will contain the 558 * set of exceptions declared in all of the interfaces' 559 * methods that can be legally thrown by all of them. 560 **/ 561 ClassDoc[] exceptionTypes() { 562 return exceptionTypes.clone(); 563 } 564 565 /** 566 * Returns the JRMP "method hash" used to identify this remote 567 * method in the JDK 1.2 version of the stub protocol. 568 **/ 569 long methodHash() { 570 return methodHash; 571 } 572 573 /** 574 * Returns the string representation of this method 575 * appropriate for the construction of a 576 * java.rmi.server.Operation object. 577 **/ 578 String operationString() { 579 return operationString; 580 } 581 582 /** 583 * Returns a string consisting of this method's name followed 584 * by its descriptor. 585 **/ 586 String nameAndDescriptor() { 587 return nameAndDescriptor; 588 } 589 590 /** 591 * Returns a new Method object that is a legal combination of 592 * this Method object and another one. 593 * 594 * Doing this requires determining the exceptions declared by 595 * the combined method, which must be (only) all of the 596 * exceptions declared in both old Methods that may thrown in 597 * either of them. 598 **/ 599 Method mergeWith(Method other) { 600 if (!nameAndDescriptor().equals(other.nameAndDescriptor())) { 601 throw new AssertionError( 602 "attempt to merge method \"" + 603 other.nameAndDescriptor() + "\" with \"" + 604 nameAndDescriptor()); 605 } 606 607 List<ClassDoc> legalExceptions = new ArrayList<ClassDoc>(); 608 collectCompatibleExceptions( 609 other.exceptionTypes, exceptionTypes, legalExceptions); 610 collectCompatibleExceptions( 611 exceptionTypes, other.exceptionTypes, legalExceptions); 612 613 Method merged = clone(); 614 merged.exceptionTypes = 615 legalExceptions.toArray(new ClassDoc[legalExceptions.size()]); 616 617 return merged; 618 } 619 620 /** 621 * Cloning is supported by returning a shallow copy of this 622 * object. 623 **/ 624 protected Method clone() { 625 try { 626 return (Method) super.clone(); 627 } catch (CloneNotSupportedException e) { 628 throw new AssertionError(e); 629 } 630 } 631 632 /** 633 * Adds to the supplied list all exceptions in the "froms" 634 * array that are subclasses of an exception in the "withs" 635 * array. 636 **/ 637 private void collectCompatibleExceptions(ClassDoc[] froms, 638 ClassDoc[] withs, 639 List<ClassDoc> list) 640 { 641 for (ClassDoc from : froms) { 642 if (!list.contains(from)) { 643 for (ClassDoc with : withs) { 644 if (from.subclassOf(with)) { 645 list.add(from); 646 break; 647 } 648 } 649 } 650 } 651 } 652 653 /** 654 * Computes the JRMP "method hash" of this remote method. The 655 * method hash is a long containing the first 64 bits of the 656 * SHA digest from the UTF-8 encoded string of the method name 657 * and descriptor. 658 **/ 659 private long computeMethodHash() { 660 long hash = 0; 661 ByteArrayOutputStream sink = new ByteArrayOutputStream(512); 662 try { 663 MessageDigest md = MessageDigest.getInstance("SHA"); 664 DataOutputStream out = new DataOutputStream( 665 new DigestOutputStream(sink, md)); 666 667 String methodString = nameAndDescriptor(); 668 out.writeUTF(methodString); 669 670 // use only the first 64 bits of the digest for the hash 671 out.flush(); 672 byte hashArray[] = md.digest(); 673 for (int i = 0; i < Math.min(8, hashArray.length); i++) { 674 hash += ((long) (hashArray[i] & 0xFF)) << (i * 8); 675 } 676 } catch (IOException e) { 677 throw new AssertionError(e); 678 } catch (NoSuchAlgorithmException e) { 679 throw new AssertionError(e); 680 } 681 682 return hash; 683 } 684 685 /** 686 * Computes the string representation of this method 687 * appropriate for the construction of a 688 * java.rmi.server.Operation object. 689 **/ 690 private String computeOperationString() { 691 /* 692 * To be consistent with previous implementations, we use 693 * the deprecated style of placing the "[]" for the return 694 * type (if any) after the parameter list. 695 */ 696 Type returnType = methodDoc.returnType(); 697 String op = returnType.qualifiedTypeName() + " " + 698 methodDoc.name() + "("; 699 Parameter[] parameters = methodDoc.parameters(); 700 for (int i = 0; i < parameters.length; i++) { 701 if (i > 0) { 702 op += ", "; 703 } 704 op += parameters[i].type().toString(); 705 } 706 op += ")" + returnType.dimension(); 707 return op; 708 } 709 } 710 }