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