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}