876 lines · 35.0 KB · Java
RawDownload
/* * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.rmi.rmic; import java.util.Vector;import java.util.Hashtable;import java.util.Enumeration;import java.io.IOException;import java.io.ByteArrayOutputStream;import java.io.DataOutputStream;import java.security.MessageDigest;import java.security.DigestOutputStream;import java.security.NoSuchAlgorithmException;import sun.tools.java.Type;import sun.tools.java.ClassDefinition;import sun.tools.java.ClassDeclaration;import sun.tools.java.MemberDefinition;import sun.tools.java.Identifier;import sun.tools.java.ClassNotFound; /** * A RemoteClass object encapsulates RMI-specific information about * a remote implementation class, i.e. a class that implements * one or more remote interfaces. * * WARNING: The contents of this source file are not part of any * supported API.  Code that depends on them does so at its own risk: * they are subject to change or removal without notice. * * @author      Peter Jones */public class RemoteClass implements sun.rmi.rmic.RMIConstants {     /**     * Create a RemoteClass object representing the remote meta-information     * of the given class.     *     * Returns true if successful.  If the class is not a properly formed     * remote implementation class or if some other error occurs, the     * return value will be null, and errors will have been reported to     * the supplied BatchEnvironment.     */    public static RemoteClass forClass(BatchEnvironment env,                                       ClassDefinition implClassDef)    {        RemoteClass rc = new RemoteClass(env, implClassDef);        if (rc.initialize()) {            return rc;        } else {            return null;        }    }     /**     * Return the ClassDefinition for this class.     */    public ClassDefinition getClassDefinition() {        return implClassDef;    }     /**     * Return the name of the class represented by this object.     */    public Identifier getName() {        return implClassDef.getName();    }     /**     * Return an array of ClassDefinitions representing all of the remote     * interfaces implemented by this class.     *     * A remote interface is any interface that extends Remote,     * directly or indirectly.  The remote interfaces of a class     * are the interfaces directly listed in either the class's     * "implements" clause, or the "implements" clause of any     * of its superclasses, that are remote interfaces.     *     * The order of the array returned is arbitrary, and some elements     * may be superfluous (i.e., superinterfaces of other interfaces     * in the array).     */    public ClassDefinition[] getRemoteInterfaces() {        return remoteInterfaces.clone();    }     /**     * Return an array of RemoteClass.Method objects representing all of     * the remote methods implemented by this class, i.e. all of the     * methods in the class's remote interfaces.     *     * The methods in the array are ordered according to the comparision     * of the strings consisting of their method name followed by their     * type signature, so each method's index in the array corresponds     * to its "operation number" in the JDK 1.1 version of the     * stub/skeleton protocol.     */    public Method[] getRemoteMethods() {        return remoteMethods.clone();    }     /**     * Return the "interface hash" used to match a stub/skeleton pair for     * this class in the JDK 1.1 version of the stub/skeleton protocol.     */    public long getInterfaceHash() {        return interfaceHash;    }     /**     * Return string representation of this object, consisting of     * the string "remote class " followed by the class name.     */    public String toString() {        return "remote class " + implClassDef.getName().toString();    }     /** rmic environment for this object */    private BatchEnvironment env;     /** the remote implementation class this object corresponds to */    private ClassDefinition implClassDef;     /** remote interfaces implemented by this class */    private ClassDefinition[] remoteInterfaces;     /** all the remote methods of this class */    private Method[] remoteMethods;     /** stub/skeleton "interface hash" for this class */    private long interfaceHash;     /** cached definition for certain classes used in this environment */    private ClassDefinition defRemote;    private ClassDefinition defException;    private ClassDefinition defRemoteException;     /**     * Create a RemoteClass instance for the given class.  The resulting     * object is not yet initialized.     */    private RemoteClass(BatchEnvironment env, ClassDefinition implClassDef) {        this.env = env;        this.implClassDef = implClassDef;    }     /**     * Validate that the remote implementation class is properly formed     * and fill in the data structures required by the public interface.     */    private boolean initialize() {        /*         * Verify that the "impl" is really a class, not an interface.         */        if (implClassDef.isInterface()) {            env.error(0, "rmic.cant.make.stubs.for.interface",                      implClassDef.getName());            return false;        }         /*         * Initialize cached definitions for the Remote interface and         * the RemoteException class.         */        try {            defRemote =                env.getClassDeclaration(idRemote).getClassDefinition(env);            defException =                env.getClassDeclaration(idJavaLangException).                getClassDefinition(env);            defRemoteException =                env.getClassDeclaration(idRemoteException).                getClassDefinition(env);        } catch (ClassNotFound e) {            env.error(0, "rmic.class.not.found", e.name);            return false;        }         /*         * Here we find all of the remote interfaces of our remote         * implementation class.  For each class up the superclass         * chain, add each directly-implemented interface that         * somehow extends Remote to a list.         */        Vector<ClassDefinition> remotesImplemented = // list of remote interfaces found            new Vector<ClassDefinition>();        for (ClassDefinition classDef = implClassDef;             classDef != null;)            {                try {                    ClassDeclaration[] interfaces = classDef.getInterfaces();                    for (int i = 0; i < interfaces.length; i++) {                        ClassDefinition interfaceDef =                            interfaces[i].getClassDefinition(env);                        /*                         * Add interface to the list if it extends Remote and                         * it is not already there.                         */                        if (!remotesImplemented.contains(interfaceDef) &&                            defRemote.implementedBy(env, interfaces[i]))                            {                                remotesImplemented.addElement(interfaceDef);                                /***** <DEBUG> */                                if (env.verbose()) {                                    System.out.println("[found remote interface: " +                                                       interfaceDef.getName() + "]");                                    /***** </DEBUG> */                                }                            }                    }                     /*                     * Verify that the candidate remote implementation class                     * implements at least one remote interface directly.                     */                    if (classDef == implClassDef && remotesImplemented.isEmpty()) {                        if (defRemote.implementedBy(env,                                                    implClassDef.getClassDeclaration()))                            {                                /*                                 * This error message is used if the class does                                 * implement a remote interface through one of                                 * its superclasses, but not directly.                                 */                                env.error(0, "rmic.must.implement.remote.directly",                                          implClassDef.getName());                            } else {                                /*                                 * This error message is used if the class never                                 * implements a remote interface.                                 */                                env.error(0, "rmic.must.implement.remote",                                          implClassDef.getName());                            }                        return false;                    }                     /*                     * Get definition for next superclass.                     */                    classDef = (classDef.getSuperClass() != null ?                                classDef.getSuperClass().getClassDefinition(env) :                                null);                 } catch (ClassNotFound e) {                    env.error(0, "class.not.found", e.name, classDef.getName());                    return false;                }            }         /*         * The "remotesImplemented" vector now contains all of the remote         * interfaces directly implemented by the remote class or by any         * of its superclasses.         *         * At this point, we could optimize the list by removing superfluous         * entries, i.e. any interfaces that are implemented by some other         * interface in the list anyway.         *         * This should be correct; would it be worthwhile?         *         *      for (int i = 0; i < remotesImplemented.size();) {         *          ClassDefinition interfaceDef =         *              (ClassDefinition) remotesImplemented.elementAt(i);         *          boolean isOtherwiseImplemented = false;         *          for (int j = 0; j < remotesImplemented.size; j++) {         *              if (j != i &&         *                  interfaceDef.implementedBy(env, (ClassDefinition)         *                  remotesImplemented.elementAt(j).         *                      getClassDeclaration()))         *              {         *                  isOtherwiseImplemented = true;         *                  break;         *              }         *          }         *          if (isOtherwiseImplemented) {         *              remotesImplemented.removeElementAt(i);         *          } else {         *              ++i;         *          }         *      }         */         /*         * Now we collect the methods from all of the remote interfaces         * into a hashtable.         */        Hashtable<String, Method> methods = new Hashtable<String, Method>();        boolean errors = false;        for (Enumeration<ClassDefinition> enumeration                 = remotesImplemented.elements();             enumeration.hasMoreElements();)            {                ClassDefinition interfaceDef = enumeration.nextElement();                if (!collectRemoteMethods(interfaceDef, methods))                    errors = true;            }        if (errors)            return false;         /*         * Convert vector of remote interfaces to an array         * (order is not important for this array).         */        remoteInterfaces = new ClassDefinition[remotesImplemented.size()];        remotesImplemented.copyInto(remoteInterfaces);         /*         * Sort table of remote methods into an array.  The elements are         * sorted in ascending order of the string of the method's name         * and type signature, so that each elements index is equal to         * its operation number of the JDK 1.1 version of the stub/skeleton         * protocol.         */        String[] orderedKeys = new String[methods.size()];        int count = 0;        for (Enumeration<Method> enumeration = methods.elements();             enumeration.hasMoreElements();)            {                Method m = enumeration.nextElement();                String key = m.getNameAndDescriptor();                int i;                for (i = count; i > 0; --i) {                    if (key.compareTo(orderedKeys[i - 1]) >= 0) {                        break;                    }                    orderedKeys[i] = orderedKeys[i - 1];                }                orderedKeys[i] = key;                ++count;            }        remoteMethods = new Method[methods.size()];        for (int i = 0; i < remoteMethods.length; i++) {            remoteMethods[i] = methods.get(orderedKeys[i]);            /***** <DEBUG> */            if (env.verbose()) {                System.out.print("[found remote method <" + i + ">: " +                                 remoteMethods[i].getOperationString());                ClassDeclaration[] exceptions =                    remoteMethods[i].getExceptions();                if (exceptions.length > 0)                    System.out.print(" throws ");                for (int j = 0; j < exceptions.length; j++) {                    if (j > 0)                        System.out.print(", ");                    System.out.print(exceptions[j].getName());                }                System.out.println("]");            }            /***** </DEBUG> */        }         /**         * Finally, pre-compute the interface hash to be used by         * stubs/skeletons for this remote class.         */        interfaceHash = computeInterfaceHash();         return true;    }     /**     * Collect and validate all methods from given interface and all of     * its superinterfaces as remote methods.  Remote methods are added     * to the supplied hashtable.  Returns true if successful,     * or false if an error occurred.     */    private boolean collectRemoteMethods(ClassDefinition interfaceDef,                                         Hashtable<String, Method> table)    {        if (!interfaceDef.isInterface()) {            throw new Error(                            "expected interface, not class: " + interfaceDef.getName());        }         /*         * rmic used to enforce that a remote interface could not extend         * a non-remote interface, i.e. an interface that did not itself         * extend from Remote.  The current version of rmic does not have         * this restriction, so the following code is now commented out.         *         * Verify that this interface extends Remote, since all interfaces         * extended by a remote interface must implement Remote.         *         *      try {         *          if (!defRemote.implementedBy(env,         *              interfaceDef.getClassDeclaration()))         *          {         *              env.error(0, "rmic.can.mix.remote.nonremote",         *                  interfaceDef.getName());         *              return false;         *          }         *      } catch (ClassNotFound e) {         *          env.error(0, "class.not.found", e.name,         *              interfaceDef.getName());         *          return false;         *      }         */         boolean errors = false;         /*         * Search interface's members for methods.         */    nextMember:        for (MemberDefinition member = interfaceDef.getFirstMember();             member != null;             member = member.getNextMember())            {                if (member.isMethod() &&                    !member.isConstructor() && !member.isInitializer())                    {                        /*                         * Verify that each method throws RemoteException.                         */                        ClassDeclaration[] exceptions = member.getExceptions(env);                        boolean hasRemoteException = false;                        for (int i = 0; i < exceptions.length; i++) {                            /*                             * rmic used to enforce that a remote method had to                             * explicitly list RemoteException in its "throws"                             * clause; i.e., just throwing Exception was not                             * acceptable.  The current version of rmic does not                             * have this restriction, so the following code is                             * now commented out.  Instead, the method is                             * considered valid if RemoteException is a subclass                             * of any of the methods declared exceptions.                             *                             *  if (exceptions[i].getName().equals(                             *      idRemoteException))                             *  {                             *      hasRemoteException = true;                             *      break;                             *  }                             */                            try {                                if (defRemoteException.subClassOf(                                                                  env, exceptions[i]))                                    {                                        hasRemoteException = true;                                        break;                                    }                            } catch (ClassNotFound e) {                                env.error(0, "class.not.found", e.name,                                          interfaceDef.getName());                                continue nextMember;                            }                        }                        /*                         * If this method did not throw RemoteException as required,                         * generate the error but continue, so that multiple such                         * errors can be reported.                         */                        if (!hasRemoteException) {                            env.error(0, "rmic.must.throw.remoteexception",                                      interfaceDef.getName(), member.toString());                            errors = true;                            continue nextMember;                        }                         /*                         * Verify that the implementation of this method throws only                         * java.lang.Exception or its subclasses (fix bugid 4092486).                         * JRMP does not support remote methods throwing                         * java.lang.Throwable or other subclasses.                         */                        try {                            MemberDefinition implMethod = implClassDef.findMethod(                                                                                  env, member.getName(), member.getType());                            if (implMethod != null) {           // should not be null                                exceptions = implMethod.getExceptions(env);                                for (int i = 0; i < exceptions.length; i++) {                                    if (!defException.superClassOf(                                                                   env, exceptions[i]))                                        {                                            env.error(0, "rmic.must.only.throw.exception",                                                      implMethod.toString(),                                                      exceptions[i].getName());

Lines 1–500 of 876.