1 /* 2 * Copyright (c) 2003, 2013, 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.Type; 31 import java.io.IOException; 32 import java.util.ArrayList; 33 import java.util.Iterator; 34 import java.util.List; 35 import sun.rmi.rmic.newrmic.BatchEnvironment; 36 import sun.rmi.rmic.newrmic.IndentingWriter; 37 38 import static sun.rmi.rmic.newrmic.Constants.*; 39 import static sun.rmi.rmic.newrmic.jrmp.Constants.*; 40 41 /** 42 * Writes the source code for the stub class and (optionally) skeleton 43 * class for a particular remote implementation class. 44 * 45 * WARNING: The contents of this source file are not part of any 46 * supported API. Code that depends on them does so at its own risk: 47 * they are subject to change or removal without notice. 48 * 49 * @author Peter Jones 50 **/ 51 class StubSkeletonWriter { 52 53 /** rmic environment for this object */ 54 private final BatchEnvironment env; 55 56 /** the remote implementation class to generate code for */ 57 private final RemoteClass remoteClass; 58 59 /** version of the JRMP stub protocol to generate code for */ 60 private final StubVersion version; 61 62 /* 63 * binary names of the stub and skeleton classes to generate for 64 * the remote class 65 */ 66 private final String stubClassName; 67 private final String skeletonClassName; 68 69 /* package name and simple names of the stub and skeleton classes */ 70 private final String packageName; 71 private final String stubClassSimpleName; 72 private final String skeletonClassSimpleName; 73 74 /** remote methods of class, indexed by operation number */ 75 private final RemoteClass.Method[] remoteMethods; 76 77 /** 78 * Names to use for the java.lang.reflect.Method static fields in 79 * the generated stub class corresponding to each remote method. 80 **/ 81 private final String[] methodFieldNames; 82 83 /** 84 * Creates a StubSkeletonWriter instance for the specified remote 85 * implementation class. The generated code will implement the 86 * specified JRMP stub protocol version. 87 **/ 88 StubSkeletonWriter(BatchEnvironment env, 89 RemoteClass remoteClass, 90 StubVersion version) 91 { 92 this.env = env; 93 this.remoteClass = remoteClass; 94 this.version = version; 95 96 stubClassName = Util.binaryNameOf(remoteClass.classDoc()) + "_Stub"; 97 skeletonClassName = 98 Util.binaryNameOf(remoteClass.classDoc()) + "_Skel"; 99 100 int i = stubClassName.lastIndexOf('.'); 101 packageName = (i != -1 ? stubClassName.substring(0, i) : ""); 102 stubClassSimpleName = stubClassName.substring(i + 1); 103 skeletonClassSimpleName = skeletonClassName.substring(i + 1); 104 105 remoteMethods = remoteClass.remoteMethods(); 106 methodFieldNames = nameMethodFields(remoteMethods); 107 } 108 109 /** 110 * Returns the binary name of the stub class to generate for the 111 * remote implementation class. 112 **/ 113 String stubClassName() { 114 return stubClassName; 115 } 116 117 /** 118 * Returns the binary name of the skeleton class to generate for 119 * the remote implementation class. 120 **/ 121 String skeletonClassName() { 122 return skeletonClassName; 123 } 124 125 /** 126 * Writes the stub class for the remote class to a stream. 127 **/ 128 void writeStub(IndentingWriter p) throws IOException { 129 130 /* 131 * Write boiler plate comment. 132 */ 133 p.pln("// Stub class generated by rmic, do not edit."); 134 p.pln("// Contents subject to change without notice."); 135 p.pln(); 136 137 /* 138 * If remote implementation class was in a particular package, 139 * declare the stub class to be in the same package. 140 */ 141 if (!packageName.equals("")) { 142 p.pln("package " + packageName + ";"); 143 p.pln(); 144 } 145 146 /* 147 * Declare the stub class; implement all remote interfaces. 148 */ 149 p.plnI("public final class " + stubClassSimpleName); 150 p.pln("extends " + REMOTE_STUB); 151 ClassDoc[] remoteInterfaces = remoteClass.remoteInterfaces(); 152 if (remoteInterfaces.length > 0) { 153 p.p("implements "); 154 for (int i = 0; i < remoteInterfaces.length; i++) { 155 if (i > 0) { 156 p.p(", "); 157 } 158 p.p(remoteInterfaces[i].qualifiedName()); 159 } 160 p.pln(); 161 } 162 p.pOlnI("{"); 163 164 if (version == StubVersion.V1_1 || 165 version == StubVersion.VCOMPAT) 166 { 167 writeOperationsArray(p); 168 p.pln(); 169 writeInterfaceHash(p); 170 p.pln(); 171 } 172 173 if (version == StubVersion.VCOMPAT || 174 version == StubVersion.V1_2) 175 { 176 p.pln("private static final long serialVersionUID = " + 177 STUB_SERIAL_VERSION_UID + ";"); 178 p.pln(); 179 180 /* 181 * We only need to declare and initialize the static fields of 182 * Method objects for each remote method if there are any remote 183 * methods; otherwise, skip this code entirely, to avoid generating 184 * a try/catch block for a checked exception that cannot occur 185 * (see bugid 4125181). 186 */ 187 if (methodFieldNames.length > 0) { 188 if (version == StubVersion.VCOMPAT) { 189 p.pln("private static boolean useNewInvoke;"); 190 } 191 writeMethodFieldDeclarations(p); 192 p.pln(); 193 194 /* 195 * Initialize java.lang.reflect.Method fields for each remote 196 * method in a static initializer. 197 */ 198 p.plnI("static {"); 199 p.plnI("try {"); 200 if (version == StubVersion.VCOMPAT) { 201 /* 202 * Fat stubs must determine whether the API required for 203 * the JDK 1.2 stub protocol is supported in the current 204 * runtime, so that it can use it if supported. This is 205 * determined by using the Reflection API to test if the 206 * new invoke method on RemoteRef exists, and setting the 207 * static boolean "useNewInvoke" to true if it does, or 208 * to false if a NoSuchMethodException is thrown. 209 */ 210 p.plnI(REMOTE_REF + ".class.getMethod(\"invoke\","); 211 p.plnI("new java.lang.Class[] {"); 212 p.pln(REMOTE + ".class,"); 213 p.pln("java.lang.reflect.Method.class,"); 214 p.pln("java.lang.Object[].class,"); 215 p.pln("long.class"); 216 p.pOln("});"); 217 p.pO(); 218 p.pln("useNewInvoke = true;"); 219 } 220 writeMethodFieldInitializers(p); 221 p.pOlnI("} catch (java.lang.NoSuchMethodException e) {"); 222 if (version == StubVersion.VCOMPAT) { 223 p.pln("useNewInvoke = false;"); 224 } else { 225 p.plnI("throw new java.lang.NoSuchMethodError("); 226 p.pln("\"stub class initialization failed\");"); 227 p.pO(); 228 } 229 p.pOln("}"); // end try/catch block 230 p.pOln("}"); // end static initializer 231 p.pln(); 232 } 233 } 234 235 writeStubConstructors(p); 236 p.pln(); 237 238 /* 239 * Write each stub method. 240 */ 241 if (remoteMethods.length > 0) { 242 p.pln("// methods from remote interfaces"); 243 for (int i = 0; i < remoteMethods.length; ++i) { 244 p.pln(); 245 writeStubMethod(p, i); 246 } 247 } 248 249 p.pOln("}"); // end stub class 250 } 251 252 /** 253 * Writes the constructors for the stub class. 254 **/ 255 private void writeStubConstructors(IndentingWriter p) 256 throws IOException 257 { 258 p.pln("// constructors"); 259 260 /* 261 * Only stubs compatible with the JDK 1.1 stub protocol need 262 * a no-arg constructor; later versions use reflection to find 263 * the constructor that directly takes a RemoteRef argument. 264 */ 265 if (version == StubVersion.V1_1 || 266 version == StubVersion.VCOMPAT) 267 { 268 p.plnI("public " + stubClassSimpleName + "() {"); 269 p.pln("super();"); 270 p.pOln("}"); 271 } 272 273 p.plnI("public " + stubClassSimpleName + "(" + REMOTE_REF + " ref) {"); 274 p.pln("super(ref);"); 275 p.pOln("}"); 276 } 277 278 /** 279 * Writes the stub method for the remote method with the given 280 * operation number. 281 **/ 282 private void writeStubMethod(IndentingWriter p, int opnum) 283 throws IOException 284 { 285 RemoteClass.Method method = remoteMethods[opnum]; 286 MethodDoc methodDoc = method.methodDoc(); 287 String methodName = methodDoc.name(); 288 Type[] paramTypes = method.parameterTypes(); 289 String paramNames[] = nameParameters(paramTypes); 290 Type returnType = methodDoc.returnType(); 291 ClassDoc[] exceptions = method.exceptionTypes(); 292 293 /* 294 * Declare stub method; throw exceptions declared in remote 295 * interface(s). 296 */ 297 p.pln("// implementation of " + 298 Util.getFriendlyUnqualifiedSignature(methodDoc)); 299 p.p("public " + returnType.toString() + " " + methodName + "("); 300 for (int i = 0; i < paramTypes.length; i++) { 301 if (i > 0) { 302 p.p(", "); 303 } 304 p.p(paramTypes[i].toString() + " " + paramNames[i]); 305 } 306 p.plnI(")"); 307 if (exceptions.length > 0) { 308 p.p("throws "); 309 for (int i = 0; i < exceptions.length; i++) { 310 if (i > 0) { 311 p.p(", "); 312 } 313 p.p(exceptions[i].qualifiedName()); 314 } 315 p.pln(); 316 } 317 p.pOlnI("{"); 318 319 /* 320 * The RemoteRef.invoke methods throw Exception, but unless 321 * this stub method throws Exception as well, we must catch 322 * Exceptions thrown from the invocation. So we must catch 323 * Exception and rethrow something we can throw: 324 * UnexpectedException, which is a subclass of 325 * RemoteException. But for any subclasses of Exception that 326 * we can throw, like RemoteException, RuntimeException, and 327 * any of the exceptions declared by this stub method, we want 328 * them to pass through unmodified, so first we must catch any 329 * such exceptions and rethrow them directly. 330 * 331 * We have to be careful generating the rethrowing catch 332 * blocks here, because javac will flag an error if there are 333 * any unreachable catch blocks, i.e. if the catch of an 334 * exception class follows a previous catch of it or of one of 335 * its superclasses. The following method invocation takes 336 * care of these details. 337 */ 338 List<ClassDoc> catchList = computeUniqueCatchList(exceptions); 339 340 /* 341 * If we need to catch any particular exceptions (i.e. this method 342 * does not declare java.lang.Exception), put the entire stub 343 * method in a try block. 344 */ 345 if (catchList.size() > 0) { 346 p.plnI("try {"); 347 } 348 349 if (version == StubVersion.VCOMPAT) { 350 p.plnI("if (useNewInvoke) {"); 351 } 352 if (version == StubVersion.VCOMPAT || 353 version == StubVersion.V1_2) 354 { 355 if (!Util.isVoid(returnType)) { 356 p.p("Object $result = "); // REMIND: why $? 357 } 358 p.p("ref.invoke(this, " + methodFieldNames[opnum] + ", "); 359 if (paramTypes.length > 0) { 360 p.p("new java.lang.Object[] {"); 361 for (int i = 0; i < paramTypes.length; i++) { 362 if (i > 0) 363 p.p(", "); 364 p.p(wrapArgumentCode(paramTypes[i], paramNames[i])); 365 } 366 p.p("}"); 367 } else { 368 p.p("null"); 369 } 370 p.pln(", " + method.methodHash() + "L);"); 371 if (!Util.isVoid(returnType)) { 372 p.pln("return " + 373 unwrapArgumentCode(returnType, "$result") + ";"); 374 } 375 } 376 if (version == StubVersion.VCOMPAT) { 377 p.pOlnI("} else {"); 378 } 379 if (version == StubVersion.V1_1 || 380 version == StubVersion.VCOMPAT) 381 { 382 p.pln(REMOTE_CALL + " call = ref.newCall((" + REMOTE_OBJECT + 383 ") this, operations, " + opnum + ", interfaceHash);"); 384 385 if (paramTypes.length > 0) { 386 p.plnI("try {"); 387 p.pln("java.io.ObjectOutput out = call.getOutputStream();"); 388 writeMarshalArguments(p, "out", paramTypes, paramNames); 389 p.pOlnI("} catch (java.io.IOException e) {"); 390 p.pln("throw new " + MARSHAL_EXCEPTION + 391 "(\"error marshalling arguments\", e);"); 392 p.pOln("}"); 393 } 394 395 p.pln("ref.invoke(call);"); 396 397 if (Util.isVoid(returnType)) { 398 p.pln("ref.done(call);"); 399 } else { 400 p.pln(returnType.toString() + " $result;"); 401 // REMIND: why $? 402 p.plnI("try {"); 403 p.pln("java.io.ObjectInput in = call.getInputStream();"); 404 boolean objectRead = 405 writeUnmarshalArgument(p, "in", returnType, "$result"); 406 p.pln(";"); 407 p.pOlnI("} catch (java.io.IOException e) {"); 408 p.pln("throw new " + UNMARSHAL_EXCEPTION + 409 "(\"error unmarshalling return\", e);"); 410 /* 411 * If any only if readObject has been invoked, we must catch 412 * ClassNotFoundException as well as IOException. 413 */ 414 if (objectRead) { 415 p.pOlnI("} catch (java.lang.ClassNotFoundException e) {"); 416 p.pln("throw new " + UNMARSHAL_EXCEPTION + 417 "(\"error unmarshalling return\", e);"); 418 } 419 p.pOlnI("} finally {"); 420 p.pln("ref.done(call);"); 421 p.pOln("}"); 422 p.pln("return $result;"); 423 } 424 } 425 if (version == StubVersion.VCOMPAT) { 426 p.pOln("}"); // end if/else (useNewInvoke) block 427 } 428 429 /* 430 * If we need to catch any particular exceptions, finally write 431 * the catch blocks for them, rethrow any other Exceptions with an 432 * UnexpectedException, and end the try block. 433 */ 434 if (catchList.size() > 0) { 435 for (ClassDoc catchClass : catchList) { 436 p.pOlnI("} catch (" + catchClass.qualifiedName() + " e) {"); 437 p.pln("throw e;"); 438 } 439 p.pOlnI("} catch (java.lang.Exception e) {"); 440 p.pln("throw new " + UNEXPECTED_EXCEPTION + 441 "(\"undeclared checked exception\", e);"); 442 p.pOln("}"); // end try/catch block 443 } 444 445 p.pOln("}"); // end stub method 446 } 447 448 /** 449 * Computes the exceptions that need to be caught and rethrown in 450 * a stub method before wrapping Exceptions in 451 * UnexpectedExceptions, given the exceptions declared in the 452 * throws clause of the method. Returns a list containing the 453 * exception to catch. Each exception is guaranteed to be unique, 454 * i.e. not a subclass of any of the other exceptions in the list, 455 * so the catch blocks for these exceptions may be generated in 456 * any order relative to each other. 457 * 458 * RemoteException and RuntimeException are each automatically 459 * placed in the returned list (unless any of their superclasses 460 * are already present), since those exceptions should always be 461 * directly rethrown by a stub method. 462 * 463 * The returned list will be empty if java.lang.Exception or one 464 * of its superclasses is in the throws clause of the method, 465 * indicating that no exceptions need to be caught. 466 **/ 467 private List<ClassDoc> computeUniqueCatchList(ClassDoc[] exceptions) { 468 List<ClassDoc> uniqueList = new ArrayList<ClassDoc>(); 469 470 uniqueList.add(env.docRuntimeException()); 471 uniqueList.add(env.docRemoteException()); // always catch/rethrow these 472 473 /* For each exception declared by the stub method's throws clause: */ 474 nextException: 475 for (ClassDoc ex : exceptions) { 476 if (env.docException().subclassOf(ex)) { 477 /* 478 * If java.lang.Exception (or a superclass) was declared 479 * in the throws clause of this stub method, then we don't 480 * have to bother catching anything; clear the list and 481 * return. 482 */ 483 uniqueList.clear(); 484 break; 485 } else if (!ex.subclassOf(env.docException())) { 486 /* 487 * Ignore other Throwables that do not extend Exception, 488 * because they cannot be thrown by the invoke methods. 489 */ 490 continue; 491 } 492 /* 493 * Compare this exception against the current list of 494 * exceptions that need to be caught: 495 */ 496 for (Iterator<ClassDoc> i = uniqueList.iterator(); i.hasNext();) { 497 ClassDoc ex2 = i.next(); 498 if (ex.subclassOf(ex2)) { 499 /* 500 * If a superclass of this exception is already on 501 * the list to catch, then ignore this one and continue; 502 */ 503 continue nextException; 504 } else if (ex2.subclassOf(ex)) { 505 /* 506 * If a subclass of this exception is on the list 507 * to catch, then remove it; 508 */ 509 i.remove(); 510 } 511 } 512 /* This exception is unique: add it to the list to catch. */ 513 uniqueList.add(ex); 514 } 515 return uniqueList; 516 } 517 518 /** 519 * Writes the skeleton for the remote class to a stream. 520 **/ 521 void writeSkeleton(IndentingWriter p) throws IOException { 522 if (version == StubVersion.V1_2) { 523 throw new AssertionError( 524 "should not generate skeleton for version " + version); 525 } 526 527 /* 528 * Write boiler plate comment. 529 */ 530 p.pln("// Skeleton class generated by rmic, do not edit."); 531 p.pln("// Contents subject to change without notice."); 532 p.pln(); 533 534 /* 535 * If remote implementation class was in a particular package, 536 * declare the skeleton class to be in the same package. 537 */ 538 if (!packageName.equals("")) { 539 p.pln("package " + packageName + ";"); 540 p.pln(); 541 } 542 543 /* 544 * Declare the skeleton class. 545 */ 546 p.plnI("public final class " + skeletonClassSimpleName); 547 p.pln("implements " + SKELETON); 548 p.pOlnI("{"); 549 550 writeOperationsArray(p); 551 p.pln(); 552 553 writeInterfaceHash(p); 554 p.pln(); 555 556 /* 557 * Define the getOperations() method. 558 */ 559 p.plnI("public " + OPERATION + "[] getOperations() {"); 560 p.pln("return (" + OPERATION + "[]) operations.clone();"); 561 p.pOln("}"); 562 p.pln(); 563 564 /* 565 * Define the dispatch() method. 566 */ 567 p.plnI("public void dispatch(" + REMOTE + " obj, " + 568 REMOTE_CALL + " call, int opnum, long hash)"); 569 p.pln("throws java.lang.Exception"); 570 p.pOlnI("{"); 571 572 if (version == StubVersion.VCOMPAT) { 573 p.plnI("if (opnum < 0) {"); 574 if (remoteMethods.length > 0) { 575 for (int opnum = 0; opnum < remoteMethods.length; opnum++) { 576 if (opnum > 0) 577 p.pO("} else "); 578 p.plnI("if (hash == " + 579 remoteMethods[opnum].methodHash() + "L) {"); 580 p.pln("opnum = " + opnum + ";"); 581 } 582 p.pOlnI("} else {"); 583 } 584 /* 585 * Skeleton throws UnmarshalException if it does not recognize 586 * the method hash; this is what UnicastServerRef.dispatch() 587 * would do. 588 */ 589 p.pln("throw new " + 590 UNMARSHAL_EXCEPTION + "(\"invalid method hash\");"); 591 if (remoteMethods.length > 0) { 592 p.pOln("}"); 593 } 594 /* 595 * Ignore the validation of the interface hash if the 596 * operation number was negative, since it is really a 597 * method hash instead. 598 */ 599 p.pOlnI("} else {"); 600 } 601 602 p.plnI("if (hash != interfaceHash)"); 603 p.pln("throw new " + 604 SKELETON_MISMATCH_EXCEPTION + "(\"interface hash mismatch\");"); 605 p.pO(); 606 607 if (version == StubVersion.VCOMPAT) { 608 p.pOln("}"); // end if/else (opnum < 0) block 609 } 610 p.pln(); 611 612 /* 613 * Cast remote object reference to the remote implementation 614 * class, if it's not private. We don't use the binary name 615 * of the class like previous implementations did because that 616 * would not compile with javac (since 1.4.1). If the remote 617 * implementation class is private, then we can't cast to it 618 * like previous implementations did because that also would 619 * not compile with javac-- so instead, we'll have to try to 620 * cast to the remote interface for each remote method. 621 */ 622 if (!remoteClass.classDoc().isPrivate()) { 623 p.pln(remoteClass.classDoc().qualifiedName() + " server = (" + 624 remoteClass.classDoc().qualifiedName() + ") obj;"); 625 } 626 627 /* 628 * Process call according to the operation number. 629 */ 630 p.plnI("switch (opnum) {"); 631 for (int opnum = 0; opnum < remoteMethods.length; opnum++) { 632 writeSkeletonDispatchCase(p, opnum); 633 } 634 p.pOlnI("default:"); 635 /* 636 * Skeleton throws UnmarshalException if it does not recognize 637 * the operation number; this is consistent with the case of an 638 * unrecognized method hash. 639 */ 640 p.pln("throw new " + UNMARSHAL_EXCEPTION + 641 "(\"invalid method number\");"); 642 p.pOln("}"); // end switch statement 643 644 p.pOln("}"); // end dispatch() method 645 646 p.pOln("}"); // end skeleton class 647 } 648 649 /** 650 * Writes the case block for the skeleton's dispatch method for 651 * the remote method with the given "opnum". 652 **/ 653 private void writeSkeletonDispatchCase(IndentingWriter p, int opnum) 654 throws IOException 655 { 656 RemoteClass.Method method = remoteMethods[opnum]; 657 MethodDoc methodDoc = method.methodDoc(); 658 String methodName = methodDoc.name(); 659 Type paramTypes[] = method.parameterTypes(); 660 String paramNames[] = nameParameters(paramTypes); 661 Type returnType = methodDoc.returnType(); 662 663 p.pOlnI("case " + opnum + ": // " + 664 Util.getFriendlyUnqualifiedSignature(methodDoc)); 665 /* 666 * Use nested block statement inside case to provide an independent 667 * namespace for local variables used to unmarshal parameters for 668 * this remote method. 669 */ 670 p.pOlnI("{"); 671 672 if (paramTypes.length > 0) { 673 /* 674 * Declare local variables to hold arguments. 675 */ 676 for (int i = 0; i < paramTypes.length; i++) { 677 p.pln(paramTypes[i].toString() + " " + paramNames[i] + ";"); 678 } 679 680 /* 681 * Unmarshal arguments from call stream. 682 */ 683 p.plnI("try {"); 684 p.pln("java.io.ObjectInput in = call.getInputStream();"); 685 boolean objectsRead = writeUnmarshalArguments(p, "in", 686 paramTypes, paramNames); 687 p.pOlnI("} catch (java.io.IOException e) {"); 688 p.pln("throw new " + UNMARSHAL_EXCEPTION + 689 "(\"error unmarshalling arguments\", e);"); 690 /* 691 * If any only if readObject has been invoked, we must catch 692 * ClassNotFoundException as well as IOException. 693 */ 694 if (objectsRead) { 695 p.pOlnI("} catch (java.lang.ClassNotFoundException e) {"); 696 p.pln("throw new " + UNMARSHAL_EXCEPTION + 697 "(\"error unmarshalling arguments\", e);"); 698 } 699 p.pOlnI("} finally {"); 700 p.pln("call.releaseInputStream();"); 701 p.pOln("}"); 702 } else { 703 p.pln("call.releaseInputStream();"); 704 } 705 706 if (!Util.isVoid(returnType)) { 707 /* 708 * Declare variable to hold return type, if not void. 709 */ 710 p.p(returnType.toString() + " $result = "); 711 // REMIND: why $? 712 } 713 714 /* 715 * Invoke the method on the server object. If the remote 716 * implementation class is private, then we don't have a 717 * reference cast to it, and so we try to cast to the remote 718 * object reference to the method's declaring interface here. 719 */ 720 String target = remoteClass.classDoc().isPrivate() ? 721 "((" + methodDoc.containingClass().qualifiedName() + ") obj)" : 722 "server"; 723 p.p(target + "." + methodName + "("); 724 for (int i = 0; i < paramNames.length; i++) { 725 if (i > 0) 726 p.p(", "); 727 p.p(paramNames[i]); 728 } 729 p.pln(");"); 730 731 /* 732 * Always invoke getResultStream(true) on the call object to send 733 * the indication of a successful invocation to the caller. If 734 * the return type is not void, keep the result stream and marshal 735 * the return value. 736 */ 737 p.plnI("try {"); 738 if (!Util.isVoid(returnType)) { 739 p.p("java.io.ObjectOutput out = "); 740 } 741 p.pln("call.getResultStream(true);"); 742 if (!Util.isVoid(returnType)) { 743 writeMarshalArgument(p, "out", returnType, "$result"); 744 p.pln(";"); 745 } 746 p.pOlnI("} catch (java.io.IOException e) {"); 747 p.pln("throw new " + 748 MARSHAL_EXCEPTION + "(\"error marshalling return\", e);"); 749 p.pOln("}"); 750 751 p.pln("break;"); // break from switch statement 752 753 p.pOlnI("}"); // end nested block statement 754 p.pln(); 755 } 756 757 /** 758 * Writes declaration and initializer for "operations" static array. 759 **/ 760 private void writeOperationsArray(IndentingWriter p) 761 throws IOException 762 { 763 p.plnI("private static final " + OPERATION + "[] operations = {"); 764 for (int i = 0; i < remoteMethods.length; i++) { 765 if (i > 0) 766 p.pln(","); 767 p.p("new " + OPERATION + "(\"" + 768 remoteMethods[i].operationString() + "\")"); 769 } 770 p.pln(); 771 p.pOln("};"); 772 } 773 774 /** 775 * Writes declaration and initializer for "interfaceHash" static field. 776 **/ 777 private void writeInterfaceHash(IndentingWriter p) 778 throws IOException 779 { 780 p.pln("private static final long interfaceHash = " + 781 remoteClass.interfaceHash() + "L;"); 782 } 783 784 /** 785 * Writes declaration for java.lang.reflect.Method static fields 786 * corresponding to each remote method in a stub. 787 **/ 788 private void writeMethodFieldDeclarations(IndentingWriter p) 789 throws IOException 790 { 791 for (String name : methodFieldNames) { 792 p.pln("private static java.lang.reflect.Method " + name + ";"); 793 } 794 } 795 796 /** 797 * Writes code to initialize the static fields for each method 798 * using the Java Reflection API. 799 **/ 800 private void writeMethodFieldInitializers(IndentingWriter p) 801 throws IOException 802 { 803 for (int i = 0; i < methodFieldNames.length; i++) { 804 p.p(methodFieldNames[i] + " = "); 805 /* 806 * Look up the Method object in the somewhat arbitrary 807 * interface that we find in the Method object. 808 */ 809 RemoteClass.Method method = remoteMethods[i]; 810 MethodDoc methodDoc = method.methodDoc(); 811 String methodName = methodDoc.name(); 812 Type paramTypes[] = method.parameterTypes(); 813 814 p.p(methodDoc.containingClass().qualifiedName() + ".class.getMethod(\"" + 815 methodName + "\", new java.lang.Class[] {"); 816 for (int j = 0; j < paramTypes.length; j++) { 817 if (j > 0) 818 p.p(", "); 819 p.p(paramTypes[j].toString() + ".class"); 820 } 821 p.pln("});"); 822 } 823 } 824 825 826 /* 827 * Following are a series of static utility methods useful during 828 * the code generation process: 829 */ 830 831 /** 832 * Generates an array of names for fields correspondins to the 833 * given array of remote methods. Each name in the returned array 834 * is guaranteed to be unique. 835 * 836 * The name of a method is included in its corresponding field 837 * name to enhance readability of the generated code. 838 **/ 839 private static String[] nameMethodFields(RemoteClass.Method[] methods) { 840 String[] names = new String[methods.length]; 841 for (int i = 0; i < names.length; i++) { 842 names[i] = "$method_" + methods[i].methodDoc().name() + "_" + i; 843 } 844 return names; 845 } 846 847 /** 848 * Generates an array of names for parameters corresponding to the 849 * given array of types for the parameters. Each name in the 850 * returned array is guaranteed to be unique. 851 * 852 * A representation of the type of a parameter is included in its 853 * corresponding parameter name to enhance the readability of the 854 * generated code. 855 **/ 856 private static String[] nameParameters(Type[] types) { 857 String[] names = new String[types.length]; 858 for (int i = 0; i < names.length; i++) { 859 names[i] = "$param_" + 860 generateNameFromType(types[i]) + "_" + (i + 1); 861 } 862 return names; 863 } 864 865 /** 866 * Generates a readable string representing the given type 867 * suitable for embedding within a Java identifier. 868 **/ 869 private static String generateNameFromType(Type type) { 870 String name = type.typeName().replace('.', '$'); 871 int dimensions = type.dimension().length() / 2; 872 for (int i = 0; i < dimensions; i++) { 873 name = "arrayOf_" + name; 874 } 875 return name; 876 } 877 878 /** 879 * Writes a snippet of Java code to marshal a value named "name" 880 * of type "type" to the java.io.ObjectOutput stream named 881 * "stream". 882 * 883 * Primitive types are marshalled with their corresponding methods 884 * in the java.io.DataOutput interface, and objects (including 885 * arrays) are marshalled using the writeObject method. 886 **/ 887 private static void writeMarshalArgument(IndentingWriter p, 888 String streamName, 889 Type type, String name) 890 throws IOException 891 { 892 if (type.dimension().length() > 0 || type.asClassDoc() != null) { 893 p.p(streamName + ".writeObject(" + name + ")"); 894 } else if (type.typeName().equals("boolean")) { 895 p.p(streamName + ".writeBoolean(" + name + ")"); 896 } else if (type.typeName().equals("byte")) { 897 p.p(streamName + ".writeByte(" + name + ")"); 898 } else if (type.typeName().equals("char")) { 899 p.p(streamName + ".writeChar(" + name + ")"); 900 } else if (type.typeName().equals("short")) { 901 p.p(streamName + ".writeShort(" + name + ")"); 902 } else if (type.typeName().equals("int")) { 903 p.p(streamName + ".writeInt(" + name + ")"); 904 } else if (type.typeName().equals("long")) { 905 p.p(streamName + ".writeLong(" + name + ")"); 906 } else if (type.typeName().equals("float")) { 907 p.p(streamName + ".writeFloat(" + name + ")"); 908 } else if (type.typeName().equals("double")) { 909 p.p(streamName + ".writeDouble(" + name + ")"); 910 } else { 911 throw new AssertionError(type); 912 } 913 } 914 915 /** 916 * Writes Java statements to marshal a series of values in order 917 * as named in the "names" array, with types as specified in the 918 * "types" array, to the java.io.ObjectOutput stream named 919 * "stream". 920 **/ 921 private static void writeMarshalArguments(IndentingWriter p, 922 String streamName, 923 Type[] types, String[] names) 924 throws IOException 925 { 926 assert types.length == names.length; 927 928 for (int i = 0; i < types.length; i++) { 929 writeMarshalArgument(p, streamName, types[i], names[i]); 930 p.pln(";"); 931 } 932 } 933 934 /** 935 * Writes a snippet of Java code to unmarshal a value of type 936 * "type" from the java.io.ObjectInput stream named "stream" into 937 * a variable named "name" (if "name" is null, the value is 938 * unmarshalled and discarded). 939 * 940 * Primitive types are unmarshalled with their corresponding 941 * methods in the java.io.DataInput interface, and objects 942 * (including arrays) are unmarshalled using the readObject 943 * method. 944 * 945 * Returns true if code to invoke readObject was written, and 946 * false otherwise. 947 **/ 948 private static boolean writeUnmarshalArgument(IndentingWriter p, 949 String streamName, 950 Type type, String name) 951 throws IOException 952 { 953 boolean readObject = false; 954 955 if (name != null) { 956 p.p(name + " = "); 957 } 958 959 if (type.dimension().length() > 0 || type.asClassDoc() != null) { 960 p.p("(" + type.toString() + ") " + streamName + ".readObject()"); 961 readObject = true; 962 } else if (type.typeName().equals("boolean")) { 963 p.p(streamName + ".readBoolean()"); 964 } else if (type.typeName().equals("byte")) { 965 p.p(streamName + ".readByte()"); 966 } else if (type.typeName().equals("char")) { 967 p.p(streamName + ".readChar()"); 968 } else if (type.typeName().equals("short")) { 969 p.p(streamName + ".readShort()"); 970 } else if (type.typeName().equals("int")) { 971 p.p(streamName + ".readInt()"); 972 } else if (type.typeName().equals("long")) { 973 p.p(streamName + ".readLong()"); 974 } else if (type.typeName().equals("float")) { 975 p.p(streamName + ".readFloat()"); 976 } else if (type.typeName().equals("double")) { 977 p.p(streamName + ".readDouble()"); 978 } else { 979 throw new AssertionError(type); 980 } 981 982 return readObject; 983 } 984 985 /** 986 * Writes Java statements to unmarshal a series of values in order 987 * of types as in the "types" array from the java.io.ObjectInput 988 * stream named "stream" into variables as named in "names" (for 989 * any element of "names" that is null, the corresponding value is 990 * unmarshalled and discarded). 991 **/ 992 private static boolean writeUnmarshalArguments(IndentingWriter p, 993 String streamName, 994 Type[] types, 995 String[] names) 996 throws IOException 997 { 998 assert types.length == names.length; 999 1000 boolean readObject = false; 1001 for (int i = 0; i < types.length; i++) { 1002 if (writeUnmarshalArgument(p, streamName, types[i], names[i])) { 1003 readObject = true; 1004 } 1005 p.pln(";"); 1006 } 1007 return readObject; 1008 } 1009 1010 /** 1011 * Returns a snippet of Java code to wrap a value named "name" of 1012 * type "type" into an object as appropriate for use by the Java 1013 * Reflection API. 1014 * 1015 * For primitive types, an appropriate wrapper class is 1016 * instantiated with the primitive value. For object types 1017 * (including arrays), no wrapping is necessary, so the value is 1018 * named directly. 1019 **/ 1020 private static String wrapArgumentCode(Type type, String name) { 1021 if (type.dimension().length() > 0 || type.asClassDoc() != null) { 1022 return name; 1023 } else if (type.typeName().equals("boolean")) { 1024 return ("(" + name + 1025 " ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE)"); 1026 } else if (type.typeName().equals("byte")) { 1027 return "new java.lang.Byte(" + name + ")"; 1028 } else if (type.typeName().equals("char")) { 1029 return "new java.lang.Character(" + name + ")"; 1030 } else if (type.typeName().equals("short")) { 1031 return "new java.lang.Short(" + name + ")"; 1032 } else if (type.typeName().equals("int")) { 1033 return "new java.lang.Integer(" + name + ")"; 1034 } else if (type.typeName().equals("long")) { 1035 return "new java.lang.Long(" + name + ")"; 1036 } else if (type.typeName().equals("float")) { 1037 return "new java.lang.Float(" + name + ")"; 1038 } else if (type.typeName().equals("double")) { 1039 return "new java.lang.Double(" + name + ")"; 1040 } else { 1041 throw new AssertionError(type); 1042 } 1043 } 1044 1045 /** 1046 * Returns a snippet of Java code to unwrap a value named "name" 1047 * into a value of type "type", as appropriate for the Java 1048 * Reflection API. 1049 * 1050 * For primitive types, the value is assumed to be of the 1051 * corresponding wrapper class, and a method is called on the 1052 * wrapper to retrieve the primitive value. For object types 1053 * (include arrays), no unwrapping is necessary; the value is 1054 * simply cast to the expected real object type. 1055 **/ 1056 private static String unwrapArgumentCode(Type type, String name) { 1057 if (type.dimension().length() > 0 || type.asClassDoc() != null) { 1058 return "((" + type.toString() + ") " + name + ")"; 1059 } else if (type.typeName().equals("boolean")) { 1060 return "((java.lang.Boolean) " + name + ").booleanValue()"; 1061 } else if (type.typeName().equals("byte")) { 1062 return "((java.lang.Byte) " + name + ").byteValue()"; 1063 } else if (type.typeName().equals("char")) { 1064 return "((java.lang.Character) " + name + ").charValue()"; 1065 } else if (type.typeName().equals("short")) { 1066 return "((java.lang.Short) " + name + ").shortValue()"; 1067 } else if (type.typeName().equals("int")) { 1068 return "((java.lang.Integer) " + name + ").intValue()"; 1069 } else if (type.typeName().equals("long")) { 1070 return "((java.lang.Long) " + name + ").longValue()"; 1071 } else if (type.typeName().equals("float")) { 1072 return "((java.lang.Float) " + name + ").floatValue()"; 1073 } else if (type.typeName().equals("double")) { 1074 return "((java.lang.Double) " + name + ").doubleValue()"; 1075 } else { 1076 throw new AssertionError(type); 1077 } 1078 } 1079 }