1 package com.atlassian.scheduler.core.util;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.io.ObjectInputStream;
7 import java.io.ObjectStreamClass;
8
9 import static com.atlassian.util.concurrent.Assertions.notNull;
10
11 /**
12 * An object input stream that uses the provided {@code ClassLoader} in preference
13 * to any other.
14 */
15 public class ClassLoaderAwareObjectInputStream extends ObjectInputStream {
16 private final ClassLoader classLoader;
17
18 /**
19 * @param classLoader the class loader to use for loading classes
20 * @param parameters the byte array to be deserialized; must not be {@code null} and is not guarded against
21 * modification for efficiency reasons
22 * @throws IOException if {@link ObjectInputStream#ObjectInputStream(InputStream)} itself does
23 */
24 public ClassLoaderAwareObjectInputStream(ClassLoader classLoader, byte[] parameters) throws IOException {
25 super(new ByteArrayInputStream(notNull("parameters", parameters)));
26 this.classLoader = notNull("classLoader", classLoader);
27 }
28
29 @Override
30 protected Class<?> resolveClass(final ObjectStreamClass desc) throws IOException, ClassNotFoundException {
31 try {
32 return Class.forName(desc.getName(), false, classLoader);
33 } catch (ClassNotFoundException originalEx) {
34 try {
35 // Desperation. Maybe we shouldn't even be trying? If it succeeds, we may be getting the
36 // "wrong" class anyway because ObjectInputStream selects the ClassLoader from the "nearest"
37 // one it finds on the Thread's stack that is not the system ClassLoader. This will *probably*
38 // be the webapp's ClassLoader, so we are *probably* ok. If this becomes a problem and we
39 // decide to drop this delegation, then we should reproduce the logic that it has for mapping
40 // primitive types; e.g., "int" -> Integer.TYPE
41 return super.resolveClass(desc);
42 } catch (ClassNotFoundException ignoredEx) {
43 // Prefer the exception that we got from our own ClassLoader to avoid confusion, since that is
44 // the one we are expected to be using.
45 throw originalEx;
46 }
47 }
48 }
49 }