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