001/* 002 * Copyright 2002-2012 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.remoting.rmi; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.rmi.server.RMIClassLoader; 022 023import org.springframework.core.ConfigurableObjectInputStream; 024 025/** 026 * Special ObjectInputStream subclass that falls back to a specified codebase 027 * to load classes from if not found locally. In contrast to standard RMI 028 * conventions for dynamic class download, it is the client that determines 029 * the codebase URL here, rather than the "java.rmi.server.codebase" system 030 * property on the server. 031 * 032 * <p>Uses the JDK's RMIClassLoader to load classes from the specified codebase. 033 * The codebase can consist of multiple URLs, separated by spaces. 034 * Note that RMIClassLoader requires a SecurityManager to be set, like when 035 * using dynamic class download with standard RMI! (See the RMI documentation 036 * for details.) 037 * 038 * <p>Despite residing in the RMI package, this class is <i>not</i> used for 039 * RmiClientInterceptor, which uses the standard RMI infrastructure instead 040 * and thus is only able to rely on RMI's standard dynamic class download via 041 * "java.rmi.server.codebase". CodebaseAwareObjectInputStream is used by 042 * HttpInvokerClientInterceptor (see the "codebaseUrl" property there). 043 * 044 * <p>Thanks to Lionel Mestre for suggesting the option and providing 045 * a prototype! 046 * 047 * @author Juergen Hoeller 048 * @since 1.1.3 049 * @see java.rmi.server.RMIClassLoader 050 * @see RemoteInvocationSerializingExporter#createObjectInputStream 051 * @see org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor#setCodebaseUrl 052 */ 053public class CodebaseAwareObjectInputStream extends ConfigurableObjectInputStream { 054 055 private final String codebaseUrl; 056 057 058 /** 059 * Create a new CodebaseAwareObjectInputStream for the given InputStream and codebase. 060 * @param in the InputStream to read from 061 * @param codebaseUrl the codebase URL to load classes from if not found locally 062 * (can consist of multiple URLs, separated by spaces) 063 * @see java.io.ObjectInputStream#ObjectInputStream(java.io.InputStream) 064 */ 065 public CodebaseAwareObjectInputStream(InputStream in, String codebaseUrl) throws IOException { 066 this(in, null, codebaseUrl); 067 } 068 069 /** 070 * Create a new CodebaseAwareObjectInputStream for the given InputStream and codebase. 071 * @param in the InputStream to read from 072 * @param classLoader the ClassLoader to use for loading local classes 073 * (may be {@code null} to indicate RMI's default ClassLoader) 074 * @param codebaseUrl the codebase URL to load classes from if not found locally 075 * (can consist of multiple URLs, separated by spaces) 076 * @see java.io.ObjectInputStream#ObjectInputStream(java.io.InputStream) 077 */ 078 public CodebaseAwareObjectInputStream( 079 InputStream in, ClassLoader classLoader, String codebaseUrl) throws IOException { 080 081 super(in, classLoader); 082 this.codebaseUrl = codebaseUrl; 083 } 084 085 /** 086 * Create a new CodebaseAwareObjectInputStream for the given InputStream and codebase. 087 * @param in the InputStream to read from 088 * @param classLoader the ClassLoader to use for loading local classes 089 * (may be {@code null} to indicate RMI's default ClassLoader) 090 * @param acceptProxyClasses whether to accept deserialization of proxy classes 091 * (may be deactivated as a security measure) 092 * @see java.io.ObjectInputStream#ObjectInputStream(java.io.InputStream) 093 */ 094 public CodebaseAwareObjectInputStream( 095 InputStream in, ClassLoader classLoader, boolean acceptProxyClasses) throws IOException { 096 097 super(in, classLoader, acceptProxyClasses); 098 this.codebaseUrl = null; 099 } 100 101 102 @Override 103 protected Class<?> resolveFallbackIfPossible(String className, ClassNotFoundException ex) 104 throws IOException, ClassNotFoundException { 105 106 // If codebaseUrl is set, try to load the class with the RMIClassLoader. 107 // Else, propagate the ClassNotFoundException. 108 if (this.codebaseUrl == null) { 109 throw ex; 110 } 111 return RMIClassLoader.loadClass(this.codebaseUrl, className); 112 } 113 114 @Override 115 protected ClassLoader getFallbackClassLoader() throws IOException { 116 return RMIClassLoader.getClassLoader(this.codebaseUrl); 117 } 118 119}