/* * 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(); } }; }