001/* 002 * Copyright 2002-2018 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.io.ObjectInputStream; 022import java.io.ObjectOutputStream; 023import java.io.OutputStream; 024import java.rmi.RemoteException; 025 026import org.springframework.beans.factory.InitializingBean; 027import org.springframework.remoting.support.RemoteInvocation; 028import org.springframework.remoting.support.RemoteInvocationBasedExporter; 029import org.springframework.remoting.support.RemoteInvocationResult; 030import org.springframework.util.Assert; 031import org.springframework.util.ClassUtils; 032 033/** 034 * Abstract base class for remote service exporters that explicitly deserialize 035 * {@link org.springframework.remoting.support.RemoteInvocation} objects and serialize 036 * {@link org.springframework.remoting.support.RemoteInvocationResult} objects, 037 * for example Spring's HTTP invoker. 038 * 039 * <p>Provides template methods for {@code ObjectInputStream} and 040 * {@code ObjectOutputStream} handling. 041 * 042 * @author Juergen Hoeller 043 * @since 2.5.1 044 * @see java.io.ObjectInputStream 045 * @see java.io.ObjectOutputStream 046 * @see #doReadRemoteInvocation 047 * @see #doWriteRemoteInvocationResult 048 */ 049public abstract class RemoteInvocationSerializingExporter extends RemoteInvocationBasedExporter 050 implements InitializingBean { 051 052 /** 053 * Default content type: "application/x-java-serialized-object". 054 */ 055 public static final String CONTENT_TYPE_SERIALIZED_OBJECT = "application/x-java-serialized-object"; 056 057 058 private String contentType = CONTENT_TYPE_SERIALIZED_OBJECT; 059 060 private boolean acceptProxyClasses = true; 061 062 private Object proxy; 063 064 065 /** 066 * Specify the content type to use for sending remote invocation responses. 067 * <p>Default is "application/x-java-serialized-object". 068 */ 069 public void setContentType(String contentType) { 070 Assert.notNull(contentType, "'contentType' must not be null"); 071 this.contentType = contentType; 072 } 073 074 /** 075 * Return the content type to use for sending remote invocation responses. 076 */ 077 public String getContentType() { 078 return this.contentType; 079 } 080 081 /** 082 * Set whether to accept deserialization of proxy classes. 083 * <p>Default is "true". May be deactivated as a security measure. 084 */ 085 public void setAcceptProxyClasses(boolean acceptProxyClasses) { 086 this.acceptProxyClasses = acceptProxyClasses; 087 } 088 089 /** 090 * Return whether to accept deserialization of proxy classes. 091 */ 092 public boolean isAcceptProxyClasses() { 093 return this.acceptProxyClasses; 094 } 095 096 097 @Override 098 public void afterPropertiesSet() { 099 prepare(); 100 } 101 102 /** 103 * Initialize this service exporter. 104 */ 105 public void prepare() { 106 this.proxy = getProxyForService(); 107 } 108 109 protected final Object getProxy() { 110 if (this.proxy == null) { 111 throw new IllegalStateException(ClassUtils.getShortName(getClass()) + " has not been initialized"); 112 } 113 return this.proxy; 114 } 115 116 117 /** 118 * Create an ObjectInputStream for the given InputStream. 119 * <p>The default implementation creates a Spring {@link CodebaseAwareObjectInputStream}. 120 * @param is the InputStream to read from 121 * @return the new ObjectInputStream instance to use 122 * @throws java.io.IOException if creation of the ObjectInputStream failed 123 */ 124 protected ObjectInputStream createObjectInputStream(InputStream is) throws IOException { 125 return new CodebaseAwareObjectInputStream(is, getBeanClassLoader(), isAcceptProxyClasses()); 126 } 127 128 /** 129 * Perform the actual reading of an invocation result object from the 130 * given ObjectInputStream. 131 * <p>The default implementation simply calls 132 * {@link java.io.ObjectInputStream#readObject()}. 133 * Can be overridden for deserialization of a custom wrapper object rather 134 * than the plain invocation, for example an encryption-aware holder. 135 * @param ois the ObjectInputStream to read from 136 * @return the RemoteInvocationResult object 137 * @throws java.io.IOException in case of I/O failure 138 * @throws ClassNotFoundException if case of a transferred class not 139 * being found in the local ClassLoader 140 */ 141 protected RemoteInvocation doReadRemoteInvocation(ObjectInputStream ois) 142 throws IOException, ClassNotFoundException { 143 144 Object obj = ois.readObject(); 145 if (!(obj instanceof RemoteInvocation)) { 146 throw new RemoteException("Deserialized object needs to be assignable to type [" + 147 RemoteInvocation.class.getName() + "]: " + ClassUtils.getDescriptiveType(obj)); 148 } 149 return (RemoteInvocation) obj; 150 } 151 152 /** 153 * Create an ObjectOutputStream for the given OutputStream. 154 * <p>The default implementation creates a plain 155 * {@link java.io.ObjectOutputStream}. 156 * @param os the OutputStream to write to 157 * @return the new ObjectOutputStream instance to use 158 * @throws java.io.IOException if creation of the ObjectOutputStream failed 159 */ 160 protected ObjectOutputStream createObjectOutputStream(OutputStream os) throws IOException { 161 return new ObjectOutputStream(os); 162 } 163 164 /** 165 * Perform the actual writing of the given invocation result object 166 * to the given ObjectOutputStream. 167 * <p>The default implementation simply calls 168 * {@link java.io.ObjectOutputStream#writeObject}. 169 * Can be overridden for serialization of a custom wrapper object rather 170 * than the plain invocation, for example an encryption-aware holder. 171 * @param result the RemoteInvocationResult object 172 * @param oos the ObjectOutputStream to write to 173 * @throws java.io.IOException if thrown by I/O methods 174 */ 175 protected void doWriteRemoteInvocationResult(RemoteInvocationResult result, ObjectOutputStream oos) 176 throws IOException { 177 178 oos.writeObject(result); 179 } 180 181}