Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/rmi/rmic/RemoteClass.java
38831 views
/*1* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.rmi.rmic;2627import java.util.Vector;28import java.util.Hashtable;29import java.util.Enumeration;30import java.io.IOException;31import java.io.ByteArrayOutputStream;32import java.io.DataOutputStream;33import java.security.MessageDigest;34import java.security.DigestOutputStream;35import java.security.NoSuchAlgorithmException;36import sun.tools.java.Type;37import sun.tools.java.ClassDefinition;38import sun.tools.java.ClassDeclaration;39import sun.tools.java.MemberDefinition;40import sun.tools.java.Identifier;41import sun.tools.java.ClassNotFound;4243/**44* A RemoteClass object encapsulates RMI-specific information about45* a remote implementation class, i.e. a class that implements46* one or more remote interfaces.47*48* WARNING: The contents of this source file are not part of any49* supported API. Code that depends on them does so at its own risk:50* they are subject to change or removal without notice.51*52* @author Peter Jones53*/54public class RemoteClass implements sun.rmi.rmic.RMIConstants {5556/**57* Create a RemoteClass object representing the remote meta-information58* of the given class.59*60* Returns true if successful. If the class is not a properly formed61* remote implementation class or if some other error occurs, the62* return value will be null, and errors will have been reported to63* the supplied BatchEnvironment.64*/65public static RemoteClass forClass(BatchEnvironment env,66ClassDefinition implClassDef)67{68RemoteClass rc = new RemoteClass(env, implClassDef);69if (rc.initialize()) {70return rc;71} else {72return null;73}74}7576/**77* Return the ClassDefinition for this class.78*/79public ClassDefinition getClassDefinition() {80return implClassDef;81}8283/**84* Return the name of the class represented by this object.85*/86public Identifier getName() {87return implClassDef.getName();88}8990/**91* Return an array of ClassDefinitions representing all of the remote92* interfaces implemented by this class.93*94* A remote interface is any interface that extends Remote,95* directly or indirectly. The remote interfaces of a class96* are the interfaces directly listed in either the class's97* "implements" clause, or the "implements" clause of any98* of its superclasses, that are remote interfaces.99*100* The order of the array returned is arbitrary, and some elements101* may be superfluous (i.e., superinterfaces of other interfaces102* in the array).103*/104public ClassDefinition[] getRemoteInterfaces() {105return remoteInterfaces.clone();106}107108/**109* Return an array of RemoteClass.Method objects representing all of110* the remote methods implemented by this class, i.e. all of the111* methods in the class's remote interfaces.112*113* The methods in the array are ordered according to the comparision114* of the strings consisting of their method name followed by their115* type signature, so each method's index in the array corresponds116* to its "operation number" in the JDK 1.1 version of the117* stub/skeleton protocol.118*/119public Method[] getRemoteMethods() {120return remoteMethods.clone();121}122123/**124* Return the "interface hash" used to match a stub/skeleton pair for125* this class in the JDK 1.1 version of the stub/skeleton protocol.126*/127public long getInterfaceHash() {128return interfaceHash;129}130131/**132* Return string representation of this object, consisting of133* the string "remote class " followed by the class name.134*/135public String toString() {136return "remote class " + implClassDef.getName().toString();137}138139/** rmic environment for this object */140private BatchEnvironment env;141142/** the remote implementation class this object corresponds to */143private ClassDefinition implClassDef;144145/** remote interfaces implemented by this class */146private ClassDefinition[] remoteInterfaces;147148/** all the remote methods of this class */149private Method[] remoteMethods;150151/** stub/skeleton "interface hash" for this class */152private long interfaceHash;153154/** cached definition for certain classes used in this environment */155private ClassDefinition defRemote;156private ClassDefinition defException;157private ClassDefinition defRemoteException;158159/**160* Create a RemoteClass instance for the given class. The resulting161* object is not yet initialized.162*/163private RemoteClass(BatchEnvironment env, ClassDefinition implClassDef) {164this.env = env;165this.implClassDef = implClassDef;166}167168/**169* Validate that the remote implementation class is properly formed170* and fill in the data structures required by the public interface.171*/172private boolean initialize() {173/*174* Verify that the "impl" is really a class, not an interface.175*/176if (implClassDef.isInterface()) {177env.error(0, "rmic.cant.make.stubs.for.interface",178implClassDef.getName());179return false;180}181182/*183* Initialize cached definitions for the Remote interface and184* the RemoteException class.185*/186try {187defRemote =188env.getClassDeclaration(idRemote).getClassDefinition(env);189defException =190env.getClassDeclaration(idJavaLangException).191getClassDefinition(env);192defRemoteException =193env.getClassDeclaration(idRemoteException).194getClassDefinition(env);195} catch (ClassNotFound e) {196env.error(0, "rmic.class.not.found", e.name);197return false;198}199200/*201* Here we find all of the remote interfaces of our remote202* implementation class. For each class up the superclass203* chain, add each directly-implemented interface that204* somehow extends Remote to a list.205*/206Vector<ClassDefinition> remotesImplemented = // list of remote interfaces found207new Vector<ClassDefinition>();208for (ClassDefinition classDef = implClassDef;209classDef != null;)210{211try {212ClassDeclaration[] interfaces = classDef.getInterfaces();213for (int i = 0; i < interfaces.length; i++) {214ClassDefinition interfaceDef =215interfaces[i].getClassDefinition(env);216/*217* Add interface to the list if it extends Remote and218* it is not already there.219*/220if (!remotesImplemented.contains(interfaceDef) &&221defRemote.implementedBy(env, interfaces[i]))222{223remotesImplemented.addElement(interfaceDef);224/***** <DEBUG> */225if (env.verbose()) {226System.out.println("[found remote interface: " +227interfaceDef.getName() + "]");228/***** </DEBUG> */229}230}231}232233/*234* Verify that the candidate remote implementation class235* implements at least one remote interface directly.236*/237if (classDef == implClassDef && remotesImplemented.isEmpty()) {238if (defRemote.implementedBy(env,239implClassDef.getClassDeclaration()))240{241/*242* This error message is used if the class does243* implement a remote interface through one of244* its superclasses, but not directly.245*/246env.error(0, "rmic.must.implement.remote.directly",247implClassDef.getName());248} else {249/*250* This error message is used if the class never251* implements a remote interface.252*/253env.error(0, "rmic.must.implement.remote",254implClassDef.getName());255}256return false;257}258259/*260* Get definition for next superclass.261*/262classDef = (classDef.getSuperClass() != null ?263classDef.getSuperClass().getClassDefinition(env) :264null);265266} catch (ClassNotFound e) {267env.error(0, "class.not.found", e.name, classDef.getName());268return false;269}270}271272/*273* The "remotesImplemented" vector now contains all of the remote274* interfaces directly implemented by the remote class or by any275* of its superclasses.276*277* At this point, we could optimize the list by removing superfluous278* entries, i.e. any interfaces that are implemented by some other279* interface in the list anyway.280*281* This should be correct; would it be worthwhile?282*283* for (int i = 0; i < remotesImplemented.size();) {284* ClassDefinition interfaceDef =285* (ClassDefinition) remotesImplemented.elementAt(i);286* boolean isOtherwiseImplemented = false;287* for (int j = 0; j < remotesImplemented.size; j++) {288* if (j != i &&289* interfaceDef.implementedBy(env, (ClassDefinition)290* remotesImplemented.elementAt(j).291* getClassDeclaration()))292* {293* isOtherwiseImplemented = true;294* break;295* }296* }297* if (isOtherwiseImplemented) {298* remotesImplemented.removeElementAt(i);299* } else {300* ++i;301* }302* }303*/304305/*306* Now we collect the methods from all of the remote interfaces307* into a hashtable.308*/309Hashtable<String, Method> methods = new Hashtable<String, Method>();310boolean errors = false;311for (Enumeration<ClassDefinition> enumeration312= remotesImplemented.elements();313enumeration.hasMoreElements();)314{315ClassDefinition interfaceDef = enumeration.nextElement();316if (!collectRemoteMethods(interfaceDef, methods))317errors = true;318}319if (errors)320return false;321322/*323* Convert vector of remote interfaces to an array324* (order is not important for this array).325*/326remoteInterfaces = new ClassDefinition[remotesImplemented.size()];327remotesImplemented.copyInto(remoteInterfaces);328329/*330* Sort table of remote methods into an array. The elements are331* sorted in ascending order of the string of the method's name332* and type signature, so that each elements index is equal to333* its operation number of the JDK 1.1 version of the stub/skeleton334* protocol.335*/336String[] orderedKeys = new String[methods.size()];337int count = 0;338for (Enumeration<Method> enumeration = methods.elements();339enumeration.hasMoreElements();)340{341Method m = enumeration.nextElement();342String key = m.getNameAndDescriptor();343int i;344for (i = count; i > 0; --i) {345if (key.compareTo(orderedKeys[i - 1]) >= 0) {346break;347}348orderedKeys[i] = orderedKeys[i - 1];349}350orderedKeys[i] = key;351++count;352}353remoteMethods = new Method[methods.size()];354for (int i = 0; i < remoteMethods.length; i++) {355remoteMethods[i] = methods.get(orderedKeys[i]);356/***** <DEBUG> */357if (env.verbose()) {358System.out.print("[found remote method <" + i + ">: " +359remoteMethods[i].getOperationString());360ClassDeclaration[] exceptions =361remoteMethods[i].getExceptions();362if (exceptions.length > 0)363System.out.print(" throws ");364for (int j = 0; j < exceptions.length; j++) {365if (j > 0)366System.out.print(", ");367System.out.print(exceptions[j].getName());368}369System.out.println("]");370}371/***** </DEBUG> */372}373374/**375* Finally, pre-compute the interface hash to be used by376* stubs/skeletons for this remote class.377*/378interfaceHash = computeInterfaceHash();379380return true;381}382383/**384* Collect and validate all methods from given interface and all of385* its superinterfaces as remote methods. Remote methods are added386* to the supplied hashtable. Returns true if successful,387* or false if an error occurred.388*/389private boolean collectRemoteMethods(ClassDefinition interfaceDef,390Hashtable<String, Method> table)391{392if (!interfaceDef.isInterface()) {393throw new Error(394"expected interface, not class: " + interfaceDef.getName());395}396397/*398* rmic used to enforce that a remote interface could not extend399* a non-remote interface, i.e. an interface that did not itself400* extend from Remote. The current version of rmic does not have401* this restriction, so the following code is now commented out.402*403* Verify that this interface extends Remote, since all interfaces404* extended by a remote interface must implement Remote.405*406* try {407* if (!defRemote.implementedBy(env,408* interfaceDef.getClassDeclaration()))409* {410* env.error(0, "rmic.can.mix.remote.nonremote",411* interfaceDef.getName());412* return false;413* }414* } catch (ClassNotFound e) {415* env.error(0, "class.not.found", e.name,416* interfaceDef.getName());417* return false;418* }419*/420421boolean errors = false;422423/*424* Search interface's members for methods.425*/426nextMember:427for (MemberDefinition member = interfaceDef.getFirstMember();428member != null;429member = member.getNextMember())430{431if (member.isMethod() &&432!member.isConstructor() && !member.isInitializer())433{434/*435* Verify that each method throws RemoteException.436*/437ClassDeclaration[] exceptions = member.getExceptions(env);438boolean hasRemoteException = false;439for (int i = 0; i < exceptions.length; i++) {440/*441* rmic used to enforce that a remote method had to442* explicitly list RemoteException in its "throws"443* clause; i.e., just throwing Exception was not444* acceptable. The current version of rmic does not445* have this restriction, so the following code is446* now commented out. Instead, the method is447* considered valid if RemoteException is a subclass448* of any of the methods declared exceptions.449*450* if (exceptions[i].getName().equals(451* idRemoteException))452* {453* hasRemoteException = true;454* break;455* }456*/457try {458if (defRemoteException.subClassOf(459env, exceptions[i]))460{461hasRemoteException = true;462break;463}464} catch (ClassNotFound e) {465env.error(0, "class.not.found", e.name,466interfaceDef.getName());467continue nextMember;468}469}470/*471* If this method did not throw RemoteException as required,472* generate the error but continue, so that multiple such473* errors can be reported.474*/475if (!hasRemoteException) {476env.error(0, "rmic.must.throw.remoteexception",477interfaceDef.getName(), member.toString());478errors = true;479continue nextMember;480}481482/*483* Verify that the implementation of this method throws only484* java.lang.Exception or its subclasses (fix bugid 4092486).485* JRMP does not support remote methods throwing486* java.lang.Throwable or other subclasses.487*/488try {489MemberDefinition implMethod = implClassDef.findMethod(490env, member.getName(), member.getType());491if (implMethod != null) { // should not be null492exceptions = implMethod.getExceptions(env);493for (int i = 0; i < exceptions.length; i++) {494if (!defException.superClassOf(495env, exceptions[i]))496{497env.error(0, "rmic.must.only.throw.exception",498implMethod.toString(),499exceptions[i].getName());500errors = true;501continue nextMember;502}503}504}505} catch (ClassNotFound e) {506env.error(0, "class.not.found", e.name,507implClassDef.getName());508continue nextMember;509}510511/*512* Create RemoteClass.Method object to represent this method513* found in a remote interface.514*/515Method newMethod = new Method(member);516/*517* Store remote method's representation in the table of518* remote methods found, keyed by its name and parameter519* signature.520*521* If the table already contains an entry with the same522* method name and parameter signature, then we must523* replace the old entry with a Method object that524* represents a legal combination of the old and the new525* methods; specifically, the combined method must have526* a throws list that contains (only) all of the checked527* exceptions that can be thrown by both the old or528* the new method (see bugid 4070653).529*/530String key = newMethod.getNameAndDescriptor();531Method oldMethod = table.get(key);532if (oldMethod != null) {533newMethod = newMethod.mergeWith(oldMethod);534if (newMethod == null) {535errors = true;536continue nextMember;537}538}539table.put(key, newMethod);540}541}542543/*544* Recursively collect methods for all superinterfaces.545*/546try {547ClassDeclaration[] superDefs = interfaceDef.getInterfaces();548for (int i = 0; i < superDefs.length; i++) {549ClassDefinition superDef =550superDefs[i].getClassDefinition(env);551if (!collectRemoteMethods(superDef, table))552errors = true;553}554} catch (ClassNotFound e) {555env.error(0, "class.not.found", e.name, interfaceDef.getName());556return false;557}558559return !errors;560}561562/**563* Compute the "interface hash" of the stub/skeleton pair for this564* remote implementation class. This is the 64-bit value used to565* enforce compatibility between a stub and a skeleton using the566* JDK 1.1 version of the stub/skeleton protocol.567*568* It is calculated using the first 64 bits of a SHA digest. The569* digest is from a stream consisting of the following data:570* (int) stub version number, always 1571* for each remote method, in order of operation number:572* (UTF) method name573* (UTF) method type signature574* for each declared exception, in alphabetical name order:575* (UTF) name of exception class576*577*/578private long computeInterfaceHash() {579long hash = 0;580ByteArrayOutputStream sink = new ByteArrayOutputStream(512);581try {582MessageDigest md = MessageDigest.getInstance("SHA");583DataOutputStream out = new DataOutputStream(584new DigestOutputStream(sink, md));585586out.writeInt(INTERFACE_HASH_STUB_VERSION);587for (int i = 0; i < remoteMethods.length; i++) {588MemberDefinition m = remoteMethods[i].getMemberDefinition();589Identifier name = m.getName();590Type type = m.getType();591592out.writeUTF(name.toString());593// type signatures already use mangled class names594out.writeUTF(type.getTypeSignature());595596ClassDeclaration exceptions[] = m.getExceptions(env);597sortClassDeclarations(exceptions);598for (int j = 0; j < exceptions.length; j++) {599out.writeUTF(Names.mangleClass(600exceptions[j].getName()).toString());601}602}603out.flush();604605// use only the first 64 bits of the digest for the hash606byte hashArray[] = md.digest();607for (int i = 0; i < Math.min(8, hashArray.length); i++) {608hash += ((long) (hashArray[i] & 0xFF)) << (i * 8);609}610} catch (IOException e) {611throw new Error(612"unexpected exception computing intetrface hash: " + e);613} catch (NoSuchAlgorithmException e) {614throw new Error(615"unexpected exception computing intetrface hash: " + e);616}617618return hash;619}620621/**622* Sort array of class declarations alphabetically by their mangled623* fully-qualified class name. This is used to feed a method's exceptions624* in a canonical order into the digest stream for the interface hash625* computation.626*/627private void sortClassDeclarations(ClassDeclaration[] decl) {628for (int i = 1; i < decl.length; i++) {629ClassDeclaration curr = decl[i];630String name = Names.mangleClass(curr.getName()).toString();631int j;632for (j = i; j > 0; j--) {633if (name.compareTo(634Names.mangleClass(decl[j - 1].getName()).toString()) >= 0)635{636break;637}638decl[j] = decl[j - 1];639}640decl[j] = curr;641}642}643644645/**646* A RemoteClass.Method object encapsulates RMI-specific information647* about a particular remote method in the remote implementation class648* represented by the outer instance.649*/650public class Method implements Cloneable {651652/**653* Return the definition of the actual class member corresponing654* to this method of a remote interface.655*656* REMIND: Can this method be removed?657*/658public MemberDefinition getMemberDefinition() {659return memberDef;660}661662/**663* Return the name of this method.664*/665public Identifier getName() {666return memberDef.getName();667}668669/**670* Return the type of this method.671*/672public Type getType() {673return memberDef.getType();674}675676/**677* Return an array of the exception classes declared to be678* thrown by this remote method.679*680* For methods with the same name and type signature inherited681* from multiple remote interfaces, the array will contain682* the set of exceptions declared in all of the interfaces'683* methods that can be legally thrown in each of them.684*/685public ClassDeclaration[] getExceptions() {686return exceptions.clone();687}688689/**690* Return the "method hash" used to identify this remote method691* in the JDK 1.2 version of the stub protocol.692*/693public long getMethodHash() {694return methodHash;695}696697/**698* Return the string representation of this method.699*/700public String toString() {701return memberDef.toString();702}703704/**705* Return the string representation of this method appropriate706* for the construction of a java.rmi.server.Operation object.707*/708public String getOperationString() {709return memberDef.toString();710}711712/**713* Return a string consisting of this method's name followed by714* its method descriptor, using the Java VM's notation for715* method descriptors (see section 4.3.3 of The Java Virtual716* Machine Specification).717*/718public String getNameAndDescriptor() {719return memberDef.getName().toString() +720memberDef.getType().getTypeSignature();721}722723/**724* Member definition for this method, from one of the remote725* interfaces that this method was found in.726*727* Note that this member definition may be only one of several728* member defintions that correspond to this remote method object,729* if several of this class's remote interfaces contain methods730* with the same name and type signature. Therefore, this member731* definition may declare more exceptions thrown that this remote732* method does.733*/734private MemberDefinition memberDef;735736/** stub "method hash" to identify this method */737private long methodHash;738739/**740* Exceptions declared to be thrown by this remote method.741*742* This list can include superfluous entries, such as743* unchecked exceptions and subclasses of other entries.744*/745private ClassDeclaration[] exceptions;746747/**748* Create a new Method object corresponding to the given749* method definition.750*/751/*752* Temporarily comment out the private modifier until753* the VM allows outer class to access inner class's754* private constructor755*/756/* private */ Method(MemberDefinition memberDef) {757this.memberDef = memberDef;758exceptions = memberDef.getExceptions(env);759methodHash = computeMethodHash();760}761762/**763* Cloning is supported by returning a shallow copy of this object.764*/765protected Object clone() {766try {767return super.clone();768} catch (CloneNotSupportedException e) {769throw new Error("clone failed");770}771}772773/**774* Return a new Method object that is a legal combination of775* this method object and another one.776*777* This requires determining the exceptions declared by the778* combined method, which must be (only) all of the exceptions779* declared in both old Methods that may thrown in either of780* them.781*/782private Method mergeWith(Method other) {783if (!getName().equals(other.getName()) ||784!getType().equals(other.getType()))785{786throw new Error("attempt to merge method \"" +787other.getNameAndDescriptor() + "\" with \"" +788getNameAndDescriptor());789}790791Vector<ClassDeclaration> legalExceptions792= new Vector<ClassDeclaration>();793try {794collectCompatibleExceptions(795other.exceptions, exceptions, legalExceptions);796collectCompatibleExceptions(797exceptions, other.exceptions, legalExceptions);798} catch (ClassNotFound e) {799env.error(0, "class.not.found", e.name,800getClassDefinition().getName());801return null;802}803804Method merged = (Method) clone();805merged.exceptions = new ClassDeclaration[legalExceptions.size()];806legalExceptions.copyInto(merged.exceptions);807808return merged;809}810811/**812* Add to the supplied list all exceptions in the "from" array813* that are subclasses of an exception in the "with" array.814*/815private void collectCompatibleExceptions(ClassDeclaration[] from,816ClassDeclaration[] with,817Vector<ClassDeclaration> list)818throws ClassNotFound819{820for (int i = 0; i < from.length; i++) {821ClassDefinition exceptionDef = from[i].getClassDefinition(env);822if (!list.contains(from[i])) {823for (int j = 0; j < with.length; j++) {824if (exceptionDef.subClassOf(env, with[j])) {825list.addElement(from[i]);826break;827}828}829}830}831}832833/**834* Compute the "method hash" of this remote method. The method835* hash is a long containing the first 64 bits of the SHA digest836* from the UTF encoded string of the method name and descriptor.837*838* REMIND: Should this method share implementation code with839* the outer class's computeInterfaceHash() method?840*/841private long computeMethodHash() {842long hash = 0;843ByteArrayOutputStream sink = new ByteArrayOutputStream(512);844try {845MessageDigest md = MessageDigest.getInstance("SHA");846DataOutputStream out = new DataOutputStream(847new DigestOutputStream(sink, md));848849String methodString = getNameAndDescriptor();850/***** <DEBUG> */851if (env.verbose()) {852System.out.println("[string used for method hash: \"" +853methodString + "\"]");854}855/***** </DEBUG> */856out.writeUTF(methodString);857858// use only the first 64 bits of the digest for the hash859out.flush();860byte hashArray[] = md.digest();861for (int i = 0; i < Math.min(8, hashArray.length); i++) {862hash += ((long) (hashArray[i] & 0xFF)) << (i * 8);863}864} catch (IOException e) {865throw new Error(866"unexpected exception computing intetrface hash: " + e);867} catch (NoSuchAlgorithmException e) {868throw new Error(869"unexpected exception computing intetrface hash: " + e);870}871872return hash;873}874}875}876877878