1 /*
   2  * Copyright (c) 1997, 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;
  27 
  28 import java.util.Vector;
  29 import java.util.Hashtable;
  30 import java.util.Enumeration;
  31 import java.io.IOException;
  32 import java.io.ByteArrayOutputStream;
  33 import java.io.DataOutputStream;
  34 import java.security.MessageDigest;
  35 import java.security.DigestOutputStream;
  36 import java.security.NoSuchAlgorithmException;
  37 import sun.tools.java.Type;
  38 import sun.tools.java.ClassDefinition;
  39 import sun.tools.java.ClassDeclaration;
  40 import sun.tools.java.MemberDefinition;
  41 import sun.tools.java.Identifier;
  42 import sun.tools.java.ClassNotFound;
  43 
  44 /**
  45  * A RemoteClass object encapsulates RMI-specific information about
  46  * a remote implementation class, i.e. a class that implements
  47  * one or more remote interfaces.
  48  *
  49  * WARNING: The contents of this source file are not part of any
  50  * supported API.  Code that depends on them does so at its own risk:
  51  * they are subject to change or removal without notice.
  52  *
  53  * @author      Peter Jones
  54  */
  55 public class RemoteClass implements sun.rmi.rmic.RMIConstants {
  56 
  57     /**
  58      * Create a RemoteClass object representing the remote meta-information
  59      * of the given class.
  60      *
  61      * Returns true if successful.  If the class is not a properly formed
  62      * remote implementation class or if some other error occurs, the
  63      * return value will be null, and errors will have been reported to
  64      * the supplied BatchEnvironment.
  65      */
  66     public static RemoteClass forClass(BatchEnvironment env,
  67                                        ClassDefinition implClassDef)
  68     {
  69         RemoteClass rc = new RemoteClass(env, implClassDef);
  70         if (rc.initialize()) {
  71             return rc;
  72         } else {
  73             return null;
  74         }
  75     }
  76 
  77     /**
  78      * Return the ClassDefinition for this class.
  79      */
  80     public ClassDefinition getClassDefinition() {
  81         return implClassDef;
  82     }
  83 
  84     /**
  85      * Return the name of the class represented by this object.
  86      */
  87     public Identifier getName() {
  88         return implClassDef.getName();
  89     }
  90 
  91     /**
  92      * Return an array of ClassDefinitions representing all of the remote
  93      * interfaces implemented by this class.
  94      *
  95      * A remote interface is any interface that extends Remote,
  96      * directly or indirectly.  The remote interfaces of a class
  97      * are the interfaces directly listed in either the class's
  98      * "implements" clause, or the "implements" clause of any
  99      * of its superclasses, that are remote interfaces.
 100      *
 101      * The order of the array returned is arbitrary, and some elements
 102      * may be superfluous (i.e., superinterfaces of other interfaces
 103      * in the array).
 104      */
 105     public ClassDefinition[] getRemoteInterfaces() {
 106         return remoteInterfaces.clone();
 107     }
 108 
 109     /**
 110      * Return an array of RemoteClass.Method objects representing all of
 111      * the remote methods implemented by this class, i.e. all of the
 112      * methods in the class's remote interfaces.
 113      *
 114      * The methods in the array are ordered according to the comparison
 115      * of the strings consisting of their method name followed by their
 116      * type signature, so each method's index in the array corresponds
 117      * to its "operation number" in the JDK 1.1 version of the
 118      * stub/skeleton protocol.
 119      */
 120     public Method[] getRemoteMethods() {
 121         return remoteMethods.clone();
 122     }
 123 
 124     /**
 125      * Return the "interface hash" used to match a stub/skeleton pair for
 126      * this class in the JDK 1.1 version of the stub/skeleton protocol.
 127      */
 128     public long getInterfaceHash() {
 129         return interfaceHash;
 130     }
 131 
 132     /**
 133      * Return string representation of this object, consisting of
 134      * the string "remote class " followed by the class name.
 135      */
 136     public String toString() {
 137         return "remote class " + implClassDef.getName().toString();
 138     }
 139 
 140     /** rmic environment for this object */
 141     private BatchEnvironment env;
 142 
 143     /** the remote implementation class this object corresponds to */
 144     private ClassDefinition implClassDef;
 145 
 146     /** remote interfaces implemented by this class */
 147     private ClassDefinition[] remoteInterfaces;
 148 
 149     /** all the remote methods of this class */
 150     private Method[] remoteMethods;
 151 
 152     /** stub/skeleton "interface hash" for this class */
 153     private long interfaceHash;
 154 
 155     /** cached definition for certain classes used in this environment */
 156     private ClassDefinition defRemote;
 157     private ClassDefinition defException;
 158     private ClassDefinition defRemoteException;
 159 
 160     /**
 161      * Create a RemoteClass instance for the given class.  The resulting
 162      * object is not yet initialized.
 163      */
 164     private RemoteClass(BatchEnvironment env, ClassDefinition implClassDef) {
 165         this.env = env;
 166         this.implClassDef = implClassDef;
 167     }
 168 
 169     /**
 170      * Validate that the remote implementation class is properly formed
 171      * and fill in the data structures required by the public interface.
 172      */
 173     private boolean initialize() {
 174         /*
 175          * Verify that the "impl" is really a class, not an interface.
 176          */
 177         if (implClassDef.isInterface()) {
 178             env.error(0, "rmic.cant.make.stubs.for.interface",
 179                       implClassDef.getName());
 180             return false;
 181         }
 182 
 183         /*
 184          * Initialize cached definitions for the Remote interface and
 185          * the RemoteException class.
 186          */
 187         try {
 188             defRemote =
 189                 env.getClassDeclaration(idRemote).getClassDefinition(env);
 190             defException =
 191                 env.getClassDeclaration(idJavaLangException).
 192                 getClassDefinition(env);
 193             defRemoteException =
 194                 env.getClassDeclaration(idRemoteException).
 195                 getClassDefinition(env);
 196         } catch (ClassNotFound e) {
 197             env.error(0, "rmic.class.not.found", e.name);
 198             return false;
 199         }
 200 
 201         /*
 202          * Here we find all of the remote interfaces of our remote
 203          * implementation class.  For each class up the superclass
 204          * chain, add each directly-implemented interface that
 205          * somehow extends Remote to a list.
 206          */
 207         Vector<ClassDefinition> remotesImplemented = // list of remote interfaces found
 208             new Vector<ClassDefinition>();
 209         for (ClassDefinition classDef = implClassDef;
 210              classDef != null;)
 211             {
 212                 try {
 213                     ClassDeclaration[] interfaces = classDef.getInterfaces();
 214                     for (int i = 0; i < interfaces.length; i++) {
 215                         ClassDefinition interfaceDef =
 216                             interfaces[i].getClassDefinition(env);
 217                         /*
 218                          * Add interface to the list if it extends Remote and
 219                          * it is not already there.
 220                          */
 221                         if (!remotesImplemented.contains(interfaceDef) &&
 222                             defRemote.implementedBy(env, interfaces[i]))
 223                             {
 224                                 remotesImplemented.addElement(interfaceDef);
 225                                 /***** <DEBUG> */
 226                                 if (env.verbose()) {
 227                                     System.out.println("[found remote interface: " +
 228                                                        interfaceDef.getName() + "]");
 229                                     /***** </DEBUG> */
 230                                 }
 231                             }
 232                     }
 233 
 234                     /*
 235                      * Verify that the candidate remote implementation class
 236                      * implements at least one remote interface directly.
 237                      */
 238                     if (classDef == implClassDef && remotesImplemented.isEmpty()) {
 239                         if (defRemote.implementedBy(env,
 240                                                     implClassDef.getClassDeclaration()))
 241                             {
 242                                 /*
 243                                  * This error message is used if the class does
 244                                  * implement a remote interface through one of
 245                                  * its superclasses, but not directly.
 246                                  */
 247                                 env.error(0, "rmic.must.implement.remote.directly",
 248                                           implClassDef.getName());
 249                             } else {
 250                                 /*
 251                                  * This error message is used if the class never
 252                                  * implements a remote interface.
 253                                  */
 254                                 env.error(0, "rmic.must.implement.remote",
 255                                           implClassDef.getName());
 256                             }
 257                         return false;
 258                     }
 259 
 260                     /*
 261                      * Get definition for next superclass.
 262                      */
 263                     classDef = (classDef.getSuperClass() != null ?
 264                                 classDef.getSuperClass().getClassDefinition(env) :
 265                                 null);
 266 
 267                 } catch (ClassNotFound e) {
 268                     env.error(0, "class.not.found", e.name, classDef.getName());
 269                     return false;
 270                 }
 271             }
 272 
 273         /*
 274          * The "remotesImplemented" vector now contains all of the remote
 275          * interfaces directly implemented by the remote class or by any
 276          * of its superclasses.
 277          *
 278          * At this point, we could optimize the list by removing superfluous
 279          * entries, i.e. any interfaces that are implemented by some other
 280          * interface in the list anyway.
 281          *
 282          * This should be correct; would it be worthwhile?
 283          *
 284          *      for (int i = 0; i < remotesImplemented.size();) {
 285          *          ClassDefinition interfaceDef =
 286          *              (ClassDefinition) remotesImplemented.elementAt(i);
 287          *          boolean isOtherwiseImplemented = false;
 288          *          for (int j = 0; j < remotesImplemented.size; j++) {
 289          *              if (j != i &&
 290          *                  interfaceDef.implementedBy(env, (ClassDefinition)
 291          *                  remotesImplemented.elementAt(j).
 292          *                      getClassDeclaration()))
 293          *              {
 294          *                  isOtherwiseImplemented = true;
 295          *                  break;
 296          *              }
 297          *          }
 298          *          if (isOtherwiseImplemented) {
 299          *              remotesImplemented.removeElementAt(i);
 300          *          } else {
 301          *              ++i;
 302          *          }
 303          *      }
 304          */
 305 
 306         /*
 307          * Now we collect the methods from all of the remote interfaces
 308          * into a hashtable.
 309          */
 310         Hashtable<String, Method> methods = new Hashtable<String, Method>();
 311         boolean errors = false;
 312         for (Enumeration<ClassDefinition> enumeration
 313                  = remotesImplemented.elements();
 314              enumeration.hasMoreElements();)
 315             {
 316                 ClassDefinition interfaceDef = enumeration.nextElement();
 317                 if (!collectRemoteMethods(interfaceDef, methods))
 318                     errors = true;
 319             }
 320         if (errors)
 321             return false;
 322 
 323         /*
 324          * Convert vector of remote interfaces to an array
 325          * (order is not important for this array).
 326          */
 327         remoteInterfaces = new ClassDefinition[remotesImplemented.size()];
 328         remotesImplemented.copyInto(remoteInterfaces);
 329 
 330         /*
 331          * Sort table of remote methods into an array.  The elements are
 332          * sorted in ascending order of the string of the method's name
 333          * and type signature, so that each elements index is equal to
 334          * its operation number of the JDK 1.1 version of the stub/skeleton
 335          * protocol.
 336          */
 337         String[] orderedKeys = new String[methods.size()];
 338         int count = 0;
 339         for (Enumeration<Method> enumeration = methods.elements();
 340              enumeration.hasMoreElements();)
 341             {
 342                 Method m = enumeration.nextElement();
 343                 String key = m.getNameAndDescriptor();
 344                 int i;
 345                 for (i = count; i > 0; --i) {
 346                     if (key.compareTo(orderedKeys[i - 1]) >= 0) {
 347                         break;
 348                     }
 349                     orderedKeys[i] = orderedKeys[i - 1];
 350                 }
 351                 orderedKeys[i] = key;
 352                 ++count;
 353             }
 354         remoteMethods = new Method[methods.size()];
 355         for (int i = 0; i < remoteMethods.length; i++) {
 356             remoteMethods[i] = methods.get(orderedKeys[i]);
 357             /***** <DEBUG> */
 358             if (env.verbose()) {
 359                 System.out.print("[found remote method <" + i + ">: " +
 360                                  remoteMethods[i].getOperationString());
 361                 ClassDeclaration[] exceptions =
 362                     remoteMethods[i].getExceptions();
 363                 if (exceptions.length > 0)
 364                     System.out.print(" throws ");
 365                 for (int j = 0; j < exceptions.length; j++) {
 366                     if (j > 0)
 367                         System.out.print(", ");
 368                     System.out.print(exceptions[j].getName());
 369                 }
 370                 System.out.println("]");
 371             }
 372             /***** </DEBUG> */
 373         }
 374 
 375         /**
 376          * Finally, pre-compute the interface hash to be used by
 377          * stubs/skeletons for this remote class.
 378          */
 379         interfaceHash = computeInterfaceHash();
 380 
 381         return true;
 382     }
 383 
 384     /**
 385      * Collect and validate all methods from given interface and all of
 386      * its superinterfaces as remote methods.  Remote methods are added
 387      * to the supplied hashtable.  Returns true if successful,
 388      * or false if an error occurred.
 389      */
 390     private boolean collectRemoteMethods(ClassDefinition interfaceDef,
 391                                          Hashtable<String, Method> table)
 392     {
 393         if (!interfaceDef.isInterface()) {
 394             throw new Error(
 395                             "expected interface, not class: " + interfaceDef.getName());
 396         }
 397 
 398         /*
 399          * rmic used to enforce that a remote interface could not extend
 400          * a non-remote interface, i.e. an interface that did not itself
 401          * extend from Remote.  The current version of rmic does not have
 402          * this restriction, so the following code is now commented out.
 403          *
 404          * Verify that this interface extends Remote, since all interfaces
 405          * extended by a remote interface must implement Remote.
 406          *
 407          *      try {
 408          *          if (!defRemote.implementedBy(env,
 409          *              interfaceDef.getClassDeclaration()))
 410          *          {
 411          *              env.error(0, "rmic.can.mix.remote.nonremote",
 412          *                  interfaceDef.getName());
 413          *              return false;
 414          *          }
 415          *      } catch (ClassNotFound e) {
 416          *          env.error(0, "class.not.found", e.name,
 417          *              interfaceDef.getName());
 418          *          return false;
 419          *      }
 420          */
 421 
 422         boolean errors = false;
 423 
 424         /*
 425          * Search interface's members for methods.
 426          */
 427     nextMember:
 428         for (MemberDefinition member = interfaceDef.getFirstMember();
 429              member != null;
 430              member = member.getNextMember())
 431             {
 432                 if (member.isMethod() &&
 433                     !member.isConstructor() && !member.isInitializer())
 434                     {
 435                         /*
 436                          * Verify that each method throws RemoteException.
 437                          */
 438                         ClassDeclaration[] exceptions = member.getExceptions(env);
 439                         boolean hasRemoteException = false;
 440                         for (int i = 0; i < exceptions.length; i++) {
 441                             /*
 442                              * rmic used to enforce that a remote method had to
 443                              * explicitly list RemoteException in its "throws"
 444                              * clause; i.e., just throwing Exception was not
 445                              * acceptable.  The current version of rmic does not
 446                              * have this restriction, so the following code is
 447                              * now commented out.  Instead, the method is
 448                              * considered valid if RemoteException is a subclass
 449                              * of any of the methods declared exceptions.
 450                              *
 451                              *  if (exceptions[i].getName().equals(
 452                              *      idRemoteException))
 453                              *  {
 454                              *      hasRemoteException = true;
 455                              *      break;
 456                              *  }
 457                              */
 458                             try {
 459                                 if (defRemoteException.subClassOf(
 460                                                                   env, exceptions[i]))
 461                                     {
 462                                         hasRemoteException = true;
 463                                         break;
 464                                     }
 465                             } catch (ClassNotFound e) {
 466                                 env.error(0, "class.not.found", e.name,
 467                                           interfaceDef.getName());
 468                                 continue nextMember;
 469                             }
 470                         }
 471                         /*
 472                          * If this method did not throw RemoteException as required,
 473                          * generate the error but continue, so that multiple such
 474                          * errors can be reported.
 475                          */
 476                         if (!hasRemoteException) {
 477                             env.error(0, "rmic.must.throw.remoteexception",
 478                                       interfaceDef.getName(), member.toString());
 479                             errors = true;
 480                             continue nextMember;
 481                         }
 482 
 483                         /*
 484                          * Verify that the implementation of this method throws only
 485                          * java.lang.Exception or its subclasses (fix bugid 4092486).
 486                          * JRMP does not support remote methods throwing
 487                          * java.lang.Throwable or other subclasses.
 488                          */
 489                         try {
 490                             MemberDefinition implMethod = implClassDef.findMethod(
 491                                                                                   env, member.getName(), member.getType());
 492                             if (implMethod != null) {           // should not be null
 493                                 exceptions = implMethod.getExceptions(env);
 494                                 for (int i = 0; i < exceptions.length; i++) {
 495                                     if (!defException.superClassOf(
 496                                                                    env, exceptions[i]))
 497                                         {
 498                                             env.error(0, "rmic.must.only.throw.exception",
 499                                                       implMethod.toString(),
 500                                                       exceptions[i].getName());
 501                                             errors = true;
 502                                             continue nextMember;
 503                                         }
 504                                 }
 505                             }
 506                         } catch (ClassNotFound e) {
 507                             env.error(0, "class.not.found", e.name,
 508                                       implClassDef.getName());
 509                             continue nextMember;
 510                         }
 511 
 512                         /*
 513                          * Create RemoteClass.Method object to represent this method
 514                          * found in a remote interface.
 515                          */
 516                         Method newMethod = new Method(member);
 517                         /*
 518                          * Store remote method's representation in the table of
 519                          * remote methods found, keyed by its name and parameter
 520                          * signature.
 521                          *
 522                          * If the table already contains an entry with the same
 523                          * method name and parameter signature, then we must
 524                          * replace the old entry with a Method object that
 525                          * represents a legal combination of the old and the new
 526                          * methods; specifically, the combined method must have
 527                          * a throws list that contains (only) all of the checked
 528                          * exceptions that can be thrown by both the old or
 529                          * the new method (see bugid 4070653).
 530                          */
 531                         String key = newMethod.getNameAndDescriptor();
 532                         Method oldMethod = table.get(key);
 533                         if (oldMethod != null) {
 534                             newMethod = newMethod.mergeWith(oldMethod);
 535                             if (newMethod == null) {
 536                                 errors = true;
 537                                 continue nextMember;
 538                             }
 539                         }
 540                         table.put(key, newMethod);
 541                     }
 542             }
 543 
 544         /*
 545          * Recursively collect methods for all superinterfaces.
 546          */
 547         try {
 548             ClassDeclaration[] superDefs = interfaceDef.getInterfaces();
 549             for (int i = 0; i < superDefs.length; i++) {
 550                 ClassDefinition superDef =
 551                     superDefs[i].getClassDefinition(env);
 552                 if (!collectRemoteMethods(superDef, table))
 553                     errors = true;
 554             }
 555         } catch (ClassNotFound e) {
 556             env.error(0, "class.not.found", e.name, interfaceDef.getName());
 557             return false;
 558         }
 559 
 560         return !errors;
 561     }
 562 
 563     /**
 564      * Compute the "interface hash" of the stub/skeleton pair for this
 565      * remote implementation class.  This is the 64-bit value used to
 566      * enforce compatibility between a stub and a skeleton using the
 567      * JDK 1.1 version of the stub/skeleton protocol.
 568      *
 569      * It is calculated using the first 64 bits of a SHA digest.  The
 570      * digest is from a stream consisting of the following data:
 571      *     (int) stub version number, always 1
 572      *     for each remote method, in order of operation number:
 573      *         (UTF) method name
 574      *         (UTF) method type signature
 575      *         for each declared exception, in alphabetical name order:
 576      *             (UTF) name of exception class
 577      *
 578      */
 579     private long computeInterfaceHash() {
 580         long hash = 0;
 581         ByteArrayOutputStream sink = new ByteArrayOutputStream(512);
 582         try {
 583             MessageDigest md = MessageDigest.getInstance("SHA");
 584             DataOutputStream out = new DataOutputStream(
 585                                                         new DigestOutputStream(sink, md));
 586 
 587             out.writeInt(INTERFACE_HASH_STUB_VERSION);
 588             for (int i = 0; i < remoteMethods.length; i++) {
 589                 MemberDefinition m = remoteMethods[i].getMemberDefinition();
 590                 Identifier name = m.getName();
 591                 Type type = m.getType();
 592 
 593                 out.writeUTF(name.toString());
 594                 // type signatures already use mangled class names
 595                 out.writeUTF(type.getTypeSignature());
 596 
 597                 ClassDeclaration exceptions[] = m.getExceptions(env);
 598                 sortClassDeclarations(exceptions);
 599                 for (int j = 0; j < exceptions.length; j++) {
 600                     out.writeUTF(Names.mangleClass(
 601                                                    exceptions[j].getName()).toString());
 602                 }
 603             }
 604             out.flush();
 605 
 606             // use only the first 64 bits of the digest for the hash
 607             byte hashArray[] = md.digest();
 608             for (int i = 0; i < Math.min(8, hashArray.length); i++) {
 609                 hash += ((long) (hashArray[i] & 0xFF)) << (i * 8);
 610             }
 611         } catch (IOException e) {
 612             throw new Error(
 613                             "unexpected exception computing intetrface hash: " + e);
 614         } catch (NoSuchAlgorithmException e) {
 615             throw new Error(
 616                             "unexpected exception computing intetrface hash: " + e);
 617         }
 618 
 619         return hash;
 620     }
 621 
 622     /**
 623      * Sort array of class declarations alphabetically by their mangled
 624      * fully-qualified class name.  This is used to feed a method's exceptions
 625      * in a canonical order into the digest stream for the interface hash
 626      * computation.
 627      */
 628     private void sortClassDeclarations(ClassDeclaration[] decl) {
 629         for (int i = 1; i < decl.length; i++) {
 630             ClassDeclaration curr = decl[i];
 631             String name = Names.mangleClass(curr.getName()).toString();
 632             int j;
 633             for (j = i; j > 0; j--) {
 634                 if (name.compareTo(
 635                                    Names.mangleClass(decl[j - 1].getName()).toString()) >= 0)
 636                     {
 637                         break;
 638                     }
 639                 decl[j] = decl[j - 1];
 640             }
 641             decl[j] = curr;
 642         }
 643     }
 644 
 645 
 646     /**
 647      * A RemoteClass.Method object encapsulates RMI-specific information
 648      * about a particular remote method in the remote implementation class
 649      * represented by the outer instance.
 650      */
 651     public class Method implements Cloneable {
 652 
 653         /**
 654          * Return the definition of the actual class member corresponing
 655          * to this method of a remote interface.
 656          *
 657          * REMIND: Can this method be removed?
 658          */
 659         public MemberDefinition getMemberDefinition() {
 660             return memberDef;
 661         }
 662 
 663         /**
 664          * Return the name of this method.
 665          */
 666         public Identifier getName() {
 667             return memberDef.getName();
 668         }
 669 
 670         /**
 671          * Return the type of this method.
 672          */
 673         public Type getType() {
 674             return memberDef.getType();
 675         }
 676 
 677         /**
 678          * Return an array of the exception classes declared to be
 679          * thrown by this remote method.
 680          *
 681          * For methods with the same name and type signature inherited
 682          * from multiple remote interfaces, the array will contain
 683          * the set of exceptions declared in all of the interfaces'
 684          * methods that can be legally thrown in each of them.
 685          */
 686         public ClassDeclaration[] getExceptions() {
 687             return exceptions.clone();
 688         }
 689 
 690         /**
 691          * Return the "method hash" used to identify this remote method
 692          * in the JDK 1.2 version of the stub protocol.
 693          */
 694         public long getMethodHash() {
 695             return methodHash;
 696         }
 697 
 698         /**
 699          * Return the string representation of this method.
 700          */
 701         public String toString() {
 702             return memberDef.toString();
 703         }
 704 
 705         /**
 706          * Return the string representation of this method appropriate
 707          * for the construction of a java.rmi.server.Operation object.
 708          */
 709         public String getOperationString() {
 710             return memberDef.toString();
 711         }
 712 
 713         /**
 714          * Return a string consisting of this method's name followed by
 715          * its method descriptor, using the Java VM's notation for
 716          * method descriptors (see section 4.3.3 of The Java Virtual
 717          * Machine Specification).
 718          */
 719         public String getNameAndDescriptor() {
 720             return memberDef.getName().toString() +
 721                 memberDef.getType().getTypeSignature();
 722         }
 723 
 724         /**
 725          * Member definition for this method, from one of the remote
 726          * interfaces that this method was found in.
 727          *
 728          * Note that this member definition may be only one of several
 729          * member defintions that correspond to this remote method object,
 730          * if several of this class's remote interfaces contain methods
 731          * with the same name and type signature.  Therefore, this member
 732          * definition may declare more exceptions thrown that this remote
 733          * method does.
 734          */
 735         private MemberDefinition memberDef;
 736 
 737         /** stub "method hash" to identify this method */
 738         private long methodHash;
 739 
 740         /**
 741          * Exceptions declared to be thrown by this remote method.
 742          *
 743          * This list can include superfluous entries, such as
 744          * unchecked exceptions and subclasses of other entries.
 745          */
 746         private ClassDeclaration[] exceptions;
 747 
 748         /**
 749          * Create a new Method object corresponding to the given
 750          * method definition.
 751          */
 752         /*
 753          * Temporarily comment out the private modifier until
 754          * the VM allows outer class to access inner class's
 755          * private constructor
 756          */
 757         /* private */ Method(MemberDefinition memberDef) {
 758             this.memberDef = memberDef;
 759             exceptions = memberDef.getExceptions(env);
 760             methodHash = computeMethodHash();
 761         }
 762 
 763         /**
 764          * Cloning is supported by returning a shallow copy of this object.
 765          */
 766         protected Object clone() {
 767             try {
 768                 return super.clone();
 769             } catch (CloneNotSupportedException e) {
 770                 throw new Error("clone failed");
 771             }
 772         }
 773 
 774         /**
 775          * Return a new Method object that is a legal combination of
 776          * this method object and another one.
 777          *
 778          * This requires determining the exceptions declared by the
 779          * combined method, which must be (only) all of the exceptions
 780          * declared in both old Methods that may thrown in either of
 781          * them.
 782          */
 783         private Method mergeWith(Method other) {
 784             if (!getName().equals(other.getName()) ||
 785                 !getType().equals(other.getType()))
 786                 {
 787                     throw new Error("attempt to merge method \"" +
 788                                     other.getNameAndDescriptor() + "\" with \"" +
 789                                     getNameAndDescriptor());
 790                 }
 791 
 792             Vector<ClassDeclaration> legalExceptions
 793                 = new Vector<ClassDeclaration>();
 794             try {
 795                 collectCompatibleExceptions(
 796                                             other.exceptions, exceptions, legalExceptions);
 797                 collectCompatibleExceptions(
 798                                             exceptions, other.exceptions, legalExceptions);
 799             } catch (ClassNotFound e) {
 800                 env.error(0, "class.not.found", e.name,
 801                           getClassDefinition().getName());
 802                 return null;
 803             }
 804 
 805             Method merged = (Method) clone();
 806             merged.exceptions = new ClassDeclaration[legalExceptions.size()];
 807             legalExceptions.copyInto(merged.exceptions);
 808 
 809             return merged;
 810         }
 811 
 812         /**
 813          * Add to the supplied list all exceptions in the "from" array
 814          * that are subclasses of an exception in the "with" array.
 815          */
 816         private void collectCompatibleExceptions(ClassDeclaration[] from,
 817                                                  ClassDeclaration[] with,
 818                                                  Vector<ClassDeclaration> list)
 819             throws ClassNotFound
 820         {
 821             for (int i = 0; i < from.length; i++) {
 822                 ClassDefinition exceptionDef = from[i].getClassDefinition(env);
 823                 if (!list.contains(from[i])) {
 824                     for (int j = 0; j < with.length; j++) {
 825                         if (exceptionDef.subClassOf(env, with[j])) {
 826                             list.addElement(from[i]);
 827                             break;
 828                         }
 829                     }
 830                 }
 831             }
 832         }
 833 
 834         /**
 835          * Compute the "method hash" of this remote method.  The method
 836          * hash is a long containing the first 64 bits of the SHA digest
 837          * from the UTF encoded string of the method name and descriptor.
 838          *
 839          * REMIND: Should this method share implementation code with
 840          * the outer class's computeInterfaceHash() method?
 841          */
 842         private long computeMethodHash() {
 843             long hash = 0;
 844             ByteArrayOutputStream sink = new ByteArrayOutputStream(512);
 845             try {
 846                 MessageDigest md = MessageDigest.getInstance("SHA");
 847                 DataOutputStream out = new DataOutputStream(
 848                                                             new DigestOutputStream(sink, md));
 849 
 850                 String methodString = getNameAndDescriptor();
 851                 /***** <DEBUG> */
 852                 if (env.verbose()) {
 853                     System.out.println("[string used for method hash: \"" +
 854                                        methodString + "\"]");
 855                 }
 856                 /***** </DEBUG> */
 857                 out.writeUTF(methodString);
 858 
 859                 // use only the first 64 bits of the digest for the hash
 860                 out.flush();
 861                 byte hashArray[] = md.digest();
 862                 for (int i = 0; i < Math.min(8, hashArray.length); i++) {
 863                     hash += ((long) (hashArray[i] & 0xFF)) << (i * 8);
 864                 }
 865             } catch (IOException e) {
 866                 throw new Error(
 867                                 "unexpected exception computing intetrface hash: " + e);
 868             } catch (NoSuchAlgorithmException e) {
 869                 throw new Error(
 870                                 "unexpected exception computing intetrface hash: " + e);
 871             }
 872 
 873             return hash;
 874         }
 875     }
 876 }