001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.remoting.httpinvoker;
018:
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.io.ObjectInputStream;
022: import java.io.ObjectOutputStream;
023: import java.io.OutputStream;
024: import java.rmi.RemoteException;
025:
026: import javax.servlet.ServletException;
027: import javax.servlet.http.HttpServletRequest;
028: import javax.servlet.http.HttpServletResponse;
029:
030: import org.springframework.beans.factory.InitializingBean;
031: import org.springframework.remoting.rmi.CodebaseAwareObjectInputStream;
032: import org.springframework.remoting.support.RemoteInvocation;
033: import org.springframework.remoting.support.RemoteInvocationBasedExporter;
034: import org.springframework.remoting.support.RemoteInvocationResult;
035: import org.springframework.util.Assert;
036: import org.springframework.web.HttpRequestHandler;
037: import org.springframework.web.util.NestedServletException;
038:
039: /**
040: * HTTP request handler that exports the specified service bean as HTTP invoker
041: * service endpoint, accessible via an HTTP invoker proxy.
042: *
043: * <p>Deserializes remote invocation objects and serializes remote invocation
044: * result objects. Uses Java serialization just like RMI, but provides the
045: * same ease of setup as Caucho's HTTP-based Hessian and Burlap protocols.
046: *
047: * <p><b>HTTP invoker is the recommended protocol for Java-to-Java remoting.</b>
048: * It is more powerful and more extensible than Hessian and Burlap, at the
049: * expense of being tied to Java. Nevertheless, it is as easy to set up as
050: * Hessian and Burlap, which is its main advantage compared to RMI.
051: *
052: * @author Juergen Hoeller
053: * @since 1.1
054: * @see HttpInvokerClientInterceptor
055: * @see HttpInvokerProxyFactoryBean
056: * @see org.springframework.remoting.rmi.RmiServiceExporter
057: * @see org.springframework.remoting.caucho.HessianServiceExporter
058: * @see org.springframework.remoting.caucho.BurlapServiceExporter
059: */
060: public class HttpInvokerServiceExporter extends
061: RemoteInvocationBasedExporter implements HttpRequestHandler,
062: InitializingBean {
063:
064: /**
065: * Default content type: "application/x-java-serialized-object"
066: */
067: public static final String CONTENT_TYPE_SERIALIZED_OBJECT = "application/x-java-serialized-object";
068:
069: private String contentType = CONTENT_TYPE_SERIALIZED_OBJECT;
070:
071: private Object proxy;
072:
073: /**
074: * Specify the content type to use for sending HTTP invoker responses.
075: * <p>Default is "application/x-java-serialized-object".
076: */
077: public void setContentType(String contentType) {
078: Assert.notNull(contentType, "'contentType' must not be null");
079: this .contentType = contentType;
080: }
081:
082: /**
083: * Return the content type to use for sending HTTP invoker responses.
084: */
085: public String getContentType() {
086: return this .contentType;
087: }
088:
089: public void afterPropertiesSet() {
090: prepare();
091: }
092:
093: /**
094: * Initialize this service exporter.
095: */
096: public void prepare() {
097: this .proxy = getProxyForService();
098: }
099:
100: /**
101: * Reads a remote invocation from the request, executes it,
102: * and writes the remote invocation result to the response.
103: * @see #readRemoteInvocation(HttpServletRequest)
104: * @see #invokeAndCreateResult(org.springframework.remoting.support.RemoteInvocation, Object)
105: * @see #writeRemoteInvocationResult(HttpServletRequest, HttpServletResponse, RemoteInvocationResult)
106: */
107: public void handleRequest(HttpServletRequest request,
108: HttpServletResponse response) throws ServletException,
109: IOException {
110:
111: Assert.notNull(this .proxy,
112: "HttpInvokerServiceExporter has not been initialized");
113:
114: try {
115: RemoteInvocation invocation = readRemoteInvocation(request);
116: RemoteInvocationResult result = invokeAndCreateResult(
117: invocation, this .proxy);
118: writeRemoteInvocationResult(request, response, result);
119: } catch (ClassNotFoundException ex) {
120: throw new NestedServletException(
121: "Class not found during deserialization", ex);
122: }
123: }
124:
125: /**
126: * Read a RemoteInvocation from the given HTTP request.
127: * <p>Delegates to
128: * {@link #readRemoteInvocation(javax.servlet.http.HttpServletRequest, java.io.InputStream)}
129: * with the
130: * {@link javax.servlet.ServletRequest#getInputStream() servlet request's input stream}.
131: * @param request current HTTP request
132: * @return the RemoteInvocation object
133: * @throws IOException in case of I/O failure
134: * @throws ClassNotFoundException if thrown by deserialization
135: */
136: protected RemoteInvocation readRemoteInvocation(
137: HttpServletRequest request) throws IOException,
138: ClassNotFoundException {
139:
140: return readRemoteInvocation(request, request.getInputStream());
141: }
142:
143: /**
144: * Deserialize a RemoteInvocation object from the given InputStream.
145: * <p>Gives {@link #decorateInputStream} a chance to decorate the stream
146: * first (for example, for custom encryption or compression). Creates a
147: * {@link org.springframework.remoting.rmi.CodebaseAwareObjectInputStream}
148: * and calls {@link #doReadRemoteInvocation} to actually read the object.
149: * <p>Can be overridden for custom serialization of the invocation.
150: * @param request current HTTP request
151: * @param is the InputStream to read from
152: * @return the RemoteInvocation object
153: * @throws IOException in case of I/O failure
154: * @throws ClassNotFoundException if thrown during deserialization
155: */
156: protected RemoteInvocation readRemoteInvocation(
157: HttpServletRequest request, InputStream is)
158: throws IOException, ClassNotFoundException {
159:
160: ObjectInputStream ois = createObjectInputStream(decorateInputStream(
161: request, is));
162: try {
163: return doReadRemoteInvocation(ois);
164: } finally {
165: ois.close();
166: }
167: }
168:
169: /**
170: * Return the InputStream to use for reading remote invocations,
171: * potentially decorating the given original InputStream.
172: * <p>The default implementation returns the given stream as-is.
173: * Can be overridden, for example, for custom encryption or compression.
174: * @param request current HTTP request
175: * @param is the original InputStream
176: * @return the potentially decorated InputStream
177: * @throws IOException in case of I/O failure
178: */
179: protected InputStream decorateInputStream(
180: HttpServletRequest request, InputStream is)
181: throws IOException {
182: return is;
183: }
184:
185: /**
186: * Create an ObjectInputStream for the given InputStream.
187: * <p>The default implementation creates a Spring
188: * {@link org.springframework.remoting.rmi.CodebaseAwareObjectInputStream}.
189: * @param is the InputStream to read from
190: * @return the new ObjectInputStream instance to use
191: * @throws IOException if creation of the ObjectInputStream failed
192: */
193: protected ObjectInputStream createObjectInputStream(InputStream is)
194: throws IOException {
195: return new CodebaseAwareObjectInputStream(is, null);
196: }
197:
198: /**
199: * Perform the actual reading of an invocation result object from the
200: * given ObjectInputStream.
201: * <p>The default implementation simply calls
202: * {@link java.io.ObjectInputStream#readObject()}.
203: * Can be overridden for deserialization of a custom wrapper object rather
204: * than the plain invocation, for example an encryption-aware holder.
205: * @param ois the ObjectInputStream to read from
206: * @return the RemoteInvocationResult object
207: * @throws IOException in case of I/O failure
208: * @throws ClassNotFoundException if case of a transferred class not
209: * being found in the local ClassLoader
210: */
211: protected RemoteInvocation doReadRemoteInvocation(
212: ObjectInputStream ois) throws IOException,
213: ClassNotFoundException {
214:
215: Object obj = ois.readObject();
216: if (!(obj instanceof RemoteInvocation)) {
217: throw new RemoteException(
218: "Deserialized object needs to be assignable to type ["
219: + RemoteInvocation.class.getName() + "]: "
220: + obj);
221: }
222: return (RemoteInvocation) obj;
223: }
224:
225: /**
226: * Write the given RemoteInvocationResult to the given HTTP response.
227: * @param request current HTTP request
228: * @param response current HTTP response
229: * @param result the RemoteInvocationResult object
230: * @throws IOException in case of I/O failure
231: */
232: protected void writeRemoteInvocationResult(
233: HttpServletRequest request, HttpServletResponse response,
234: RemoteInvocationResult result) throws IOException {
235:
236: response.setContentType(getContentType());
237: writeRemoteInvocationResult(request, response, result, response
238: .getOutputStream());
239: }
240:
241: /**
242: * Serialize the given RemoteInvocation to the given OutputStream.
243: * <p>The default implementation gives {@link #decorateOutputStream} a chance
244: * to decorate the stream first (for example, for custom encryption or compression).
245: * Creates an {@link java.io.ObjectOutputStream} for the final stream and calls
246: * {@link #doWriteRemoteInvocationResult} to actually write the object.
247: * <p>Can be overridden for custom serialization of the invocation.
248: * @param request current HTTP request
249: * @param response current HTTP response
250: * @param result the RemoteInvocationResult object
251: * @param os the OutputStream to write to
252: * @throws IOException in case of I/O failure
253: * @see #decorateOutputStream
254: * @see #doWriteRemoteInvocationResult
255: */
256: protected void writeRemoteInvocationResult(
257: HttpServletRequest request, HttpServletResponse response,
258: RemoteInvocationResult result, OutputStream os)
259: throws IOException {
260:
261: ObjectOutputStream oos = createObjectOutputStream(decorateOutputStream(
262: request, response, os));
263: try {
264: doWriteRemoteInvocationResult(result, oos);
265: oos.flush();
266: } finally {
267: oos.close();
268: }
269: }
270:
271: /**
272: * Return the OutputStream to use for writing remote invocation results,
273: * potentially decorating the given original OutputStream.
274: * <p>The default implementation returns the given stream as-is.
275: * Can be overridden, for example, for custom encryption or compression.
276: * @param request current HTTP request
277: * @param response current HTTP response
278: * @param os the original OutputStream
279: * @return the potentially decorated OutputStream
280: * @throws IOException in case of I/O failure
281: */
282: protected OutputStream decorateOutputStream(
283: HttpServletRequest request, HttpServletResponse response,
284: OutputStream os) throws IOException {
285:
286: return os;
287: }
288:
289: /**
290: * Create an ObjectOutputStream for the given OutputStream.
291: * <p>The default implementation creates a plain
292: * {@link java.io.ObjectOutputStream}.
293: * @param os the OutputStream to write to
294: * @return the new ObjectOutputStream instance to use
295: * @throws IOException if creation of the ObjectOutputStream failed
296: */
297: protected ObjectOutputStream createObjectOutputStream(
298: OutputStream os) throws IOException {
299: return new ObjectOutputStream(os);
300: }
301:
302: /**
303: * Perform the actual writing of the given invocation result object
304: * to the given ObjectOutputStream.
305: * <p>The default implementation simply calls
306: * {@link java.io.ObjectOutputStream#writeObject}.
307: * Can be overridden for serialization of a custom wrapper object rather
308: * than the plain invocation, for example an encryption-aware holder.
309: * @param result the RemoteInvocationResult object
310: * @param oos the ObjectOutputStream to write to
311: * @throws IOException if thrown by I/O methods
312: */
313: protected void doWriteRemoteInvocationResult(
314: RemoteInvocationResult result, ObjectOutputStream oos)
315: throws IOException {
316:
317: oos.writeObject(result);
318: }
319:
320: }
|