KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mx4j > remote > rmi > RMIMarshaller


1 /*
2  * Copyright (C) MX4J.
3  * All rights reserved.
4  *
5  * This software is distributed under the terms of the MX4J License version 1.0.
6  * See the terms of the MX4J License in the documentation provided with this software.
7  */

8
9 package mx4j.remote.rmi;
10
11 import java.io.BufferedInputStream JavaDoc;
12 import java.io.BufferedOutputStream JavaDoc;
13 import java.io.ByteArrayOutputStream JavaDoc;
14 import java.io.IOException JavaDoc;
15 import java.io.InputStream JavaDoc;
16 import java.lang.reflect.InvocationTargetException JavaDoc;
17 import java.lang.reflect.Method JavaDoc;
18 import java.net.URL JavaDoc;
19 import java.rmi.MarshalledObject JavaDoc;
20 import java.security.AccessController JavaDoc;
21 import java.security.PrivilegedAction JavaDoc;
22 import java.security.SecureClassLoader JavaDoc;
23
24 /**
25  * Marshaller/Unmarshaller for RMI's MarshalledObjects. <br />
26  * <p/>
27  * This class implements the JMX Remote Specification, chapter 2. <br />
28  * <strong>
29  * Don't touch unless discussed on mx4j-devel@users.sourceforge.net, and unless
30  * you know what you're doing (included who wrote this javadoc).
31  * </strong>
32  * <br />
33  * IMPLEMENTATION NOTES: <br />
34  * MarshalledObject.get() loads the object it contains by using the first user-defined classloader it can find
35  * in the stack frames of the call. <br />
36  * If the class cannot be found with that loader, then the RMI semantic is tried: first the thread context
37  * classloader, then dynamic code download (if there is a security manager). <br />
38  * We need that MarshalledObject.get() unmarshals using the context classloader and not the first user-defined
39  * classloader, since this way we can implement correctly the JMX specification (see
40  * {@link #unmarshal(MarshalledObject, ClassLoader, ClassLoader)}). <br />
41  * Here we load the {@link Marshaller} class using {@link MarshallerClassLoader} that can only load the
42  * {@link Marshaller} class.
43  * It is important that {@link MarshallerClassLoader} cannot load from the classpath (the system classloader)
44  * since that would break the JMX Remote Specification compliance. <br />
45  * This URLClassLoader then becomes the first user-defined classloader in the stack frames, but it will fail
46  * to load anything else, thus allowing MarshalledObject.get() to use the thread context classloader.
47  * The stack trace will be something like:
48  * <pre>
49  * {@link Marshaller#unmarshal} [MarshallerClassLoader]
50  * ... reflection classes ... [Boot ClassLoader]
51  * {@link RMIMarshaller#unmarshal(MarshalledObject)} [System ClassLoader (normally)]
52  * </pre>
53  * <br />
54  * Note that the classloader that loaded this class may be something totally different from URLClassLoader:
55  * this is the case for the <a HREF="http://www.osgi.org">OSGi</a>. <br />
56  * We just rely on the {@link ClassLoader#getResourceAsStream(java.lang.String)} semantic to load the
57  * {@link Marshaller} class' bytes.
58  *
59  * @author <a HREF="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
60  * @version $Revision: 1.10 $
61  */

62 class RMIMarshaller
63 {
64    private static final Method JavaDoc unmarshal = getUnmarshalMethod();
65
66    private static Method JavaDoc getUnmarshalMethod()
67    {
68       return (Method JavaDoc)AccessController.doPrivileged(new PrivilegedAction JavaDoc()
69       {
70          public Object JavaDoc run()
71          {
72             String JavaDoc marshallerName = Marshaller.class.getName();
73             InputStream JavaDoc stream = Marshaller.class.getResourceAsStream(marshallerName.substring(marshallerName.lastIndexOf('.') + 1) + ".class");
74             if (stream == null) throw new Error JavaDoc("Could not load implementation class " + marshallerName);
75             BufferedInputStream JavaDoc bis = new BufferedInputStream JavaDoc(stream);
76             ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
77             BufferedOutputStream JavaDoc bos = new BufferedOutputStream JavaDoc(baos);
78             try
79             {
80                byte[] buffer = new byte[256];
81                int read = -1;
82                while ((read = bis.read(buffer)) >= 0) bos.write(buffer, 0, read);
83                bis.close();
84                bos.close();
85             }
86             catch (IOException JavaDoc x)
87             {
88                throw new Error JavaDoc(x.toString());
89             }
90
91             byte[] classBytes = baos.toByteArray();
92
93             MarshallerClassLoader loader = new MarshallerClassLoader(classBytes);
94
95             try
96             {
97                Class JavaDoc cls = loader.loadClass(marshallerName);
98                return cls.getMethod("unmarshal", new Class JavaDoc[]{MarshalledObject JavaDoc.class});
99             }
100             catch (ClassNotFoundException JavaDoc x)
101             {
102                throw new Error JavaDoc(x.toString());
103             }
104             catch (NoSuchMethodException JavaDoc x)
105             {
106                throw new Error JavaDoc(x.toString());
107             }
108          }
109       });
110    }
111
112    /**
113     * Returns a MarshalledObject obtained by marshalling the given object.
114     */

115    public static MarshalledObject JavaDoc marshal(Object JavaDoc object) throws IOException JavaDoc
116    {
117       if (object == null) return null;
118       return new MarshalledObject JavaDoc(object);
119    }
120
121    /**
122     * Returns the unmarshalled object obtained unmarshalling the given MarshalledObject,
123     * using as context classloader first the given mbeanLoader, if not null, then with the given defaultLoader.
124     */

125    public static Object JavaDoc unmarshal(MarshalledObject JavaDoc object, final ClassLoader JavaDoc mbeanLoader, final ClassLoader JavaDoc defaultLoader) throws IOException JavaDoc
126    {
127       if (object == null) return null;
128       if (mbeanLoader == null) return unmarshal(object, defaultLoader);
129
130       ClassLoader JavaDoc loader = (ClassLoader JavaDoc)AccessController.doPrivileged(new PrivilegedAction JavaDoc()
131       {
132          public Object JavaDoc run()
133          {
134             return new ExtendedClassLoader(mbeanLoader, defaultLoader);
135          }
136       });
137       return unmarshal(object, loader);
138    }
139
140    private static Object JavaDoc unmarshal(MarshalledObject JavaDoc object, ClassLoader JavaDoc loader) throws IOException JavaDoc
141    {
142       if (loader != null)
143       {
144          ClassLoader JavaDoc old = Thread.currentThread().getContextClassLoader();
145          try
146          {
147             setContextClassLoader(loader);
148             return unmarshal(object);
149          }
150          catch (IOException JavaDoc x)
151          {
152             throw x;
153          }
154          catch (ClassNotFoundException JavaDoc ignored)
155          {
156          }
157          finally
158          {
159             setContextClassLoader(old);
160          }
161       }
162       throw new IOException JavaDoc("Cannot unmarshal " + object);
163    }
164
165    private static Object JavaDoc unmarshal(MarshalledObject JavaDoc marshalled) throws IOException JavaDoc, ClassNotFoundException JavaDoc
166    {
167       try
168       {
169          return unmarshal.invoke(null, new Object JavaDoc[]{marshalled});
170       }
171       catch (InvocationTargetException JavaDoc x)
172       {
173          Throwable JavaDoc t = x.getTargetException();
174          if (t instanceof IOException JavaDoc) throw (IOException JavaDoc)t;
175          if (t instanceof ClassNotFoundException JavaDoc) throw (ClassNotFoundException JavaDoc)t;
176          throw new IOException JavaDoc(t.toString());
177       }
178       catch (Exception JavaDoc x)
179       {
180          throw new IOException JavaDoc(x.toString());
181       }
182    }
183
184    private static void setContextClassLoader(final ClassLoader JavaDoc loader)
185    {
186       AccessController.doPrivileged(new PrivilegedAction JavaDoc()
187       {
188          public Object JavaDoc run()
189          {
190             Thread.currentThread().setContextClassLoader(loader);
191             return null;
192          }
193       });
194    }
195
196    private static class MarshallerClassLoader extends SecureClassLoader JavaDoc
197    {
198       private byte[] bytes;
199
200       /**
201        * Parent classloader is null, thus we can load JDK classes, but for example not the JMX classes.
202        * This will force usage of the context classloader for unmarshalling, and the context classloader
203        * must be able to find the JMX classes. For example, when invoking setAttribute(), the marshalled
204        * parameter is a javax.management.Attribute that may contain an object of a custom class.
205        * To unmarshal the custom class (seen only by the context classloader), we must be able to unmarshal
206        * first the Attribute class that so it must either be loadable by the context classloader or by
207        * one of its ancestor classloaders.
208        */

209       private MarshallerClassLoader(byte[] classBytes)
210       {
211          super(null);
212          this.bytes = classBytes;
213       }
214
215       /**
216        * This method is overridden to define the {@link Marshaller} class and to delegate to the parent
217        * further loading.
218        * Classes from java.* packages (like java.lang.Object and java.rmi.MarshalledObject) are
219        * referenced by {@link Marshaller} itself.
220        * We don't load classes from javax.management.* packages, since we assume the context classloader
221        * can load those classes by itself or by one of its ancestor classloaders.
222        */

223       public Class JavaDoc loadClass(final String JavaDoc name) throws ClassNotFoundException JavaDoc
224       {
225          if (name.startsWith(Marshaller.class.getName()))
226          {
227             try
228             {
229                return defineClass(name, bytes, 0, bytes.length, MarshallerClassLoader.this.getClass().getProtectionDomain());
230             }
231             catch (ClassFormatError JavaDoc x)
232             {
233                throw new ClassNotFoundException JavaDoc("Class Format Error", x);
234             }
235          }
236          else
237          {
238             return super.loadClass(name);
239          }
240       }
241    }
242
243    /**
244     * This is an implementation of the extended classloader as defined by the
245     * JMX Remote Specification, chapter 2.
246     */

247    private static class ExtendedClassLoader extends SecureClassLoader JavaDoc
248    {
249       private final ClassLoader JavaDoc defaultLoader;
250
251       private ExtendedClassLoader(ClassLoader JavaDoc mbeanLoader, ClassLoader JavaDoc defaultLoader)
252       {
253          super(mbeanLoader);
254          this.defaultLoader = defaultLoader;
255       }
256
257       protected Class JavaDoc findClass(String JavaDoc name) throws ClassNotFoundException JavaDoc
258       {
259          return defaultLoader.loadClass(name);
260       }
261
262       protected URL JavaDoc findResource(String JavaDoc name)
263       {
264          return defaultLoader.getResource(name);
265       }
266    }
267 }
268
Popular Tags