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. */ NoCallStackClassLoader(String className, byte[] byteCode, String[] referencedClassNames, ClassLoader referencedClassLoader, ProtectionDomain protectionDomain)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 NoCallStackClassLoader(String[] classNames, byte[][] byteCodes, String[] referencedClassNames, ClassLoader referencedClassLoader, ProtectionDomain protectionDomain)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 findClass(String name)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 */ stringToBytes(String s)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 */ 297