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.lang.Nullable; 026import org.springframework.util.ClassUtils; 027 028/** 029 * Special ObjectInputStream subclass that resolves class names 030 * against a specific ClassLoader. Serves as base class for 031 * {@link org.springframework.remoting.rmi.CodebaseAwareObjectInputStream}. 032 * 033 * @author Juergen Hoeller 034 * @since 2.5.5 035 */ 036public class ConfigurableObjectInputStream extends ObjectInputStream { 037 038 @Nullable 039 private final ClassLoader classLoader; 040 041 private final boolean acceptProxyClasses; 042 043 044 /** 045 * Create a new ConfigurableObjectInputStream for the given InputStream and ClassLoader. 046 * @param in the InputStream to read from 047 * @param classLoader the ClassLoader to use for loading local classes 048 * @see java.io.ObjectInputStream#ObjectInputStream(java.io.InputStream) 049 */ 050 public ConfigurableObjectInputStream(InputStream in, @Nullable ClassLoader classLoader) throws IOException { 051 this(in, classLoader, true); 052 } 053 054 /** 055 * Create a new ConfigurableObjectInputStream for the given InputStream and ClassLoader. 056 * @param in the InputStream to read from 057 * @param classLoader the ClassLoader to use for loading local classes 058 * @param acceptProxyClasses whether to accept deserialization of proxy classes 059 * (may be deactivated as a security measure) 060 * @see java.io.ObjectInputStream#ObjectInputStream(java.io.InputStream) 061 */ 062 public ConfigurableObjectInputStream( 063 InputStream in, @Nullable ClassLoader classLoader, boolean acceptProxyClasses) throws IOException { 064 065 super(in); 066 this.classLoader = classLoader; 067 this.acceptProxyClasses = acceptProxyClasses; 068 } 069 070 071 @Override 072 protected Class<?> resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException { 073 try { 074 if (this.classLoader != null) { 075 // Use the specified ClassLoader to resolve local classes. 076 return ClassUtils.forName(classDesc.getName(), this.classLoader); 077 } 078 else { 079 // Use the default ClassLoader... 080 return super.resolveClass(classDesc); 081 } 082 } 083 catch (ClassNotFoundException ex) { 084 return resolveFallbackIfPossible(classDesc.getName(), ex); 085 } 086 } 087 088 @Override 089 protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException { 090 if (!this.acceptProxyClasses) { 091 throw new NotSerializableException("Not allowed to accept serialized proxy classes"); 092 } 093 if (this.classLoader != null) { 094 // Use the specified ClassLoader to resolve local proxy classes. 095 Class<?>[] resolvedInterfaces = new Class<?>[interfaces.length]; 096 for (int i = 0; i < interfaces.length; i++) { 097 try { 098 resolvedInterfaces[i] = ClassUtils.forName(interfaces[i], this.classLoader); 099 } 100 catch (ClassNotFoundException ex) { 101 resolvedInterfaces[i] = resolveFallbackIfPossible(interfaces[i], ex); 102 } 103 } 104 try { 105 return ClassUtils.createCompositeInterface(resolvedInterfaces, this.classLoader); 106 } 107 catch (IllegalArgumentException ex) { 108 throw new ClassNotFoundException(null, ex); 109 } 110 } 111 else { 112 // Use ObjectInputStream's default ClassLoader... 113 try { 114 return super.resolveProxyClass(interfaces); 115 } 116 catch (ClassNotFoundException ex) { 117 Class<?>[] resolvedInterfaces = new Class<?>[interfaces.length]; 118 for (int i = 0; i < interfaces.length; i++) { 119 resolvedInterfaces[i] = resolveFallbackIfPossible(interfaces[i], ex); 120 } 121 return ClassUtils.createCompositeInterface(resolvedInterfaces, getFallbackClassLoader()); 122 } 123 } 124 } 125 126 127 /** 128 * Resolve the given class name against a fallback class loader. 129 * <p>The default implementation simply rethrows the original exception, 130 * since there is no fallback available. 131 * @param className the class name to resolve 132 * @param ex the original exception thrown when attempting to load the class 133 * @return the newly resolved class (never {@code null}) 134 */ 135 protected Class<?> resolveFallbackIfPossible(String className, ClassNotFoundException ex) 136 throws IOException, ClassNotFoundException{ 137 138 throw ex; 139 } 140 141 /** 142 * Return the fallback ClassLoader to use when no ClassLoader was specified 143 * and ObjectInputStream's own default class loader failed. 144 * <p>The default implementation simply returns {@code null}, indicating 145 * that no specific fallback is available. 146 */ 147 @Nullable 148 protected ClassLoader getFallbackClassLoader() throws IOException { 149 return null; 150 } 151 152}