1 /*
   2  * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package sun.rmi.server;
  26 
  27 import java.io.ByteArrayOutputStream;
  28 import java.io.IOException;
  29 import java.io.DataOutputStream;
  30 import java.lang.reflect.Constructor;
  31 import java.lang.reflect.InvocationHandler;
  32 import java.lang.reflect.InvocationTargetException;
  33 import java.lang.reflect.Proxy;
  34 import java.lang.reflect.Method;
  35 import java.rmi.Remote;
  36 import java.rmi.RemoteException;
  37 import java.rmi.StubNotFoundException;
  38 import java.rmi.registry.Registry;
  39 import java.rmi.server.LogStream;
  40 import java.rmi.server.ObjID;
  41 import java.rmi.server.RMIClientSocketFactory;
  42 import java.rmi.server.RemoteObjectInvocationHandler;
  43 import java.rmi.server.RemoteRef;
  44 import java.rmi.server.RemoteStub;
  45 import java.rmi.server.Skeleton;
  46 import java.rmi.server.SkeletonNotFoundException;
  47 import java.security.AccessController;
  48 import java.security.MessageDigest;
  49 import java.security.DigestOutputStream;
  50 import java.security.NoSuchAlgorithmException;
  51 import java.security.PrivilegedAction;
  52 import java.util.ArrayList;
  53 import java.util.Collections;
  54 import java.util.Map;
  55 import java.util.WeakHashMap;
  56 import sun.rmi.registry.RegistryImpl;
  57 import sun.rmi.runtime.Log;
  58 import sun.rmi.transport.LiveRef;
  59 import sun.rmi.transport.tcp.TCPEndpoint;
  60 import sun.security.action.GetBooleanAction;
  61 import sun.security.action.GetPropertyAction;
  62 
  63 /**
  64  * A utility class with static methods for creating stubs/proxies and
  65  * skeletons for remote objects.
  66  */
  67 @SuppressWarnings("deprecation")
  68 public final class Util {
  69 
  70     /** "server" package log level */
  71     static final int logLevel = LogStream.parseLevel(
  72         AccessController.doPrivileged(
  73             new GetPropertyAction("sun.rmi.server.logLevel")));
  74 
  75     /** server reference log */
  76     public static final Log serverRefLog =
  77         Log.getLog("sun.rmi.server.ref", "transport", Util.logLevel);
  78 
  79     /** cached value of property java.rmi.server.ignoreStubClasses */
  80     private static final boolean ignoreStubClasses =
  81         AccessController.doPrivileged(
  82             new GetBooleanAction("java.rmi.server.ignoreStubClasses")).
  83             booleanValue();
  84 
  85     /** cache of  impl classes that have no corresponding stub class */
  86     private static final Map<Class<?>, Void> withoutStubs =
  87         Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>(11));
  88 
  89     /** parameter types for stub constructor */
  90     private static final Class<?>[] stubConsParamTypes = { RemoteRef.class };
  91 
  92     private Util() {
  93     }
  94 
  95     /**
  96      * Returns a proxy for the specified implClass.
  97      *
  98      * If both of the following criteria is satisfied, a dynamic proxy for
  99      * the specified implClass is returned (otherwise a RemoteStub instance
 100      * for the specified implClass is returned):
 101      *
 102      *    a) either the property java.rmi.server.ignoreStubClasses is true or
 103      *       a pregenerated stub class does not exist for the impl class, and
 104      *    b) forceStubUse is false.
 105      *
 106      * If the above criteria are satisfied, this method constructs a
 107      * dynamic proxy instance (that implements the remote interfaces of
 108      * implClass) constructed with a RemoteObjectInvocationHandler instance
 109      * constructed with the clientRef.
 110      *
 111      * Otherwise, this method loads the pregenerated stub class (which
 112      * extends RemoteStub and implements the remote interfaces of
 113      * implClass) and constructs an instance of the pregenerated stub
 114      * class with the clientRef.
 115      *
 116      * @param implClass the class to obtain remote interfaces from
 117      * @param clientRef the remote ref to use in the invocation handler
 118      * @param forceStubUse if true, forces creation of a RemoteStub
 119      * @throws IllegalArgumentException if implClass implements illegal
 120      * remote interfaces
 121      * @throws StubNotFoundException if problem locating/creating stub or
 122      * creating the dynamic proxy instance
 123      **/
 124     public static Remote createProxy(Class<?> implClass,
 125                                      RemoteRef clientRef,
 126                                      boolean forceStubUse)
 127         throws StubNotFoundException
 128     {
 129         Class<?> remoteClass;
 130 
 131         try {
 132             remoteClass = getRemoteClass(implClass);
 133         } catch (ClassNotFoundException ex ) {
 134             throw new StubNotFoundException(
 135                 "object does not implement a remote interface: " +
 136                 implClass.getName());
 137         }
 138 
 139         if (forceStubUse ||
 140             !(ignoreStubClasses || !stubClassExists(remoteClass)))
 141         {
 142             return createStub(remoteClass, clientRef);
 143         }
 144 
 145         final ClassLoader loader = implClass.getClassLoader();
 146         final Class<?>[] interfaces = getRemoteInterfaces(implClass);
 147         final InvocationHandler handler =
 148             new RemoteObjectInvocationHandler(clientRef);
 149 
 150         /* REMIND: private remote interfaces? */
 151 
 152         try {
 153             return AccessController.doPrivileged(new PrivilegedAction<Remote>() {
 154                 public Remote run() {
 155                     return (Remote) Proxy.newProxyInstance(loader,
 156                                                            interfaces,
 157                                                            handler);
 158                 }});
 159         } catch (IllegalArgumentException e) {
 160             throw new StubNotFoundException("unable to create proxy", e);
 161         }
 162     }
 163 
 164     /**
 165      * Returns true if a stub class for the given impl class can be loaded,
 166      * otherwise returns false.
 167      *
 168      * @param remoteClass the class to obtain remote interfaces from
 169      */
 170     private static boolean stubClassExists(Class<?> remoteClass) {
 171         if (!withoutStubs.containsKey(remoteClass)) {
 172             try {
 173                 Class.forName(remoteClass.getName() + "_Stub",
 174                               false,
 175                               remoteClass.getClassLoader());
 176                 return true;
 177 
 178             } catch (ClassNotFoundException cnfe) {
 179                 withoutStubs.put(remoteClass, null);
 180             }
 181         }
 182         return false;
 183     }
 184 
 185     /*
 186      * Returns the class/superclass that implements the remote interface.
 187      * @throws ClassNotFoundException if no class is found to have a
 188      * remote interface
 189      */
 190     private static Class<?> getRemoteClass(Class<?> cl)
 191         throws ClassNotFoundException
 192     {
 193         while (cl != null) {
 194             Class<?>[] interfaces = cl.getInterfaces();
 195             for (int i = interfaces.length -1; i >= 0; i--) {
 196                 if (Remote.class.isAssignableFrom(interfaces[i]))
 197                     return cl;          // this class implements remote object
 198             }
 199             cl = cl.getSuperclass();
 200         }
 201         throw new ClassNotFoundException(
 202                 "class does not implement java.rmi.Remote");
 203     }
 204 
 205     /**
 206      * Returns an array containing the remote interfaces implemented
 207      * by the given class.
 208      *
 209      * @param   remoteClass the class to obtain remote interfaces from
 210      * @throws  IllegalArgumentException if remoteClass implements
 211      *          any illegal remote interfaces
 212      * @throws  NullPointerException if remoteClass is null
 213      */
 214     private static Class<?>[] getRemoteInterfaces(Class<?> remoteClass) {
 215         ArrayList<Class<?>> list = new ArrayList<>();
 216         getRemoteInterfaces(list, remoteClass);
 217         return list.toArray(new Class<?>[list.size()]);
 218     }
 219 
 220     /**
 221      * Fills the given array list with the remote interfaces implemented
 222      * by the given class.
 223      *
 224      * @throws  IllegalArgumentException if the specified class implements
 225      *          any illegal remote interfaces
 226      * @throws  NullPointerException if the specified class or list is null
 227      */
 228     private static void getRemoteInterfaces(ArrayList<Class<?>> list, Class<?> cl) {
 229         Class<?> superclass = cl.getSuperclass();
 230         if (superclass != null) {
 231             getRemoteInterfaces(list, superclass);
 232         }
 233 
 234         Class<?>[] interfaces = cl.getInterfaces();
 235         for (int i = 0; i < interfaces.length; i++) {
 236             Class<?> intf = interfaces[i];
 237             /*
 238              * If it is a remote interface (if it extends from
 239              * java.rmi.Remote) and is not already in the list,
 240              * then add the interface to the list.
 241              */
 242             if (Remote.class.isAssignableFrom(intf)) {
 243                 if (!(list.contains(intf))) {
 244                     Method[] methods = intf.getMethods();
 245                     for (int j = 0; j < methods.length; j++) {
 246                         checkMethod(methods[j]);
 247                     }
 248                     list.add(intf);
 249                 }
 250             }
 251         }
 252     }
 253 
 254     /**
 255      * Verifies that the supplied method has at least one declared exception
 256      * type that is RemoteException or one of its superclasses.  If not,
 257      * then this method throws IllegalArgumentException.
 258      *
 259      * @throws IllegalArgumentException if m is an illegal remote method
 260      */
 261     private static void checkMethod(Method m) {
 262         Class<?>[] ex = m.getExceptionTypes();
 263         for (int i = 0; i < ex.length; i++) {
 264             if (ex[i].isAssignableFrom(RemoteException.class))
 265                 return;
 266         }
 267         throw new IllegalArgumentException(
 268             "illegal remote method encountered: " + m);
 269     }
 270 
 271     /**
 272      * Creates a RemoteStub instance for the specified class, constructed
 273      * with the specified RemoteRef.  The supplied class must be the most
 274      * derived class in the remote object's superclass chain that
 275      * implements a remote interface.  The stub class name is the name of
 276      * the specified remoteClass with the suffix "_Stub".  The loading of
 277      * the stub class is initiated from class loader of the specified class
 278      * (which may be the bootstrap class loader).
 279      **/
 280     private static RemoteStub createStub(Class<?> remoteClass, RemoteRef ref)
 281         throws StubNotFoundException
 282     {
 283         String stubname = remoteClass.getName() + "_Stub";
 284 
 285         /* Make sure to use the local stub loader for the stub classes.
 286          * When loaded by the local loader the load path can be
 287          * propagated to remote clients, by the MarshalOutputStream/InStream
 288          * pickle methods
 289          */
 290         try {
 291             Class<?> stubcl =
 292                 Class.forName(stubname, false, remoteClass.getClassLoader());
 293             Constructor<?> cons = stubcl.getConstructor(stubConsParamTypes);
 294             return (RemoteStub) cons.newInstance(new Object[] { ref });
 295 
 296         } catch (ClassNotFoundException e) {
 297             throw new StubNotFoundException(
 298                 "Stub class not found: " + stubname, e);
 299         } catch (NoSuchMethodException e) {
 300             throw new StubNotFoundException(
 301                 "Stub class missing constructor: " + stubname, e);
 302         } catch (InstantiationException e) {
 303             throw new StubNotFoundException(
 304                 "Can't create instance of stub class: " + stubname, e);
 305         } catch (IllegalAccessException e) {
 306             throw new StubNotFoundException(
 307                 "Stub class constructor not public: " + stubname, e);
 308         } catch (InvocationTargetException e) {
 309             throw new StubNotFoundException(
 310                 "Exception creating instance of stub class: " + stubname, e);
 311         } catch (ClassCastException e) {
 312             throw new StubNotFoundException(
 313                 "Stub class not instance of RemoteStub: " + stubname, e);
 314         }
 315     }
 316 
 317     /**
 318      * Locate and return the Skeleton for the specified remote object
 319      */
 320     static Skeleton createSkeleton(Remote object)
 321         throws SkeletonNotFoundException
 322     {
 323         Class<?> cl;
 324         try {
 325             cl = getRemoteClass(object.getClass());
 326         } catch (ClassNotFoundException ex ) {
 327             throw new SkeletonNotFoundException(
 328                 "object does not implement a remote interface: " +
 329                 object.getClass().getName());
 330         }
 331 
 332         // now try to load the skeleton based ont he name of the class
 333         String skelname = cl.getName() + "_Skel";
 334         try {
 335             Class<?> skelcl = Class.forName(skelname, false, cl.getClassLoader());
 336 
 337             return (Skeleton)skelcl.newInstance();
 338         } catch (ClassNotFoundException ex) {
 339             throw new SkeletonNotFoundException("Skeleton class not found: " +
 340                                                 skelname, ex);
 341         } catch (InstantiationException ex) {
 342             throw new SkeletonNotFoundException("Can't create skeleton: " +
 343                                                 skelname, ex);
 344         } catch (IllegalAccessException ex) {
 345             throw new SkeletonNotFoundException("No public constructor: " +
 346                                                 skelname, ex);
 347         } catch (ClassCastException ex) {
 348             throw new SkeletonNotFoundException(
 349                 "Skeleton not of correct class: " + skelname, ex);
 350         }
 351     }
 352 
 353     /**
 354      * Compute the "method hash" of a remote method.  The method hash
 355      * is a long containing the first 64 bits of the SHA digest from
 356      * the UTF encoded string of the method name and descriptor.
 357      */
 358     public static long computeMethodHash(Method m) {
 359         long hash = 0;
 360         ByteArrayOutputStream sink = new ByteArrayOutputStream(127);
 361         try {
 362             MessageDigest md = MessageDigest.getInstance("SHA");
 363             DataOutputStream out = new DataOutputStream(
 364                 new DigestOutputStream(sink, md));
 365 
 366             String s = getMethodNameAndDescriptor(m);
 367             if (serverRefLog.isLoggable(Log.VERBOSE)) {
 368                 serverRefLog.log(Log.VERBOSE,
 369                     "string used for method hash: \"" + s + "\"");
 370             }
 371             out.writeUTF(s);
 372 
 373             // use only the first 64 bits of the digest for the hash
 374             out.flush();
 375             byte hasharray[] = md.digest();
 376             for (int i = 0; i < Math.min(8, hasharray.length); i++) {
 377                 hash += ((long) (hasharray[i] & 0xFF)) << (i * 8);
 378             }
 379         } catch (IOException ignore) {
 380             /* can't happen, but be deterministic anyway. */
 381             hash = -1;
 382         } catch (NoSuchAlgorithmException complain) {
 383             throw new SecurityException(complain.getMessage());
 384         }
 385         return hash;
 386     }
 387 
 388     /**
 389      * Return a string consisting of the given method's name followed by
 390      * its "method descriptor", as appropriate for use in the computation
 391      * of the "method hash".
 392      *
 393      * See section 4.3.3 of The Java Virtual Machine Specification for
 394      * the definition of a "method descriptor".
 395      */
 396     private static String getMethodNameAndDescriptor(Method m) {
 397         StringBuffer desc = new StringBuffer(m.getName());
 398         desc.append('(');
 399         Class<?>[] paramTypes = m.getParameterTypes();
 400         for (int i = 0; i < paramTypes.length; i++) {
 401             desc.append(getTypeDescriptor(paramTypes[i]));
 402         }
 403         desc.append(')');
 404         Class<?> returnType = m.getReturnType();
 405         if (returnType == void.class) { // optimization: handle void here
 406             desc.append('V');
 407         } else {
 408             desc.append(getTypeDescriptor(returnType));
 409         }
 410         return desc.toString();
 411     }
 412 
 413     /**
 414      * Get the descriptor of a particular type, as appropriate for either
 415      * a parameter or return type in a method descriptor.
 416      */
 417     private static String getTypeDescriptor(Class<?> type) {
 418         if (type.isPrimitive()) {
 419             if (type == int.class) {
 420                 return "I";
 421             } else if (type == boolean.class) {
 422                 return "Z";
 423             } else if (type == byte.class) {
 424                 return "B";
 425             } else if (type == char.class) {
 426                 return "C";
 427             } else if (type == short.class) {
 428                 return "S";
 429             } else if (type == long.class) {
 430                 return "J";
 431             } else if (type == float.class) {
 432                 return "F";
 433             } else if (type == double.class) {
 434                 return "D";
 435             } else if (type == void.class) {
 436                 return "V";
 437             } else {
 438                 throw new Error("unrecognized primitive type: " + type);
 439             }
 440         } else if (type.isArray()) {
 441             /*
 442              * According to JLS 20.3.2, the getName() method on Class does
 443              * return the VM type descriptor format for array classes (only);
 444              * using that should be quicker than the otherwise obvious code:
 445              *
 446              *     return "[" + getTypeDescriptor(type.getComponentType());
 447              */
 448             return type.getName().replace('.', '/');
 449         } else {
 450             return "L" + type.getName().replace('.', '/') + ";";
 451         }
 452     }
 453 
 454     /**
 455      * Returns the binary name of the given type without package
 456      * qualification.  Nested types are treated no differently from
 457      * top-level types, so for a nested type, the returned name will
 458      * still be qualified with the simple name of its enclosing
 459      * top-level type (and perhaps other enclosing types), the
 460      * separator will be '$', etc.
 461      **/
 462     public static String getUnqualifiedName(Class<?> c) {
 463         String binaryName = c.getName();
 464         return binaryName.substring(binaryName.lastIndexOf('.') + 1);
 465     }
 466 }