1 /*
2 * Copyright 1996-2003 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 sun.rmi.server;
27
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.ObjectInputStream;
31 import java.io.ObjectStreamClass;
32 import java.io.StreamCorruptedException;
33 import java.net.URL;
34 import java.util;
35 import java.security.AccessControlException;
36 import java.security.Permission;
37
38 import java.rmi.server.RMIClassLoader;
39
40 /**
41 * MarshalInputStream is an extension of ObjectInputStream. When resolving
42 * a class, it reads an object from the stream written by a corresponding
43 * MarshalOutputStream. If the class to be resolved is not available
44 * locally, from the first class loader on the execution stack, or from the
45 * context class loader of the current thread, it will attempt to load the
46 * class from the location annotated by the sending MarshalOutputStream.
47 * This location object must be a string representing a path of URLs.
48 *
49 * A new MarshalInputStream should be created to deserialize remote objects or
50 * graphs containing remote objects. Objects are created from the stream
51 * using the ObjectInputStream.readObject method.
52 *
53 * @author Peter Jones
54 */
55 public class MarshalInputStream extends ObjectInputStream {
56
57 /**
58 * value of "java.rmi.server.useCodebaseOnly" property,
59 * as cached at class initialization time.
60 */
61 private static final boolean useCodebaseOnlyProperty =
62 java.security.AccessController.doPrivileged(
63 new sun.security.action.GetBooleanAction(
64 "java.rmi.server.useCodebaseOnly")).booleanValue();
65
66 /** table to hold sun classes to which access is explicitly permitted */
67 protected static Map<String, Class<?>> permittedSunClasses
68 = new HashMap<String, Class<?>>(3);
69
70 /** if true, don't try superclass first in resolveClass() */
71 private boolean skipDefaultResolveClass = false;
72
73 /** callbacks to make when done() called: maps Object to Runnable */
74 private final Map<Object, Runnable> doneCallbacks
75 = new HashMap<Object, Runnable>(3);
76
77 /**
78 * if true, load classes (if not available locally) only from the
79 * URL specified by the "java.rmi.server.codebase" property.
80 */
81 private boolean useCodebaseOnly = useCodebaseOnlyProperty;
82
83 /*
84 * Fix for 4179055: The remote object services inside the
85 * activation daemon use stubs that are in the package
86 * sun.rmi.server. Classes for these stubs should be loaded from
87 * the classpath by RMI system code and not by the normal
88 * unmarshalling process as applications should not need to have
89 * permission to access the sun implementation classes.
90 *
91 * Note: this fix should be redone when API changes may be
92 * integrated
93 *
94 * During parameter unmarshalling RMI needs to explicitly permit
95 * access to three sun.* stub classes
96 */
97 static {
98 try {
99 String system =
100 "sun.rmi.server.Activation$ActivationSystemImpl_Stub";
101 String registry = "sun.rmi.registry.RegistryImpl_Stub";
102
103 permittedSunClasses.put(system, Class.forName(system));
104 permittedSunClasses.put(registry, Class.forName(registry));
105
106 } catch (ClassNotFoundException e) {
107 throw new NoClassDefFoundError("Missing system class: " +
108 e.getMessage());
109 }
110 }
111
112 /**
113 * Load the "rmi" native library.
114 */
115 static {
116 java.security.AccessController.doPrivileged(
117 new sun.security.action.LoadLibraryAction("rmi"));
118 }
119
120 /**
121 * Create a new MarshalInputStream object.
122 */
123 public MarshalInputStream(InputStream in)
124 throws IOException, StreamCorruptedException
125 {
126 super(in);
127 }
128
129 /**
130 * Returns a callback previously registered via the setDoneCallback
131 * method with given key, or null if no callback has yet been registered
132 * with that key.
133 */
134 public Runnable getDoneCallback(Object key) {
135 return doneCallbacks.get(key); // not thread-safe
136 }
137
138 /**
139 * Registers a callback to make when this stream's done() method is
140 * invoked, along with a key for retrieving the same callback instance
141 * subsequently from the getDoneCallback method.
142 */
143 public void setDoneCallback(Object key, Runnable callback) {
144 //assert(!doneCallbacks.contains(key));
145 doneCallbacks.put(key, callback); // not thread-safe
146 }
147
148 /**
149 * Indicates that the user of this MarshalInputStream is done reading
150 * objects from it, so all callbacks registered with the setDoneCallback
151 * method should now be (synchronously) executed. When this method
152 * returns, there are no more callbacks registered.
153 *
154 * This method is implicitly invoked by close() before it delegates to
155 * the superclass's close method.
156 */
157 public void done() {
158 Iterator<Runnable> iter = doneCallbacks.values().iterator();
159 while (iter.hasNext()) { // not thread-safe
160 Runnable callback = iter.next();
161 callback.run();
162 }
163 doneCallbacks.clear();
164 }
165
166 /**
167 * Closes this stream, implicitly invoking done() first.
168 */
169 public void close() throws IOException {
170 done();
171 super.close();
172 }
173
174 /**
175 * resolveClass is extended to acquire (if present) the location
176 * from which to load the specified class.
177 * It will find, load, and return the class.
178 */
179 protected Class resolveClass(ObjectStreamClass classDesc)
180 throws IOException, ClassNotFoundException
181 {
182 /*
183 * Always read annotation written by MarshalOutputStream
184 * describing where to load class from.
185 */
186 Object annotation = readLocation();
187
188 String className = classDesc.getName();
189
190 /*
191 * Unless we were told to skip this consideration, choose the
192 * "default loader" to simulate the default ObjectInputStream
193 * resolveClass mechanism (that is, choose the first non-null
194 * loader on the execution stack) to maximize the likelihood of
195 * type compatibility with calling code. (This consideration
196 * is skipped during server parameter unmarshalling using the 1.2
197 * stub protocol, because there would never be a non-null class
198 * loader on the stack in that situation anyway.)
199 */
200 ClassLoader defaultLoader =
201 skipDefaultResolveClass ? null : latestUserDefinedLoader();
202
203 /*
204 * If the "java.rmi.server.useCodebaseOnly" property was true or
205 * useCodebaseOnly() was called or the annotation is not a String,
206 * load from the local loader using the "java.rmi.server.codebase"
207 * URL. Otherwise, load from a loader using the codebase URL in
208 * the annotation.
209 */
210 String codebase = null;
211 if (!useCodebaseOnly && annotation instanceof String) {
212 codebase = (String) annotation;
213 }
214
215 try {
216 return RMIClassLoader.loadClass(codebase, className,
217 defaultLoader);
218 } catch (AccessControlException e) {
219 return checkSunClass(className, e);
220 } catch (ClassNotFoundException e) {
221 /*
222 * Fix for 4442373: delegate to ObjectInputStream.resolveClass()
223 * to resolve primitive classes.
224 */
225 try {
226 if (Character.isLowerCase(className.charAt(0)) &&
227 className.indexOf('.') == -1)
228 {
229 return super.resolveClass(classDesc);
230 }
231 } catch (ClassNotFoundException e2) {
232 }
233 throw e;
234 }
235 }
236
237 /**
238 * resolveProxyClass is extended to acquire (if present) the location
239 * to determine the class loader to define the proxy class in.
240 */
241 protected Class resolveProxyClass(String[] interfaces)
242 throws IOException, ClassNotFoundException
243 {
244 /*
245 * Always read annotation written by MarshalOutputStream.
246 */
247 Object annotation = readLocation();
248
249 ClassLoader defaultLoader =
250 skipDefaultResolveClass ? null : latestUserDefinedLoader();
251
252 String codebase = null;
253 if (!useCodebaseOnly && annotation instanceof String) {
254 codebase = (String) annotation;
255 }
256
257 return RMIClassLoader.loadProxyClass(codebase, interfaces,
258 defaultLoader);
259 }
260
261 /*
262 * Returns the first non-null class loader up the execution stack, or null
263 * if only code from the null class loader is on the stack.
264 */
265 private static native ClassLoader latestUserDefinedLoader();
266
267 /**
268 * Fix for 4179055: Need to assist resolving sun stubs; resolve
269 * class locally if it is a "permitted" sun class
270 */
271 private Class checkSunClass(String className, AccessControlException e)
272 throws AccessControlException
273 {
274 // ensure that we are giving out a stub for the correct reason
275 Permission perm = e.getPermission();
276 String name = null;
277 if (perm != null) {
278 name = perm.getName();
279 }
280
281 Class<?> resolvedClass = permittedSunClasses.get(className);
282
283 // if class not permitted, throw the SecurityException
284 if ((name == null) ||
285 (resolvedClass == null) ||
286 ((!name.equals("accessClassInPackage.sun.rmi.server")) &&
287 (!name.equals("accessClassInPackage.sun.rmi.registry"))))
288 {
289 throw e;
290 }
291
292 return resolvedClass;
293 }
294
295 /**
296 * Return the location for the class in the stream. This method can
297 * be overridden by subclasses that store this annotation somewhere
298 * else than as the next object in the stream, as is done by this class.
299 */
300 protected Object readLocation()
301 throws IOException, ClassNotFoundException
302 {
303 return readObject();
304 }
305
306 /**
307 * Set a flag to indicate that the superclass's default resolveClass()
308 * implementation should not be invoked by our resolveClass().
309 */
310 void skipDefaultResolveClass() {
311 skipDefaultResolveClass = true;
312 }
313
314 /**
315 * Disable code downloading except from the URL specified by the
316 * "java.rmi.server.codebase" property.
317 */
318 void useCodebaseOnly() {
319 useCodebaseOnly = true;
320 }
321 }