src/jdk.rmic/share/classes/sun/rmi/rmic/newrmic/jrmp/RemoteClass.java
changeset 47493 843c071258a6
parent 47492 560fab171dc7
child 47494 24e43fd1ad69
equal deleted inserted replaced
47492:560fab171dc7 47493:843c071258a6
     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 }