1 /*
   2  * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.management.remote.rmi;
  27 
  28 import java.security.ProtectionDomain;
  29 
  30 /**
  31     <p>A class loader that only knows how to define a limited number
  32     of classes, and load a limited number of other classes through
  33     delegation to another loader.  It is used to get around a problem
  34     with Serialization, in particular as used by RMI. The JMX Remote API
  35     defines exactly what class loader must be used to deserialize arguments on
  36     the server, and return values on the client.  We communicate this class
  37     loader to RMI by setting it as the context class loader.  RMI uses the
  38     context class loader to load classes as it deserializes, which is what we
  39     want.  However, before consulting the context class loader, it
  40     looks up the call stack for a class with a non-null class loader,
  41     and uses that if it finds one.  So, in the standalone version of
  42     javax.management.remote, if the class you're looking for is known
  43     to the loader of jmxremote.jar (typically the system class loader)
  44     then that loader will load it.  This contradicts the class-loading
  45     semantics required.
  46 
  47     <p>We get around the problem by ensuring that the search up the
  48     call stack will find a non-null class loader that doesn't load any
  49     classes of interest, namely this one.  So even though this loader
  50     is indeed consulted during deserialization, it never finds the
  51     class being deserialized.  RMI then proceeds to use the context
  52     class loader, as we require.
  53 
  54     <p>This loader is constructed with the name and byte-code of one
  55     or more classes that it defines, and a class-loader to which it
  56     will delegate certain other classes required by that byte-code.
  57     We construct the byte-code somewhat painstakingly, by compiling
  58     the Java code directly, converting into a string, copying that
  59     string into the class that needs this loader, and using the
  60     stringToBytes method to convert it into the byte array.  We
  61     compile with -g:none because there's not much point in having
  62     line-number information and the like in these directly-encoded
  63     classes.
  64 
  65     <p>The referencedClassNames should contain the names of all
  66     classes that are referenced by the classes defined by this loader.
  67     It is not necessary to include standard J2SE classes, however.
  68     Here, a class is referenced if it is the superclass or a
  69     superinterface of a defined class, or if it is the type of a
  70     field, parameter, or return value.  A class is not referenced if
  71     it only appears in the throws clause of a method or constructor.
  72     Of course, referencedClassNames should not contain any classes
  73     that the user might want to deserialize, because the whole point
  74     of this loader is that it does not find such classes.
  75 */
  76 
  77 class NoCallStackClassLoader extends ClassLoader {
  78     /** Simplified constructor when this loader only defines one class.  */
  79     public NoCallStackClassLoader(String className,
  80                                   byte[] byteCode,
  81                                   String[] referencedClassNames,
  82                                   ClassLoader referencedClassLoader,
  83                                   ProtectionDomain protectionDomain) {
  84         this(new String[] {className}, new byte[][] {byteCode},
  85              referencedClassNames, referencedClassLoader, protectionDomain);
  86     }
  87 
  88     public NoCallStackClassLoader(String[] classNames,
  89                                   byte[][] byteCodes,
  90                                   String[] referencedClassNames,
  91                                   ClassLoader referencedClassLoader,
  92                                   ProtectionDomain protectionDomain) {
  93         super(null);
  94 
  95         /* Validation. */
  96         if (classNames == null || classNames.length == 0
  97             || byteCodes == null || classNames.length != byteCodes.length
  98             || referencedClassNames == null || protectionDomain == null)
  99             throw new IllegalArgumentException();
 100         for (int i = 0; i < classNames.length; i++) {
 101             if (classNames[i] == null || byteCodes[i] == null)
 102                 throw new IllegalArgumentException();
 103         }
 104         for (int i = 0; i < referencedClassNames.length; i++) {
 105             if (referencedClassNames[i] == null)
 106                 throw new IllegalArgumentException();
 107         }
 108 
 109         this.classNames = classNames;
 110         this.byteCodes = byteCodes;
 111         this.referencedClassNames = referencedClassNames;
 112         this.referencedClassLoader = referencedClassLoader;
 113         this.protectionDomain = protectionDomain;
 114     }
 115 
 116     /* This method is called at most once per name.  Define the name
 117      * if it is one of the classes whose byte code we have, or
 118      * delegate the load if it is one of the referenced classes.
 119      */
 120     @Override
 121     protected Class<?> findClass(String name) throws ClassNotFoundException {
 122         // Note: classNames is guaranteed by the constructor to be non-null.
 123         for (int i = 0; i < classNames.length; i++) {
 124             if (name.equals(classNames[i])) {
 125                 return defineClass(classNames[i], byteCodes[i], 0,
 126                                    byteCodes[i].length, protectionDomain);
 127             }
 128         }
 129 
 130         /* If the referencedClassLoader is null, it is the bootstrap
 131          * class loader, and there's no point in delegating to it
 132          * because it's already our parent class loader.
 133          */
 134         if (referencedClassLoader != null) {
 135             for (int i = 0; i < referencedClassNames.length; i++) {
 136                 if (name.equals(referencedClassNames[i]))
 137                     return referencedClassLoader.loadClass(name);
 138             }
 139         }
 140 
 141         throw new ClassNotFoundException(name);
 142     }
 143 
 144     private final String[] classNames;
 145     private final byte[][] byteCodes;
 146     private final String[] referencedClassNames;
 147     private final ClassLoader referencedClassLoader;
 148     private final ProtectionDomain protectionDomain;
 149 
 150     /**
 151      * <p>Construct a <code>byte[]</code> using the characters of the
 152      * given <code>String</code>.  Only the low-order byte of each
 153      * character is used.  This method is useful to reduce the
 154      * footprint of classes that include big byte arrays (e.g. the
 155      * byte code of other classes), because a string takes up much
 156      * less space in a class file than the byte code to initialize a
 157      * <code>byte[]</code> with the same number of bytes.</p>
 158      *
 159      * <p>We use just one byte per character even though characters
 160      * contain two bytes.  The resultant output length is much the
 161      * same: using one byte per character is shorter because it has
 162      * more characters in the optimal 1-127 range but longer because
 163      * it has more zero bytes (which are frequent, and are encoded as
 164      * two bytes in classfile UTF-8).  But one byte per character has
 165      * two key advantages: (1) you can see the string constants, which
 166      * is reassuring, (2) you don't need to know whether the class
 167      * file length is odd.</p>
 168      *
 169      * <p>This method differs from {@link String#getBytes()} in that
 170      * it does not use any encoding.  So it is guaranteed that each
 171      * byte of the result is numerically identical (mod 256) to the
 172      * corresponding character of the input.
 173      */
 174     public static byte[] stringToBytes(String s) {
 175         final int slen = s.length();
 176         byte[] bytes = new byte[slen];
 177         for (int i = 0; i < slen; i++)
 178             bytes[i] = (byte) s.charAt(i);
 179         return bytes;
 180     }
 181 }
 182 
 183 /*
 184 
 185 You can use the following Emacs function to convert class files into
 186 strings to be used by the stringToBytes method above.  Select the
 187 whole (defun...) with the mouse and type M-x eval-region, or save it
 188 to a file and do M-x load-file.  Then visit the *.class file and do
 189 M-x class-string.
 190 
 191 ;; class-string.el
 192 ;; visit the *.class file with emacs, then invoke this function
 193 
 194 (defun class-string ()
 195   "Construct a Java string whose bytes are the same as the current
 196 buffer.  The resultant string is put in a buffer called *string*,
 197 possibly with a numeric suffix like <2>.  From there it can be
 198 insert-buffer'd into a Java program."
 199   (interactive)
 200   (let* ((s (buffer-string))
 201          (slen (length s))
 202          (i 0)
 203          (buf (generate-new-buffer "*string*")))
 204     (set-buffer buf)
 205     (insert "\"")
 206     (while (< i slen)
 207       (if (> (current-column) 61)
 208           (insert "\"+\n\""))
 209       (let ((c (aref s i)))
 210         (insert (cond
 211                  ((> c 126) (format "\\%o" c))
 212                  ((= c ?\") "\\\"")
 213                  ((= c ?\\) "\\\\")
 214                  ((< c 33)
 215                   (let ((nextc (if (< (1+ i) slen)
 216                                    (aref s (1+ i))
 217                                  ?\0)))
 218                     (cond
 219                      ((and (<= nextc ?7) (>= nextc ?0))
 220                       (format "\\%03o" c))
 221                      (t
 222                       (format "\\%o" c)))))
 223                  (t c))))
 224       (setq i (1+ i)))
 225     (insert "\"")
 226     (switch-to-buffer buf)))
 227 
 228 Alternatively, the following class reads a class file and outputs a string
 229 that can be used by the stringToBytes method above.
 230 
 231 import java.io.File;
 232 import java.io.FileInputStream;
 233 import java.io.IOException;
 234 
 235 public class BytesToString {
 236 
 237     public static void main(String[] args) throws IOException {
 238         File f = new File(args[0]);
 239         int len = (int)f.length();
 240         byte[] classBytes = new byte[len];
 241 
 242         FileInputStream in = new FileInputStream(args[0]);
 243         try {
 244             int pos = 0;
 245             for (;;) {
 246                 int n = in.read(classBytes, pos, (len-pos));
 247                 if (n < 0)
 248                     throw new RuntimeException("class file changed??");
 249                 pos += n;
 250                 if (pos >= n)
 251                     break;
 252             }
 253         } finally {
 254             in.close();
 255         }
 256 
 257         int pos = 0;
 258         boolean lastWasOctal = false;
 259         for (int i=0; i<len; i++) {
 260             int value = classBytes[i];
 261             if (value < 0)
 262                 value += 256;
 263             String s = null;
 264             if (value == '\\')
 265                 s = "\\\\";
 266             else if (value == '\"')
 267                 s = "\\\"";
 268             else {
 269                 if ((value >= 32 && value < 127) && ((!lastWasOctal ||
 270                     (value < '0' || value > '7')))) {
 271                     s = Character.toString((char)value);
 272                 }
 273             }
 274             if (s == null) {
 275                 s = "\\" + Integer.toString(value, 8);
 276                 lastWasOctal = true;
 277             } else {
 278                 lastWasOctal = false;
 279             }
 280             if (pos > 61) {
 281                 System.out.print("\"");
 282                 if (i<len)
 283                     System.out.print("+");
 284                 System.out.println();
 285                 pos = 0;
 286             }
 287             if (pos == 0)
 288                 System.out.print("                \"");
 289             System.out.print(s);
 290             pos += s.length();
 291         }
 292         System.out.println("\"");
 293     }
 294 }
 295 
 296 */