blob: a4c5fe0eadf47097e081213234d4290310b662be [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
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
26package javax.management.remote.rmi;
27
28import 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
78class 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
184You can use the following Emacs function to convert class files into
185strings to be used by the stringToBytes method above. Select the
186whole (defun...) with the mouse and type M-x eval-region, or save it
187to a file and do M-x load-file. Then visit the *.class file and do
188M-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
195buffer. The resultant string is put in a buffer called *string*,
196possibly with a numeric suffix like <2>. From there it can be
197insert-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*/