J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2003-2006 Sun Microsystems, Inc. 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. Sun designates this |
| 8 | * particular file as subject to the "Classpath" exception as provided |
| 9 | * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 23 | * have any 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 (including |
| 35 | RMI/IIOP). The JMX Remote API defines exactly what class loader |
| 36 | must be used to deserialize arguments on the server, and return |
| 37 | values on the client. We communicate this class loader to RMI by |
| 38 | setting it as the context class loader. RMI uses the context |
| 39 | class loader to load classes as it deserializes, which is what we |
| 40 | want. However, before consulting the context class loader, it |
| 41 | looks up the call stack for a class with a non-null class loader, |
| 42 | and uses that if it finds one. So, in the standalone version of |
| 43 | javax.management.remote, if the class you're looking for is known |
| 44 | to the loader of jmxremote.jar (typically the system class loader) |
| 45 | then that loader will load it. This contradicts the class-loading |
| 46 | semantics required. |
| 47 | |
| 48 | <p>We get around the problem by ensuring that the search up the |
| 49 | call stack will find a non-null class loader that doesn't load any |
| 50 | classes of interest, namely this one. So even though this loader |
| 51 | is indeed consulted during deserialization, it never finds the |
| 52 | class being deserialized. RMI then proceeds to use the context |
| 53 | class loader, as we require. |
| 54 | |
| 55 | <p>This loader is constructed with the name and byte-code of one |
| 56 | or more classes that it defines, and a class-loader to which it |
| 57 | will delegate certain other classes required by that byte-code. |
| 58 | We construct the byte-code somewhat painstakingly, by compiling |
| 59 | the Java code directly, converting into a string, copying that |
| 60 | string into the class that needs this loader, and using the |
| 61 | stringToBytes method to convert it into the byte array. We |
| 62 | compile with -g:none because there's not much point in having |
| 63 | line-number information and the like in these directly-encoded |
| 64 | classes. |
| 65 | |
| 66 | <p>The referencedClassNames should contain the names of all |
| 67 | classes that are referenced by the classes defined by this loader. |
| 68 | It is not necessary to include standard J2SE classes, however. |
| 69 | Here, a class is referenced if it is the superclass or a |
| 70 | superinterface of a defined class, or if it is the type of a |
| 71 | field, parameter, or return value. A class is not referenced if |
| 72 | it only appears in the throws clause of a method or constructor. |
| 73 | Of course, referencedClassNames should not contain any classes |
| 74 | that the user might want to deserialize, because the whole point |
| 75 | of this loader is that it does not find such classes. |
| 76 | */ |
| 77 | |
| 78 | class NoCallStackClassLoader extends ClassLoader { |
| 79 | /** Simplified constructor when this loader only defines one class. */ |
| 80 | public NoCallStackClassLoader(String className, |
| 81 | byte[] byteCode, |
| 82 | String[] referencedClassNames, |
| 83 | ClassLoader referencedClassLoader, |
| 84 | ProtectionDomain protectionDomain) { |
| 85 | this(new String[] {className}, new byte[][] {byteCode}, |
| 86 | referencedClassNames, referencedClassLoader, protectionDomain); |
| 87 | } |
| 88 | |
| 89 | public NoCallStackClassLoader(String[] classNames, |
| 90 | byte[][] byteCodes, |
| 91 | String[] referencedClassNames, |
| 92 | ClassLoader referencedClassLoader, |
| 93 | ProtectionDomain protectionDomain) { |
| 94 | super(null); |
| 95 | |
| 96 | /* Validation. */ |
| 97 | if (classNames == null || classNames.length == 0 |
| 98 | || byteCodes == null || classNames.length != byteCodes.length |
| 99 | || referencedClassNames == null || protectionDomain == null) |
| 100 | throw new IllegalArgumentException(); |
| 101 | for (int i = 0; i < classNames.length; i++) { |
| 102 | if (classNames[i] == null || byteCodes[i] == null) |
| 103 | throw new IllegalArgumentException(); |
| 104 | } |
| 105 | for (int i = 0; i < referencedClassNames.length; i++) { |
| 106 | if (referencedClassNames[i] == null) |
| 107 | throw new IllegalArgumentException(); |
| 108 | } |
| 109 | |
| 110 | this.classNames = classNames; |
| 111 | this.byteCodes = byteCodes; |
| 112 | this.referencedClassNames = referencedClassNames; |
| 113 | this.referencedClassLoader = referencedClassLoader; |
| 114 | this.protectionDomain = protectionDomain; |
| 115 | } |
| 116 | |
| 117 | /* This method is called at most once per name. Define the name |
| 118 | * if it is one of the classes whose byte code we have, or |
| 119 | * delegate the load if it is one of the referenced classes. |
| 120 | */ |
| 121 | protected Class findClass(String name) throws ClassNotFoundException { |
| 122 | for (int i = 0; i < classNames.length; i++) { |
| 123 | if (name.equals(classNames[i])) { |
| 124 | return defineClass(classNames[i], byteCodes[i], 0, |
| 125 | byteCodes[i].length, protectionDomain); |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | /* If the referencedClassLoader is null, it is the bootstrap |
| 130 | * class loader, and there's no point in delegating to it |
| 131 | * because it's already our parent class loader. |
| 132 | */ |
| 133 | if (referencedClassLoader != null) { |
| 134 | for (int i = 0; i < referencedClassNames.length; i++) { |
| 135 | if (name.equals(referencedClassNames[i])) |
| 136 | return referencedClassLoader.loadClass(name); |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | throw new ClassNotFoundException(name); |
| 141 | } |
| 142 | |
| 143 | private final String[] classNames; |
| 144 | private final byte[][] byteCodes; |
| 145 | private final String[] referencedClassNames; |
| 146 | private final ClassLoader referencedClassLoader; |
| 147 | private final ProtectionDomain protectionDomain; |
| 148 | |
| 149 | /** |
| 150 | * <p>Construct a <code>byte[]</code> using the characters of the |
| 151 | * given <code>String</code>. Only the low-order byte of each |
| 152 | * character is used. This method is useful to reduce the |
| 153 | * footprint of classes that include big byte arrays (e.g. the |
| 154 | * byte code of other classes), because a string takes up much |
| 155 | * less space in a class file than the byte code to initialize a |
| 156 | * <code>byte[]</code> with the same number of bytes.</p> |
| 157 | * |
| 158 | * <p>We use just one byte per character even though characters |
| 159 | * contain two bytes. The resultant output length is much the |
| 160 | * same: using one byte per character is shorter because it has |
| 161 | * more characters in the optimal 1-127 range but longer because |
| 162 | * it has more zero bytes (which are frequent, and are encoded as |
| 163 | * two bytes in classfile UTF-8). But one byte per character has |
| 164 | * two key advantages: (1) you can see the string constants, which |
| 165 | * is reassuring, (2) you don't need to know whether the class |
| 166 | * file length is odd.</p> |
| 167 | * |
| 168 | * <p>This method differs from {@link String#getBytes()} in that |
| 169 | * it does not use any encoding. So it is guaranteed that each |
| 170 | * byte of the result is numerically identical (mod 256) to the |
| 171 | * corresponding character of the input. |
| 172 | */ |
| 173 | public static byte[] stringToBytes(String s) { |
| 174 | final int slen = s.length(); |
| 175 | byte[] bytes = new byte[slen]; |
| 176 | for (int i = 0; i < slen; i++) |
| 177 | bytes[i] = (byte) s.charAt(i); |
| 178 | return bytes; |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | /* |
| 183 | |
| 184 | You can use the following Emacs function to convert class files into |
| 185 | strings to be used by the stringToBytes method above. Select the |
| 186 | whole (defun...) with the mouse and type M-x eval-region, or save it |
| 187 | to a file and do M-x load-file. Then visit the *.class file and do |
| 188 | M-x class-string. |
| 189 | |
| 190 | ;; class-string.el |
| 191 | ;; visit the *.class file with emacs, then invoke this function |
| 192 | |
| 193 | (defun class-string () |
| 194 | "Construct a Java string whose bytes are the same as the current |
| 195 | buffer. The resultant string is put in a buffer called *string*, |
| 196 | possibly with a numeric suffix like <2>. From there it can be |
| 197 | insert-buffer'd into a Java program." |
| 198 | (interactive) |
| 199 | (let* ((s (buffer-string)) |
| 200 | (slen (length s)) |
| 201 | (i 0) |
| 202 | (buf (generate-new-buffer "*string*"))) |
| 203 | (set-buffer buf) |
| 204 | (insert "\"") |
| 205 | (while (< i slen) |
| 206 | (if (> (current-column) 61) |
| 207 | (insert "\"+\n\"")) |
| 208 | (let ((c (aref s i))) |
| 209 | (insert (cond |
| 210 | ((> c 126) (format "\\%o" c)) |
| 211 | ((= c ?\") "\\\"") |
| 212 | ((= c ?\\) "\\\\") |
| 213 | ((< c 33) |
| 214 | (let ((nextc (if (< (1+ i) slen) |
| 215 | (aref s (1+ i)) |
| 216 | ?\0))) |
| 217 | (cond |
| 218 | ((and (<= nextc ?7) (>= nextc ?0)) |
| 219 | (format "\\%03o" c)) |
| 220 | (t |
| 221 | (format "\\%o" c))))) |
| 222 | (t c)))) |
| 223 | (setq i (1+ i))) |
| 224 | (insert "\"") |
| 225 | (switch-to-buffer buf))) |
| 226 | |
| 227 | */ |