KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > management > remote > rmi > NoCallStackClassLoader


1 /*
2  * @(#)NoCallStackClassLoader.java 1.5 04/02/05
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.management.remote.rmi;
9
10 import java.io.ByteArrayOutputStream JavaDoc;
11 import java.io.DataOutputStream JavaDoc;
12 import java.security.ProtectionDomain JavaDoc;
13
14 /**
15     <p>A class loader that only knows how to define a limited number
16     of classes, and load a limited number of other classes through
17     delegation to another loader. It is used to get around a problem
18     with Serialization, in particular as used by RMI (including
19     RMI/IIOP). The JMX Remote API defines exactly what class loader
20     must be used to deserialize arguments on the server, and return
21     values on the client. We communicate this class loader to RMI by
22     setting it as the context class loader. RMI uses the context
23     class loader to load classes as it deserializes, which is what we
24     want. However, before consulting the context class loader, it
25     looks up the call stack for a class with a non-null class loader,
26     and uses that if it finds one. So, in the standalone version of
27     javax.management.remote, if the class you're looking for is known
28     to the loader of jmxremote.jar (typically the system class loader)
29     then that loader will load it. This contradicts the class-loading
30     semantics required.
31     
32     <p>We get around the problem by ensuring that the search up the
33     call stack will find a non-null class loader that doesn't load any
34     classes of interest, namely this one. So even though this loader
35     is indeed consulted during deserialization, it never finds the
36     class being deserialized. RMI then proceeds to use the context
37     class loader, as we require.
38
39     <p>This loader is constructed with the name and byte-code of one
40     or more classes that it defines, and a class-loader to which it
41     will delegate certain other classes required by that byte-code.
42     We construct the byte-code somewhat painstakingly, by compiling
43     the Java code directly, converting into a string, copying that
44     string into the class that needs this loader, and using the
45     stringToBytes method to convert it into the byte array. We
46     compile with -g:none because there's not much point in having
47     line-number information and the like in these directly-encoded
48     classes.
49
50     <p>The referencedClassNames should contain the names of all
51     classes that are referenced by the classes defined by this loader.
52     It is not necessary to include standard J2SE classes, however.
53     Here, a class is referenced if it is the superclass or a
54     superinterface of a defined class, or if it is the type of a
55     field, parameter, or return value. A class is not referenced if
56     it only appears in the throws clause of a method or constructor.
57     Of course, referencedClassNames should not contain any classes
58     that the user might want to deserialize, because the whole point
59     of this loader is that it does not find such classes.
60 */

61
62 class NoCallStackClassLoader extends ClassLoader JavaDoc {
63     /** Simplified constructor when this loader only defines one class. */
64     public NoCallStackClassLoader(String JavaDoc className,
65                   byte[] byteCode,
66                   String JavaDoc[] referencedClassNames,
67                   ClassLoader JavaDoc referencedClassLoader,
68                   ProtectionDomain JavaDoc protectionDomain) {
69     this(new String JavaDoc[] {className}, new byte[][] {byteCode},
70          referencedClassNames, referencedClassLoader, protectionDomain);
71     }
72
73     public NoCallStackClassLoader(String JavaDoc[] classNames,
74                   byte[][] byteCodes,
75                   String JavaDoc[] referencedClassNames,
76                   ClassLoader JavaDoc referencedClassLoader,
77                   ProtectionDomain JavaDoc protectionDomain) {
78     super(null);
79
80     /* Validation. */
81     if (classNames == null || classNames.length == 0
82         || byteCodes == null || classNames.length != byteCodes.length
83         || referencedClassNames == null || protectionDomain == null)
84         throw new IllegalArgumentException JavaDoc();
85     for (int i = 0; i < classNames.length; i++) {
86         if (classNames[i] == null || byteCodes[i] == null)
87         throw new IllegalArgumentException JavaDoc();
88     }
89     for (int i = 0; i < referencedClassNames.length; i++) {
90         if (referencedClassNames[i] == null)
91         throw new IllegalArgumentException JavaDoc();
92     }
93
94     this.classNames = classNames;
95     this.byteCodes = byteCodes;
96     this.referencedClassNames = referencedClassNames;
97     this.referencedClassLoader = referencedClassLoader;
98     this.protectionDomain = protectionDomain;
99     }
100
101     /* This method is called at most once per name. Define the name
102      * if it is one of the classes whose byte code we have, or
103      * delegate the load if it is one of the referenced classes.
104      */

105     protected Class JavaDoc findClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
106     for (int i = 0; i < classNames.length; i++) {
107         if (name.equals(classNames[i])) {
108         return defineClass(classNames[i], byteCodes[i], 0,
109                    byteCodes[i].length, protectionDomain);
110         }
111     }
112
113     /* If the referencedClassLoader is null, it is the bootstrap
114      * class loader, and there's no point in delegating to it
115      * because it's already our parent class loader.
116      */

117     if (referencedClassLoader != null) {
118         for (int i = 0; i < referencedClassNames.length; i++) {
119         if (name.equals(referencedClassNames[i]))
120             return referencedClassLoader.loadClass(name);
121         }
122     }
123
124     throw new ClassNotFoundException JavaDoc(name);
125     }
126
127     private final String JavaDoc[] classNames;
128     private final byte[][] byteCodes;
129     private final String JavaDoc[] referencedClassNames;
130     private final ClassLoader JavaDoc referencedClassLoader;
131     private final ProtectionDomain JavaDoc protectionDomain;
132
133     /**
134      * <p>Construct a <code>byte[]</code> using the characters of the
135      * given <code>String</code>. Only the low-order byte of each
136      * character is used. This method is useful to reduce the
137      * footprint of classes that include big byte arrays (e.g. the
138      * byte code of other classes), because a string takes up much
139      * less space in a class file than the byte code to initialize a
140      * <code>byte[]</code> with the same number of bytes.</p>
141      *
142      * <p>We use just one byte per character even though characters
143      * contain two bytes. The resultant output length is much the
144      * same: using one byte per character is shorter because it has
145      * more characters in the optimal 1-127 range but longer because
146      * it has more zero bytes (which are frequent, and are encoded as
147      * two bytes in classfile UTF-8). But one byte per character has
148      * two key advantages: (1) you can see the string constants, which
149      * is reassuring, (2) you don't need to know whether the class
150      * file length is odd.</p>
151      *
152      * <p>This method differs from {@link String#getBytes()} in that
153      * it does not use any encoding. So it is guaranteed that each
154      * byte of the result is numerically identical (mod 256) to the
155      * corresponding character of the input.
156      */

157     public static byte[] stringToBytes(String JavaDoc s) {
158     final int slen = s.length();
159     byte[] bytes = new byte[slen];
160     for (int i = 0; i < slen; i++)
161         bytes[i] = (byte) s.charAt(i);
162     return bytes;
163     }
164 }
165
166 /*
167
168 You can use the following Emacs function to convert class files into
169 strings to be used by the stringToBytes method above. Select the
170 whole (defun...) with the mouse and type M-x eval-region, or save it
171 to a file and do M-x load-file. Then visit the *.class file and do
172 M-x class-string.
173
174 ;; class-string.el
175 ;; visit the *.class file with emacs, then invoke this function
176
177 (defun class-string ()
178   "Construct a Java string whose bytes are the same as the current
179 buffer. The resultant string is put in a buffer called *string*,
180 possibly with a numeric suffix like <2>. From there it can be
181 insert-buffer'd into a Java program."
182   (interactive)
183   (let* ((s (buffer-string))
184      (slen (length s))
185      (i 0)
186      (buf (generate-new-buffer "*string*")))
187     (set-buffer buf)
188     (insert "\"")
189     (while (< i slen)
190       (if (> (current-column) 61)
191       (insert "\"+\n\""))
192       (let ((c (aref s i)))
193     (insert (cond
194          ((> c 126) (format "\\%o" c))
195          ((= c ?\") "\\\"")
196          ((= c ?\\) "\\\\")
197          ((< c 33)
198           (let ((nextc (if (< (1+ i) slen)
199                    (aref s (1+ i))
200                  ?\0)))
201             (cond
202              ((and (<= nextc ?7) (>= nextc ?0))
203               (format "\\%03o" c))
204              (t
205               (format "\\%o" c)))))
206          (t c))))
207       (setq i (1+ i)))
208     (insert "\"")
209     (switch-to-buffer buf)))
210
211 */

212
Popular Tags