0N/A/*
2362N/A * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.rmi.rmic;
0N/A
0N/Aimport java.util.Vector;
0N/Aimport java.util.Hashtable;
0N/Aimport java.util.Enumeration;
0N/Aimport java.io.IOException;
0N/Aimport java.io.ByteArrayOutputStream;
0N/Aimport java.io.DataOutputStream;
0N/Aimport java.security.MessageDigest;
0N/Aimport java.security.DigestOutputStream;
0N/Aimport java.security.NoSuchAlgorithmException;
0N/Aimport sun.tools.java.Type;
0N/Aimport sun.tools.java.ClassDefinition;
0N/Aimport sun.tools.java.ClassDeclaration;
0N/Aimport sun.tools.java.MemberDefinition;
0N/Aimport sun.tools.java.Identifier;
0N/Aimport sun.tools.java.ClassNotFound;
0N/A
0N/A/**
0N/A * A RemoteClass object encapsulates RMI-specific information about
0N/A * a remote implementation class, i.e. a class that implements
0N/A * one or more remote interfaces.
0N/A *
0N/A * WARNING: The contents of this source file are not part of any
0N/A * supported API. Code that depends on them does so at its own risk:
0N/A * they are subject to change or removal without notice.
0N/A *
0N/A * @author Peter Jones
0N/A */
0N/Apublic class RemoteClass implements sun.rmi.rmic.RMIConstants {
0N/A
0N/A /**
0N/A * Create a RemoteClass object representing the remote meta-information
0N/A * of the given class.
0N/A *
0N/A * Returns true if successful. If the class is not a properly formed
0N/A * remote implementation class or if some other error occurs, the
0N/A * return value will be null, and errors will have been reported to
0N/A * the supplied BatchEnvironment.
0N/A */
0N/A public static RemoteClass forClass(BatchEnvironment env,
0N/A ClassDefinition implClassDef)
0N/A {
0N/A RemoteClass rc = new RemoteClass(env, implClassDef);
0N/A if (rc.initialize()) {
0N/A return rc;
0N/A } else {
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Return the ClassDefinition for this class.
0N/A */
0N/A public ClassDefinition getClassDefinition() {
0N/A return implClassDef;
0N/A }
0N/A
0N/A /**
0N/A * Return the name of the class represented by this object.
0N/A */
0N/A public Identifier getName() {
0N/A return implClassDef.getName();
0N/A }
0N/A
0N/A /**
0N/A * Return an array of ClassDefinitions representing all of the remote
0N/A * interfaces implemented by this class.
0N/A *
0N/A * A remote interface is any interface that extends Remote,
0N/A * directly or indirectly. The remote interfaces of a class
0N/A * are the interfaces directly listed in either the class's
0N/A * "implements" clause, or the "implements" clause of any
0N/A * of its superclasses, that are remote interfaces.
0N/A *
0N/A * The order of the array returned is arbitrary, and some elements
0N/A * may be superfluous (i.e., superinterfaces of other interfaces
0N/A * in the array).
0N/A */
0N/A public ClassDefinition[] getRemoteInterfaces() {
28N/A return remoteInterfaces.clone();
0N/A }
0N/A
0N/A /**
0N/A * Return an array of RemoteClass.Method objects representing all of
0N/A * the remote methods implemented by this class, i.e. all of the
0N/A * methods in the class's remote interfaces.
0N/A *
0N/A * The methods in the array are ordered according to the comparision
0N/A * of the strings consisting of their method name followed by their
0N/A * type signature, so each method's index in the array corresponds
0N/A * to its "operation number" in the JDK 1.1 version of the
0N/A * stub/skeleton protocol.
0N/A */
0N/A public Method[] getRemoteMethods() {
28N/A return remoteMethods.clone();
0N/A }
0N/A
0N/A /**
0N/A * Return the "interface hash" used to match a stub/skeleton pair for
0N/A * this class in the JDK 1.1 version of the stub/skeleton protocol.
0N/A */
0N/A public long getInterfaceHash() {
0N/A return interfaceHash;
0N/A }
0N/A
0N/A /**
0N/A * Return string representation of this object, consisting of
0N/A * the string "remote class " followed by the class name.
0N/A */
0N/A public String toString() {
0N/A return "remote class " + implClassDef.getName().toString();
0N/A }
0N/A
0N/A /** rmic environment for this object */
0N/A private BatchEnvironment env;
0N/A
0N/A /** the remote implementation class this object corresponds to */
0N/A private ClassDefinition implClassDef;
0N/A
0N/A /** remote interfaces implemented by this class */
0N/A private ClassDefinition[] remoteInterfaces;
0N/A
0N/A /** all the remote methods of this class */
0N/A private Method[] remoteMethods;
0N/A
0N/A /** stub/skeleton "interface hash" for this class */
0N/A private long interfaceHash;
0N/A
0N/A /** cached definition for certain classes used in this environment */
0N/A private ClassDefinition defRemote;
0N/A private ClassDefinition defException;
0N/A private ClassDefinition defRemoteException;
0N/A
0N/A /**
0N/A * Create a RemoteClass instance for the given class. The resulting
0N/A * object is not yet initialized.
0N/A */
0N/A private RemoteClass(BatchEnvironment env, ClassDefinition implClassDef) {
0N/A this.env = env;
0N/A this.implClassDef = implClassDef;
0N/A }
0N/A
0N/A /**
0N/A * Validate that the remote implementation class is properly formed
0N/A * and fill in the data structures required by the public interface.
0N/A */
0N/A private boolean initialize() {
0N/A /*
0N/A * Verify that the "impl" is really a class, not an interface.
0N/A */
0N/A if (implClassDef.isInterface()) {
0N/A env.error(0, "rmic.cant.make.stubs.for.interface",
0N/A implClassDef.getName());
0N/A return false;
0N/A }
0N/A
0N/A /*
0N/A * Initialize cached definitions for the Remote interface and
0N/A * the RemoteException class.
0N/A */
0N/A try {
0N/A defRemote =
0N/A env.getClassDeclaration(idRemote).getClassDefinition(env);
0N/A defException =
0N/A env.getClassDeclaration(idJavaLangException).
0N/A getClassDefinition(env);
0N/A defRemoteException =
0N/A env.getClassDeclaration(idRemoteException).
0N/A getClassDefinition(env);
0N/A } catch (ClassNotFound e) {
0N/A env.error(0, "rmic.class.not.found", e.name);
0N/A return false;
0N/A }
0N/A
0N/A /*
0N/A * Here we find all of the remote interfaces of our remote
0N/A * implementation class. For each class up the superclass
0N/A * chain, add each directly-implemented interface that
0N/A * somehow extends Remote to a list.
0N/A */
28N/A Vector<ClassDefinition> remotesImplemented = // list of remote interfaces found
28N/A new Vector<ClassDefinition>();
0N/A for (ClassDefinition classDef = implClassDef;
0N/A classDef != null;)
0N/A {
0N/A try {
0N/A ClassDeclaration[] interfaces = classDef.getInterfaces();
0N/A for (int i = 0; i < interfaces.length; i++) {
0N/A ClassDefinition interfaceDef =
0N/A interfaces[i].getClassDefinition(env);
0N/A /*
0N/A * Add interface to the list if it extends Remote and
0N/A * it is not already there.
0N/A */
0N/A if (!remotesImplemented.contains(interfaceDef) &&
0N/A defRemote.implementedBy(env, interfaces[i]))
0N/A {
0N/A remotesImplemented.addElement(interfaceDef);
0N/A /***** <DEBUG> */
0N/A if (env.verbose()) {
0N/A System.out.println("[found remote interface: " +
0N/A interfaceDef.getName() + "]");
0N/A /***** </DEBUG> */
0N/A }
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Verify that the candidate remote implementation class
0N/A * implements at least one remote interface directly.
0N/A */
0N/A if (classDef == implClassDef && remotesImplemented.isEmpty()) {
0N/A if (defRemote.implementedBy(env,
0N/A implClassDef.getClassDeclaration()))
0N/A {
0N/A /*
0N/A * This error message is used if the class does
0N/A * implement a remote interface through one of
0N/A * its superclasses, but not directly.
0N/A */
0N/A env.error(0, "rmic.must.implement.remote.directly",
0N/A implClassDef.getName());
0N/A } else {
0N/A /*
0N/A * This error message is used if the class never
0N/A * implements a remote interface.
0N/A */
0N/A env.error(0, "rmic.must.implement.remote",
0N/A implClassDef.getName());
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A /*
0N/A * Get definition for next superclass.
0N/A */
0N/A classDef = (classDef.getSuperClass() != null ?
0N/A classDef.getSuperClass().getClassDefinition(env) :
0N/A null);
0N/A
0N/A } catch (ClassNotFound e) {
0N/A env.error(0, "class.not.found", e.name, classDef.getName());
0N/A return false;
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * The "remotesImplemented" vector now contains all of the remote
0N/A * interfaces directly implemented by the remote class or by any
0N/A * of its superclasses.
0N/A *
0N/A * At this point, we could optimize the list by removing superfluous
0N/A * entries, i.e. any interfaces that are implemented by some other
0N/A * interface in the list anyway.
0N/A *
0N/A * This should be correct; would it be worthwhile?
0N/A *
0N/A * for (int i = 0; i < remotesImplemented.size();) {
0N/A * ClassDefinition interfaceDef =
0N/A * (ClassDefinition) remotesImplemented.elementAt(i);
0N/A * boolean isOtherwiseImplemented = false;
0N/A * for (int j = 0; j < remotesImplemented.size; j++) {
0N/A * if (j != i &&
0N/A * interfaceDef.implementedBy(env, (ClassDefinition)
0N/A * remotesImplemented.elementAt(j).
0N/A * getClassDeclaration()))
0N/A * {
0N/A * isOtherwiseImplemented = true;
0N/A * break;
0N/A * }
0N/A * }
0N/A * if (isOtherwiseImplemented) {
0N/A * remotesImplemented.removeElementAt(i);
0N/A * } else {
0N/A * ++i;
0N/A * }
0N/A * }
0N/A */
0N/A
0N/A /*
0N/A * Now we collect the methods from all of the remote interfaces
0N/A * into a hashtable.
0N/A */
28N/A Hashtable<String, Method> methods = new Hashtable<String, Method>();
0N/A boolean errors = false;
28N/A for (Enumeration<ClassDefinition> enumeration
28N/A = remotesImplemented.elements();
0N/A enumeration.hasMoreElements();)
0N/A {
28N/A ClassDefinition interfaceDef = enumeration.nextElement();
0N/A if (!collectRemoteMethods(interfaceDef, methods))
0N/A errors = true;
0N/A }
0N/A if (errors)
0N/A return false;
0N/A
0N/A /*
0N/A * Convert vector of remote interfaces to an array
0N/A * (order is not important for this array).
0N/A */
0N/A remoteInterfaces = new ClassDefinition[remotesImplemented.size()];
0N/A remotesImplemented.copyInto(remoteInterfaces);
0N/A
0N/A /*
0N/A * Sort table of remote methods into an array. The elements are
0N/A * sorted in ascending order of the string of the method's name
0N/A * and type signature, so that each elements index is equal to
0N/A * its operation number of the JDK 1.1 version of the stub/skeleton
0N/A * protocol.
0N/A */
0N/A String[] orderedKeys = new String[methods.size()];
0N/A int count = 0;
28N/A for (Enumeration<Method> enumeration = methods.elements();
0N/A enumeration.hasMoreElements();)
0N/A {
28N/A Method m = enumeration.nextElement();
0N/A String key = m.getNameAndDescriptor();
0N/A int i;
0N/A for (i = count; i > 0; --i) {
0N/A if (key.compareTo(orderedKeys[i - 1]) >= 0) {
0N/A break;
0N/A }
0N/A orderedKeys[i] = orderedKeys[i - 1];
0N/A }
0N/A orderedKeys[i] = key;
0N/A ++count;
0N/A }
0N/A remoteMethods = new Method[methods.size()];
0N/A for (int i = 0; i < remoteMethods.length; i++) {
28N/A remoteMethods[i] = methods.get(orderedKeys[i]);
0N/A /***** <DEBUG> */
0N/A if (env.verbose()) {
0N/A System.out.print("[found remote method <" + i + ">: " +
0N/A remoteMethods[i].getOperationString());
0N/A ClassDeclaration[] exceptions =
0N/A remoteMethods[i].getExceptions();
0N/A if (exceptions.length > 0)
0N/A System.out.print(" throws ");
0N/A for (int j = 0; j < exceptions.length; j++) {
0N/A if (j > 0)
0N/A System.out.print(", ");
0N/A System.out.print(exceptions[j].getName());
0N/A }
0N/A System.out.println("]");
0N/A }
0N/A /***** </DEBUG> */
0N/A }
0N/A
0N/A /**
0N/A * Finally, pre-compute the interface hash to be used by
0N/A * stubs/skeletons for this remote class.
0N/A */
0N/A interfaceHash = computeInterfaceHash();
0N/A
0N/A return true;
0N/A }
0N/A
0N/A /**
0N/A * Collect and validate all methods from given interface and all of
0N/A * its superinterfaces as remote methods. Remote methods are added
0N/A * to the supplied hashtable. Returns true if successful,
0N/A * or false if an error occurred.
0N/A */
0N/A private boolean collectRemoteMethods(ClassDefinition interfaceDef,
28N/A Hashtable<String, Method> table)
0N/A {
0N/A if (!interfaceDef.isInterface()) {
0N/A throw new Error(
0N/A "expected interface, not class: " + interfaceDef.getName());
0N/A }
0N/A
0N/A /*
0N/A * rmic used to enforce that a remote interface could not extend
0N/A * a non-remote interface, i.e. an interface that did not itself
0N/A * extend from Remote. The current version of rmic does not have
0N/A * this restriction, so the following code is now commented out.
0N/A *
0N/A * Verify that this interface extends Remote, since all interfaces
0N/A * extended by a remote interface must implement Remote.
0N/A *
0N/A * try {
0N/A * if (!defRemote.implementedBy(env,
0N/A * interfaceDef.getClassDeclaration()))
0N/A * {
0N/A * env.error(0, "rmic.can.mix.remote.nonremote",
0N/A * interfaceDef.getName());
0N/A * return false;
0N/A * }
0N/A * } catch (ClassNotFound e) {
0N/A * env.error(0, "class.not.found", e.name,
0N/A * interfaceDef.getName());
0N/A * return false;
0N/A * }
0N/A */
0N/A
0N/A boolean errors = false;
0N/A
0N/A /*
0N/A * Search interface's members for methods.
0N/A */
0N/A nextMember:
0N/A for (MemberDefinition member = interfaceDef.getFirstMember();
0N/A member != null;
0N/A member = member.getNextMember())
0N/A {
0N/A if (member.isMethod() &&
0N/A !member.isConstructor() && !member.isInitializer())
0N/A {
0N/A /*
0N/A * Verify that each method throws RemoteException.
0N/A */
0N/A ClassDeclaration[] exceptions = member.getExceptions(env);
0N/A boolean hasRemoteException = false;
0N/A for (int i = 0; i < exceptions.length; i++) {
0N/A /*
0N/A * rmic used to enforce that a remote method had to
0N/A * explicitly list RemoteException in its "throws"
0N/A * clause; i.e., just throwing Exception was not
0N/A * acceptable. The current version of rmic does not
0N/A * have this restriction, so the following code is
0N/A * now commented out. Instead, the method is
0N/A * considered valid if RemoteException is a subclass
0N/A * of any of the methods declared exceptions.
0N/A *
0N/A * if (exceptions[i].getName().equals(
0N/A * idRemoteException))
0N/A * {
0N/A * hasRemoteException = true;
0N/A * break;
0N/A * }
0N/A */
0N/A try {
0N/A if (defRemoteException.subClassOf(
0N/A env, exceptions[i]))
0N/A {
0N/A hasRemoteException = true;
0N/A break;
0N/A }
0N/A } catch (ClassNotFound e) {
0N/A env.error(0, "class.not.found", e.name,
0N/A interfaceDef.getName());
0N/A continue nextMember;
0N/A }
0N/A }
0N/A /*
0N/A * If this method did not throw RemoteException as required,
0N/A * generate the error but continue, so that multiple such
0N/A * errors can be reported.
0N/A */
0N/A if (!hasRemoteException) {
0N/A env.error(0, "rmic.must.throw.remoteexception",
0N/A interfaceDef.getName(), member.toString());
0N/A errors = true;
0N/A continue nextMember;
0N/A }
0N/A
0N/A /*
0N/A * Verify that the implementation of this method throws only
0N/A * java.lang.Exception or its subclasses (fix bugid 4092486).
0N/A * JRMP does not support remote methods throwing
0N/A * java.lang.Throwable or other subclasses.
0N/A */
0N/A try {
0N/A MemberDefinition implMethod = implClassDef.findMethod(
0N/A env, member.getName(), member.getType());
0N/A if (implMethod != null) { // should not be null
0N/A exceptions = implMethod.getExceptions(env);
0N/A for (int i = 0; i < exceptions.length; i++) {
0N/A if (!defException.superClassOf(
0N/A env, exceptions[i]))
0N/A {
0N/A env.error(0, "rmic.must.only.throw.exception",
0N/A implMethod.toString(),
0N/A exceptions[i].getName());
0N/A errors = true;
0N/A continue nextMember;
0N/A }
0N/A }
0N/A }
0N/A } catch (ClassNotFound e) {
0N/A env.error(0, "class.not.found", e.name,
0N/A implClassDef.getName());
0N/A continue nextMember;
0N/A }
0N/A
0N/A /*
0N/A * Create RemoteClass.Method object to represent this method
0N/A * found in a remote interface.
0N/A */
0N/A Method newMethod = new Method(member);
0N/A /*
0N/A * Store remote method's representation in the table of
0N/A * remote methods found, keyed by its name and parameter
0N/A * signature.
0N/A *
0N/A * If the table already contains an entry with the same
0N/A * method name and parameter signature, then we must
0N/A * replace the old entry with a Method object that
0N/A * represents a legal combination of the old and the new
0N/A * methods; specifically, the combined method must have
0N/A * a throws list that contains (only) all of the checked
0N/A * exceptions that can be thrown by both the old or
0N/A * the new method (see bugid 4070653).
0N/A */
0N/A String key = newMethod.getNameAndDescriptor();
28N/A Method oldMethod = table.get(key);
0N/A if (oldMethod != null) {
0N/A newMethod = newMethod.mergeWith(oldMethod);
0N/A if (newMethod == null) {
0N/A errors = true;
0N/A continue nextMember;
0N/A }
0N/A }
0N/A table.put(key, newMethod);
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Recursively collect methods for all superinterfaces.
0N/A */
0N/A try {
0N/A ClassDeclaration[] superDefs = interfaceDef.getInterfaces();
0N/A for (int i = 0; i < superDefs.length; i++) {
0N/A ClassDefinition superDef =
0N/A superDefs[i].getClassDefinition(env);
0N/A if (!collectRemoteMethods(superDef, table))
0N/A errors = true;
0N/A }
0N/A } catch (ClassNotFound e) {
0N/A env.error(0, "class.not.found", e.name, interfaceDef.getName());
0N/A return false;
0N/A }
0N/A
0N/A return !errors;
0N/A }
0N/A
0N/A /**
0N/A * Compute the "interface hash" of the stub/skeleton pair for this
0N/A * remote implementation class. This is the 64-bit value used to
0N/A * enforce compatibility between a stub and a skeleton using the
0N/A * JDK 1.1 version of the stub/skeleton protocol.
0N/A *
0N/A * It is calculated using the first 64 bits of a SHA digest. The
0N/A * digest is from a stream consisting of the following data:
0N/A * (int) stub version number, always 1
0N/A * for each remote method, in order of operation number:
0N/A * (UTF) method name
0N/A * (UTF) method type signature
0N/A * for each declared exception, in alphabetical name order:
0N/A * (UTF) name of exception class
0N/A *
0N/A */
0N/A private long computeInterfaceHash() {
0N/A long hash = 0;
0N/A ByteArrayOutputStream sink = new ByteArrayOutputStream(512);
0N/A try {
0N/A MessageDigest md = MessageDigest.getInstance("SHA");
0N/A DataOutputStream out = new DataOutputStream(
0N/A new DigestOutputStream(sink, md));
0N/A
0N/A out.writeInt(INTERFACE_HASH_STUB_VERSION);
0N/A for (int i = 0; i < remoteMethods.length; i++) {
0N/A MemberDefinition m = remoteMethods[i].getMemberDefinition();
0N/A Identifier name = m.getName();
0N/A Type type = m.getType();
0N/A
0N/A out.writeUTF(name.toString());
0N/A // type signatures already use mangled class names
0N/A out.writeUTF(type.getTypeSignature());
0N/A
0N/A ClassDeclaration exceptions[] = m.getExceptions(env);
0N/A sortClassDeclarations(exceptions);
0N/A for (int j = 0; j < exceptions.length; j++) {
0N/A out.writeUTF(Names.mangleClass(
0N/A exceptions[j].getName()).toString());
0N/A }
0N/A }
0N/A out.flush();
0N/A
0N/A // use only the first 64 bits of the digest for the hash
0N/A byte hashArray[] = md.digest();
0N/A for (int i = 0; i < Math.min(8, hashArray.length); i++) {
0N/A hash += ((long) (hashArray[i] & 0xFF)) << (i * 8);
0N/A }
0N/A } catch (IOException e) {
0N/A throw new Error(
0N/A "unexpected exception computing intetrface hash: " + e);
0N/A } catch (NoSuchAlgorithmException e) {
0N/A throw new Error(
0N/A "unexpected exception computing intetrface hash: " + e);
0N/A }
0N/A
0N/A return hash;
0N/A }
0N/A
0N/A /**
0N/A * Sort array of class declarations alphabetically by their mangled
0N/A * fully-qualfied class name. This is used to feed a method's exceptions
0N/A * in a canonical order into the digest stream for the interface hash
0N/A * computation.
0N/A */
0N/A private void sortClassDeclarations(ClassDeclaration[] decl) {
0N/A for (int i = 1; i < decl.length; i++) {
0N/A ClassDeclaration curr = decl[i];
0N/A String name = Names.mangleClass(curr.getName()).toString();
0N/A int j;
0N/A for (j = i; j > 0; j--) {
0N/A if (name.compareTo(
0N/A Names.mangleClass(decl[j - 1].getName()).toString()) >= 0)
0N/A {
0N/A break;
0N/A }
0N/A decl[j] = decl[j - 1];
0N/A }
0N/A decl[j] = curr;
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * A RemoteClass.Method object encapsulates RMI-specific information
0N/A * about a particular remote method in the remote implementation class
0N/A * represented by the outer instance.
0N/A */
0N/A public class Method implements Cloneable {
0N/A
0N/A /**
0N/A * Return the definition of the actual class member corresponing
0N/A * to this method of a remote interface.
0N/A *
0N/A * REMIND: Can this method be removed?
0N/A */
0N/A public MemberDefinition getMemberDefinition() {
0N/A return memberDef;
0N/A }
0N/A
0N/A /**
0N/A * Return the name of this method.
0N/A */
0N/A public Identifier getName() {
0N/A return memberDef.getName();
0N/A }
0N/A
0N/A /**
0N/A * Return the type of this method.
0N/A */
0N/A public Type getType() {
0N/A return memberDef.getType();
0N/A }
0N/A
0N/A /**
0N/A * Return an array of the exception classes declared to be
0N/A * thrown by this remote method.
0N/A *
0N/A * For methods with the same name and type signature inherited
0N/A * from multiple remote interfaces, the array will contain
0N/A * the set of exceptions declared in all of the interfaces'
0N/A * methods that can be legally thrown in each of them.
0N/A */
0N/A public ClassDeclaration[] getExceptions() {
28N/A return exceptions.clone();
0N/A }
0N/A
0N/A /**
0N/A * Return the "method hash" used to identify this remote method
0N/A * in the JDK 1.2 version of the stub protocol.
0N/A */
0N/A public long getMethodHash() {
0N/A return methodHash;
0N/A }
0N/A
0N/A /**
0N/A * Return the string representation of this method.
0N/A */
0N/A public String toString() {
0N/A return memberDef.toString();
0N/A }
0N/A
0N/A /**
0N/A * Return the string representation of this method appropriate
0N/A * for the construction of a java.rmi.server.Operation object.
0N/A */
0N/A public String getOperationString() {
0N/A return memberDef.toString();
0N/A }
0N/A
0N/A /**
0N/A * Return a string consisting of this method's name followed by
0N/A * its method descriptor, using the Java VM's notation for
0N/A * method descriptors (see section 4.3.3 of The Java Virtual
0N/A * Machine Specification).
0N/A */
0N/A public String getNameAndDescriptor() {
0N/A return memberDef.getName().toString() +
0N/A memberDef.getType().getTypeSignature();
0N/A }
0N/A
0N/A /**
0N/A * Member definition for this method, from one of the remote
0N/A * interfaces that this method was found in.
0N/A *
0N/A * Note that this member definition may be only one of several
0N/A * member defintions that correspond to this remote method object,
0N/A * if several of this class's remote interfaces contain methods
0N/A * with the same name and type signature. Therefore, this member
0N/A * definition may declare more exceptions thrown that this remote
0N/A * method does.
0N/A */
0N/A private MemberDefinition memberDef;
0N/A
0N/A /** stub "method hash" to identify this method */
0N/A private long methodHash;
0N/A
0N/A /**
0N/A * Exceptions declared to be thrown by this remote method.
0N/A *
0N/A * This list can include superfluous entries, such as
0N/A * unchecked exceptions and subclasses of other entries.
0N/A */
0N/A private ClassDeclaration[] exceptions;
0N/A
0N/A /**
0N/A * Create a new Method object corresponding to the given
0N/A * method definition.
0N/A */
0N/A /*
0N/A * Temporarily comment out the private modifier until
0N/A * the VM allows outer class to access inner class's
0N/A * private constructor
0N/A */
0N/A /* private */ Method(MemberDefinition memberDef) {
0N/A this.memberDef = memberDef;
0N/A exceptions = memberDef.getExceptions(env);
0N/A methodHash = computeMethodHash();
0N/A }
0N/A
0N/A /**
0N/A * Cloning is supported by returning a shallow copy of this object.
0N/A */
0N/A protected Object clone() {
0N/A try {
0N/A return super.clone();
0N/A } catch (CloneNotSupportedException e) {
0N/A throw new Error("clone failed");
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Return a new Method object that is a legal combination of
0N/A * this method object and another one.
0N/A *
0N/A * This requires determining the exceptions declared by the
0N/A * combined method, which must be (only) all of the exceptions
0N/A * declared in both old Methods that may thrown in either of
0N/A * them.
0N/A */
0N/A private Method mergeWith(Method other) {
0N/A if (!getName().equals(other.getName()) ||
0N/A !getType().equals(other.getType()))
0N/A {
0N/A throw new Error("attempt to merge method \"" +
0N/A other.getNameAndDescriptor() + "\" with \"" +
0N/A getNameAndDescriptor());
0N/A }
0N/A
28N/A Vector<ClassDeclaration> legalExceptions
28N/A = new Vector<ClassDeclaration>();
0N/A try {
0N/A collectCompatibleExceptions(
0N/A other.exceptions, exceptions, legalExceptions);
0N/A collectCompatibleExceptions(
0N/A exceptions, other.exceptions, legalExceptions);
0N/A } catch (ClassNotFound e) {
0N/A env.error(0, "class.not.found", e.name,
0N/A getClassDefinition().getName());
0N/A return null;
0N/A }
0N/A
0N/A Method merged = (Method) clone();
0N/A merged.exceptions = new ClassDeclaration[legalExceptions.size()];
0N/A legalExceptions.copyInto(merged.exceptions);
0N/A
0N/A return merged;
0N/A }
0N/A
0N/A /**
0N/A * Add to the supplied list all exceptions in the "from" array
0N/A * that are subclasses of an exception in the "with" array.
0N/A */
0N/A private void collectCompatibleExceptions(ClassDeclaration[] from,
0N/A ClassDeclaration[] with,
28N/A Vector<ClassDeclaration> list)
0N/A throws ClassNotFound
0N/A {
0N/A for (int i = 0; i < from.length; i++) {
0N/A ClassDefinition exceptionDef = from[i].getClassDefinition(env);
0N/A if (!list.contains(from[i])) {
0N/A for (int j = 0; j < with.length; j++) {
0N/A if (exceptionDef.subClassOf(env, with[j])) {
0N/A list.addElement(from[i]);
0N/A break;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Compute the "method hash" of this remote method. The method
0N/A * hash is a long containing the first 64 bits of the SHA digest
0N/A * from the UTF encoded string of the method name and descriptor.
0N/A *
0N/A * REMIND: Should this method share implementation code with
0N/A * the outer class's computeInterfaceHash() method?
0N/A */
0N/A private long computeMethodHash() {
0N/A long hash = 0;
0N/A ByteArrayOutputStream sink = new ByteArrayOutputStream(512);
0N/A try {
0N/A MessageDigest md = MessageDigest.getInstance("SHA");
0N/A DataOutputStream out = new DataOutputStream(
0N/A new DigestOutputStream(sink, md));
0N/A
0N/A String methodString = getNameAndDescriptor();
0N/A /***** <DEBUG> */
0N/A if (env.verbose()) {
0N/A System.out.println("[string used for method hash: \"" +
0N/A methodString + "\"]");
0N/A }
0N/A /***** </DEBUG> */
0N/A out.writeUTF(methodString);
0N/A
0N/A // use only the first 64 bits of the digest for the hash
0N/A out.flush();
0N/A byte hashArray[] = md.digest();
0N/A for (int i = 0; i < Math.min(8, hashArray.length); i++) {
0N/A hash += ((long) (hashArray[i] & 0xFF)) << (i * 8);
0N/A }
0N/A } catch (IOException e) {
0N/A throw new Error(
0N/A "unexpected exception computing intetrface hash: " + e);
0N/A } catch (NoSuchAlgorithmException e) {
0N/A throw new Error(
0N/A "unexpected exception computing intetrface hash: " + e);
0N/A }
0N/A
0N/A return hash;
0N/A }
0N/A }
0N/A}