001/* 002 * Copyright 2002-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.core; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.io.NotSerializableException; 022import java.io.ObjectInputStream; 023import java.io.ObjectStreamClass; 024 025import org.springframework.util.ClassUtils; 026 027/** 028 * Special ObjectInputStream subclass that resolves class names 029 * against a specific ClassLoader. Serves as base class for 030 * {@link org.springframework.remoting.rmi.CodebaseAwareObjectInputStream}. 031 * 032 * @author Juergen Hoeller 033 * @since 2.5.5 034 */ 035public class ConfigurableObjectInputStream extends ObjectInputStream { 036 037 private final ClassLoader classLoader; 038 039 private final boolean acceptProxyClasses; 040 041 042 /** 043 * Create a new ConfigurableObjectInputStream for the given InputStream and ClassLoader. 044 * @param in the InputStream to read from 045 * @param classLoader the ClassLoader to use for loading local classes 046 * @see java.io.ObjectInputStream#ObjectInputStream(java.io.InputStream) 047 */ 048 public ConfigurableObjectInputStream(InputStream in, ClassLoader classLoader) throws IOException { 049 this(in, classLoader, true); 050 } 051 052 /** 053 * Create a new ConfigurableObjectInputStream for the given InputStream and ClassLoader. 054 * @param in the InputStream to read from 055 * @param classLoader the ClassLoader to use for loading local classes 056 * @param acceptProxyClasses whether to accept deserialization of proxy classes 057 * (may be deactivated as a security measure) 058 * @see java.io.ObjectInputStream#ObjectInputStream(java.io.InputStream) 059 */ 060 public ConfigurableObjectInputStream( 061 InputStream in, ClassLoader classLoader, boolean acceptProxyClasses) throws IOException { 062 063 super(in); 064 this.classLoader = classLoader; 065 this.acceptProxyClasses = acceptProxyClasses; 066 } 067 068 069 @Override 070 protected Class<?> resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException { 071 try { 072 if (this.classLoader != null) { 073 // Use the specified ClassLoader to resolve local classes. 074 return ClassUtils.forName(classDesc.getName(), this.classLoader); 075 } 076 else { 077 // Use the default ClassLoader... 078 return super.resolveClass(classDesc); 079 } 080 } 081 catch (ClassNotFoundException ex) { 082 return resolveFallbackIfPossible(classDesc.getName(), ex); 083 } 084 } 085 086 @Override 087 protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException { 088 if (!this.acceptProxyClasses) { 089 throw new NotSerializableException("Not allowed to accept serialized proxy classes"); 090 } 091 if (this.classLoader != null) { 092 // Use the specified ClassLoader to resolve local proxy classes. 093 Class<?>[] resolvedInterfaces = new Class<?>[interfaces.length]; 094 for (int i = 0; i < interfaces.length; i++) { 095 try { 096 resolvedInterfaces[i] = ClassUtils.forName(interfaces[i], this.classLoader); 097 } 098 catch (ClassNotFoundException ex) { 099 resolvedInterfaces[i] = resolveFallbackIfPossible(interfaces[i], ex); 100 } 101 } 102 try { 103 return ClassUtils.createCompositeInterface(resolvedInterfaces, this.classLoader); 104 } 105 catch (IllegalArgumentException ex) { 106 throw new ClassNotFoundException(null, ex); 107 } 108 } 109 else { 110 // Use ObjectInputStream's default ClassLoader... 111 try { 112 return super.resolveProxyClass(interfaces); 113 } 114 catch (ClassNotFoundException ex) { 115 Class<?>[] resolvedInterfaces = new Class<?>[interfaces.length]; 116 for (int i = 0; i < interfaces.length; i++) { 117 resolvedInterfaces[i] = resolveFallbackIfPossible(interfaces[i], ex); 118 } 119 return ClassUtils.createCompositeInterface(resolvedInterfaces, getFallbackClassLoader()); 120 } 121 } 122 } 123 124 125 /** 126 * Resolve the given class name against a fallback class loader. 127 * <p>The default implementation simply rethrows the original exception, 128 * since there is no fallback available. 129 * @param className the class name to resolve 130 * @param ex the original exception thrown when attempting to load the class 131 * @return the newly resolved class (never {@code null}) 132 */ 133 protected Class<?> resolveFallbackIfPossible(String className, ClassNotFoundException ex) 134 throws IOException, ClassNotFoundException{ 135 136 throw ex; 137 } 138 139 /** 140 * Return the fallback ClassLoader to use when no ClassLoader was specified 141 * and ObjectInputStream's own default class loader failed. 142 * <p>The default implementation simply returns {@code null}, indicating 143 * that no specific fallback is available. 144 */ 145 protected ClassLoader getFallbackClassLoader() throws IOException { 146 return null; 147 } 148 149}