/* | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you under the Apache License, Version 2.0 (the | |
* "License"); you may not use this file except in compliance | |
* with the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, | |
* software distributed under the License is distributed on an | |
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
* KIND, either express or implied. See the License for the | |
* specific language governing permissions and limitations | |
* under the License. | |
*/ | |
package org.apache.axis2.context.externalize; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.ObjectStreamClass; | |
import java.lang.reflect.Modifier; | |
import java.lang.reflect.Proxy; | |
import java.security.AccessController; | |
import java.security.PrivilegedAction; | |
import java.security.PrivilegedExceptionAction; | |
import java.security.PrivilegedActionException; | |
import java.util.HashMap; | |
/** | |
* An ObjectInputStream that is constructed with a ClassLoader or ClassResolver. | |
* The default behavior is to use the ContextClassLoader | |
*/ | |
public class ObjectInputStreamWithCL extends java.io.ObjectInputStream | |
{ | |
/** | |
* <p> | |
* This interface is used to resolve OSGi declared serializable classes. | |
* </p> | |
*/ | |
public interface ClassResolver | |
{ | |
/** | |
* Attempt to load the specified class. | |
* | |
* @param className | |
* The classname. | |
* @return The class, or null if not found. | |
*/ | |
public Class resolveClass(String className); | |
} | |
private static final HashMap primClasses = new HashMap(8, 1.0F); | |
/** The class resolver */ | |
protected ClassResolver resolver; | |
static | |
{ | |
primClasses.put("boolean", boolean.class); | |
primClasses.put("byte", byte.class); | |
primClasses.put("char", char.class); | |
primClasses.put("short", short.class); | |
primClasses.put("int", int.class); | |
primClasses.put("long", long.class); | |
primClasses.put("float", float.class); | |
primClasses.put("double", double.class); | |
primClasses.put("void", void.class); | |
} | |
protected ClassLoader classloader; | |
protected String name; | |
/** | |
* Construct using ContextClassLoader | |
* @param is | |
* @throws IOException | |
*/ | |
public ObjectInputStreamWithCL(InputStream is) throws IOException | |
{ | |
super(is); | |
classloader = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() | |
{ | |
public Object run() | |
{ | |
return Thread.currentThread().getContextClassLoader(); | |
} | |
}); | |
} | |
/** | |
* Constructor that accepts a ClassLoader | |
* @param is | |
* @param cl | |
* @throws IOException | |
*/ | |
public ObjectInputStreamWithCL(InputStream is, ClassLoader cl) throws IOException | |
{ | |
super(is); | |
classloader = cl; | |
} | |
/** | |
* Constructor that accepts a ClassResolver | |
* @param is | |
* @param r ClassResolver | |
* @throws IOException | |
*/ | |
public ObjectInputStreamWithCL(InputStream is, ClassResolver r) throws IOException | |
{ | |
super(is); | |
resolver = r; | |
} | |
/** | |
* Override resolveClass so that we can use our own ClassLoader | |
*/ | |
protected Class resolveClass(ObjectStreamClass objStrmClass) throws ClassNotFoundException | |
{ | |
return resolveClass(objStrmClass.getName()); | |
} | |
private Class resolveClass(String name) throws ClassNotFoundException | |
{ | |
try | |
{ | |
this.name = name; | |
return (Class) AccessController.doPrivileged(loadAction); | |
} | |
catch (java.security.PrivilegedActionException pae) | |
{ | |
Exception wrapped = pae.getException(); | |
if (wrapped instanceof ClassNotFoundException) throw (ClassNotFoundException) wrapped; | |
throw new ClassNotFoundException(name); | |
} | |
} | |
java.security.PrivilegedExceptionAction loadAction = | |
new java.security.PrivilegedExceptionAction() | |
{ | |
public java.lang.Object run() throws Exception | |
{ | |
try | |
{ | |
Class clazz = null; | |
// If the resolver is set | |
if (resolver != null) | |
{ | |
// use the resolver to load the class. | |
clazz = resolver.resolveClass(name); | |
} | |
// if the class is not loadable | |
if (clazz == null) | |
{ | |
clazz = loadClass(name, classloader); // d296416 | |
} | |
return clazz; | |
} | |
catch (ClassNotFoundException cnf) | |
{ | |
Class c = (Class) primClasses.get(name); | |
if (c != null) | |
{ | |
return c; | |
} | |
throw cnf; | |
} | |
} | |
}; | |
// d296416: Use runtime bundle classloader (current) to resolve a class when | |
// the class could not be resolved using the specified classloader. | |
// A serializable class in a bundle should specify via | |
// <com.ibm.ws.runtime.serializable> bundle extension point | |
// that it is deserializable outside the current bundle. | |
// NOTE: Looking up current classloader is only a tactical solution, | |
// and could be deprecated in future. | |
// | |
private java.lang.Class loadClass(final String name, final ClassLoader loader) throws ClassNotFoundException | |
{ | |
try | |
{ | |
try { | |
return (Class) org.apache.axis2.java.security.AccessController.doPrivileged( | |
new PrivilegedExceptionAction() { | |
public Object run() throws ClassNotFoundException { | |
return Class.forName(name, true, loader); | |
} | |
} | |
); | |
} catch (PrivilegedActionException e) { | |
throw (ClassNotFoundException) e.getException(); | |
} | |
} | |
catch (ClassNotFoundException cnf) | |
{ | |
try { | |
return (Class) org.apache.axis2.java.security.AccessController.doPrivileged( | |
new PrivilegedExceptionAction() { | |
public Object run() throws ClassNotFoundException { | |
return Class.forName(name); | |
} | |
} | |
); | |
} catch (PrivilegedActionException e) { | |
throw (ClassNotFoundException) e.getException(); | |
} | |
} | |
} | |
/** | |
* Override to provide our own resolution | |
*/ | |
protected Class resolveProxyClass(String[] interfaces) throws ClassNotFoundException | |
{ | |
if (interfaces.length == 0) | |
{ | |
throw new ClassNotFoundException("zero-length interfaces array"); | |
} | |
Class nonPublicClass = null; | |
final Class[] classes = new Class[interfaces.length]; | |
for (int i = 0; i < interfaces.length; i++) | |
{ | |
classes[i] = resolveClass(interfaces[i]); | |
if ((classes[i].getModifiers() & Modifier.PUBLIC) == 0) | |
{ | |
// "if more than one non-public interface class loader is | |
// encountered, an IllegalAccessError is thrown" | |
if (nonPublicClass != null) | |
{ | |
throw new IllegalAccessError(nonPublicClass + " and " + classes[i] + " both declared non-public"); | |
} | |
nonPublicClass = classes[i]; | |
} | |
} | |
// The javadocs for this method say: | |
// | |
// "Unless any of the resolved interfaces are non-public, this same | |
// value of loader is also the class loader passed to | |
// Proxy.getProxyClass; if non-public interfaces are present, their | |
// class loader is passed instead" | |
// | |
// Unfortunately, we don't have a single classloader that we can use. | |
// Call getClassLoader() on either the non-public class (if any) or the | |
// first class. | |
proxyClass = nonPublicClass != null ? nonPublicClass : classes[0]; | |
final ClassLoader loader = (ClassLoader) AccessController.doPrivileged(proxyClassLoaderAction); | |
// "If Proxy.getProxyClass throws an IllegalArgumentException, | |
// resolveProxyClass will throw a ClassNotFoundException containing the | |
// IllegalArgumentException." | |
try | |
{ | |
return (Class) org.apache.axis2.java.security.AccessController.doPrivileged( | |
new PrivilegedAction() { | |
public Object run() { | |
return Proxy.getProxyClass(loader, classes); | |
} | |
} | |
); | |
} | |
catch (IllegalArgumentException ex) | |
{ | |
throw new ClassNotFoundException(ex.getMessage(), ex); | |
} | |
} | |
private Class proxyClass; | |
PrivilegedAction proxyClassLoaderAction = new PrivilegedAction() | |
{ | |
public Object run() | |
{ | |
return proxyClass.getClassLoader(); | |
} | |
}; | |
} |