From d64bc93e3625b36c31df3e4ca2eea0e33aec7790 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 21 Jul 2009 12:31:03 +0000 Subject: [PATCH] updated Hessian support for Hessian 3.2 compatibility; general remoting refinements --- .../remoting/caucho/HessianExporter.java | 148 +++++++++++------- .../caucho/HessianServiceExporter.java | 4 +- .../caucho/SimpleBurlapServiceExporter.java | 4 +- .../caucho/SimpleHessianServiceExporter.java | 7 +- .../SimpleHttpInvokerServiceExporter.java | 5 +- .../remoting/caucho/CauchoRemotingTests.java | 39 ++++- .../remoting/jaxws/JaxWsSupportTests.java | 6 +- 7 files changed, 140 insertions(+), 73 deletions(-) diff --git a/org.springframework.web/src/main/java/org/springframework/remoting/caucho/HessianExporter.java b/org.springframework.web/src/main/java/org/springframework/remoting/caucho/HessianExporter.java index d198a10be0f..a3a3aeca6c6 100644 --- a/org.springframework.web/src/main/java/org/springframework/remoting/caucho/HessianExporter.java +++ b/org.springframework.web/src/main/java/org/springframework/remoting/caucho/HessianExporter.java @@ -16,6 +16,7 @@ package org.springframework.remoting.caucho; +import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -44,7 +45,7 @@ import org.springframework.util.CommonsLogWriter; *

Hessian is a slim, binary RPC protocol. * For information on Hessian, see the * Hessian website. - * This exporter requires Hessian 3.1.3 or above. + * Note: As of Spring 3.0, this exporter requires Hessian 3.2 or above. * * @author Juergen Hoeller * @since 2.5.1 @@ -114,81 +115,108 @@ public class HessianExporter extends RemoteExporter implements InitializingBean */ public void invoke(InputStream inputStream, OutputStream outputStream) throws Throwable { Assert.notNull(this.skeleton, "Hessian exporter has not been initialized"); - ClassLoader originalClassLoader = overrideThreadContextClassLoader(); - try { - doInvoke(inputStream, outputStream); - } - finally { - resetThreadContextClassLoader(originalClassLoader); - } + doInvoke(this.skeleton, inputStream, outputStream); } - public void doInvoke(final InputStream inputStream, final OutputStream outputStream) throws Throwable { - InputStream isToUse = inputStream; - OutputStream osToUse = outputStream; - - if (this.debugLogger != null && this.debugLogger.isDebugEnabled()) { - PrintWriter debugWriter = new PrintWriter(new CommonsLogWriter(this.debugLogger)); - isToUse = new HessianDebugInputStream(inputStream, debugWriter); - osToUse = new HessianDebugOutputStream(outputStream, debugWriter); - } + /** + * Actually invoke the skeleton with the given streams. + * @param skeleton the skeleton to invoke + * @param inputStream the request stream + * @param outputStream the response stream + * @throws Throwable if invocation failed + */ + protected void doInvoke(HessianSkeleton skeleton, InputStream inputStream, OutputStream outputStream) + throws Throwable { - int code = isToUse.read(); - int major; - int minor; + ClassLoader originalClassLoader = overrideThreadContextClassLoader(); + try { + InputStream isToUse = inputStream; + OutputStream osToUse = outputStream; + + if (this.debugLogger != null && this.debugLogger.isDebugEnabled()) { + PrintWriter debugWriter = new PrintWriter(new CommonsLogWriter(this.debugLogger)); + HessianDebugInputStream dis = new HessianDebugInputStream(inputStream, debugWriter); + dis.startTop2(); + HessianDebugOutputStream dos = new HessianDebugOutputStream(outputStream, debugWriter); + dos.startTop2(); + isToUse = dis; + osToUse = dos; + } - AbstractHessianInput in; - AbstractHessianOutput out; + if (!isToUse.markSupported()) { + isToUse = new BufferedInputStream(isToUse); + isToUse.mark(1); + } - if (code == 'H') { - major = isToUse.read(); - minor = isToUse.read(); - if (major != 0x02) { - throw new IOException("Version " + major + "." + minor + " is not understood"); + int code = isToUse.read(); + int major; + int minor; + + AbstractHessianInput in; + AbstractHessianOutput out; + + if (code == 'H') { + // Hessian 2.0 stream + major = isToUse.read(); + minor = isToUse.read(); + if (major != 0x02) { + throw new IOException("Version " + major + "." + minor + " is not understood"); + } + in = new Hessian2Input(isToUse); + out = new Hessian2Output(osToUse); + in.readCall(); } - in = new Hessian2Input(isToUse); - out = new Hessian2Output(osToUse); - in.readCall(); - } - else if (code == 'c') { - major = isToUse.read(); - minor = isToUse.read(); - in = new HessianInput(isToUse); - if (major >= 2) { + else if (code == 'C') { + // Hessian 2.0 call... for some reason not handled in HessianServlet! + isToUse.reset(); + in = new Hessian2Input(isToUse); out = new Hessian2Output(osToUse); + in.readCall(); + } + else if (code == 'c') { + // Hessian 1.0 call + major = isToUse.read(); + minor = isToUse.read(); + in = new HessianInput(isToUse); + if (major >= 2) { + out = new Hessian2Output(osToUse); + } + else { + out = new HessianOutput(osToUse); + } } else { - out = new HessianOutput(osToUse); + throw new IOException("Expected 'H'/'C' (Hessian 2.0) or 'c' (Hessian 1.0) in hessian input at " + code); } - } - else { - throw new IOException("Expected 'H' (Hessian 2.0) or 'c' (Hessian 1.0) in hessian input at " + code); - } - - if (this.serializerFactory != null) { - in.setSerializerFactory(this.serializerFactory); - out.setSerializerFactory(this.serializerFactory); - } - try { - this.skeleton.invoke(in, out); - } - finally { - try { - in.close(); - isToUse.close(); - } - catch (IOException ex) { - // ignore + if (this.serializerFactory != null) { + in.setSerializerFactory(this.serializerFactory); + out.setSerializerFactory(this.serializerFactory); } + try { - out.close(); - osToUse.close(); + skeleton.invoke(in, out); } - catch (IOException ex) { - // ignore + finally { + try { + in.close(); + isToUse.close(); + } + catch (IOException ex) { + // ignore + } + try { + out.close(); + osToUse.close(); + } + catch (IOException ex) { + // ignore + } } } + finally { + resetThreadContextClassLoader(originalClassLoader); + } } } diff --git a/org.springframework.web/src/main/java/org/springframework/remoting/caucho/HessianServiceExporter.java b/org.springframework.web/src/main/java/org/springframework/remoting/caucho/HessianServiceExporter.java index f47c773616f..9f83c31bec4 100644 --- a/org.springframework.web/src/main/java/org/springframework/remoting/caucho/HessianServiceExporter.java +++ b/org.springframework.web/src/main/java/org/springframework/remoting/caucho/HessianServiceExporter.java @@ -35,9 +35,9 @@ import org.springframework.web.util.NestedServletException; *

Hessian is a slim, binary RPC protocol. * For information on Hessian, see the * Hessian website. - * This exporter requires Hessian 3.1.3 or above. + * Note: As of Spring 3.0, this exporter requires Hessian 3.2 or above. * - *

Note: Hessian services exported with this class can be accessed by + *

Hessian services exported with this class can be accessed by * any Hessian client, as there isn't any special handling involved. * * @author Juergen Hoeller diff --git a/org.springframework.web/src/main/java/org/springframework/remoting/caucho/SimpleBurlapServiceExporter.java b/org.springframework.web/src/main/java/org/springframework/remoting/caucho/SimpleBurlapServiceExporter.java index e3776bb2949..aed4137c5ad 100644 --- a/org.springframework.web/src/main/java/org/springframework/remoting/caucho/SimpleBurlapServiceExporter.java +++ b/org.springframework.web/src/main/java/org/springframework/remoting/caucho/SimpleBurlapServiceExporter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -63,7 +63,7 @@ public class SimpleBurlapServiceExporter extends BurlapExporter implements HttpH } catch (Throwable ex) { exchange.sendResponseHeaders(500, -1); - throw new IOException("Burlap skeleton invocation failed", ex); + logger.error("Burlap skeleton invocation failed", ex); } exchange.sendResponseHeaders(200, output.size()); diff --git a/org.springframework.web/src/main/java/org/springframework/remoting/caucho/SimpleHessianServiceExporter.java b/org.springframework.web/src/main/java/org/springframework/remoting/caucho/SimpleHessianServiceExporter.java index 39e0aa0bb1a..d0e49e94a43 100644 --- a/org.springframework.web/src/main/java/org/springframework/remoting/caucho/SimpleHessianServiceExporter.java +++ b/org.springframework.web/src/main/java/org/springframework/remoting/caucho/SimpleHessianServiceExporter.java @@ -33,9 +33,9 @@ import org.springframework.util.FileCopyUtils; *

Hessian is a slim, binary RPC protocol. * For information on Hessian, see the * Hessian website. - * This exporter requires Hessian 3.1.3 or above. + * Note: As of Spring 3.0, this exporter requires Hessian 3.2 or above. * - *

Note: Hessian services exported with this class can be accessed by + *

Hessian services exported with this class can be accessed by * any Hessian client, as there isn't any special handling involved. * * @author Juergen Hoeller @@ -63,7 +63,8 @@ public class SimpleHessianServiceExporter extends HessianExporter implements Htt } catch (Throwable ex) { exchange.sendResponseHeaders(500, -1); - throw new IOException("Hessian skeleton invocation failed", ex); + logger.error("Hessian skeleton invocation failed", ex); + return; } exchange.getResponseHeaders().set("Content-Type", CONTENT_TYPE_HESSIAN); diff --git a/org.springframework.web/src/main/java/org/springframework/remoting/httpinvoker/SimpleHttpInvokerServiceExporter.java b/org.springframework.web/src/main/java/org/springframework/remoting/httpinvoker/SimpleHttpInvokerServiceExporter.java index dd18c2d164d..084a4e6596f 100644 --- a/org.springframework.web/src/main/java/org/springframework/remoting/httpinvoker/SimpleHttpInvokerServiceExporter.java +++ b/org.springframework.web/src/main/java/org/springframework/remoting/httpinvoker/SimpleHttpInvokerServiceExporter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -69,7 +69,8 @@ public class SimpleHttpInvokerServiceExporter extends RemoteInvocationSerializin exchange.close(); } catch (ClassNotFoundException ex) { - throw new IOException("Class not found during deserialization", ex); + exchange.sendResponseHeaders(500, -1); + logger.error("Class not found during deserialization", ex); } } diff --git a/org.springframework.web/src/test/java/org/springframework/remoting/caucho/CauchoRemotingTests.java b/org.springframework.web/src/test/java/org/springframework/remoting/caucho/CauchoRemotingTests.java index 66016a9fc8b..41de7614f7a 100644 --- a/org.springframework.web/src/test/java/org/springframework/remoting/caucho/CauchoRemotingTests.java +++ b/org.springframework.web/src/test/java/org/springframework/remoting/caucho/CauchoRemotingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,19 @@ package org.springframework.remoting.caucho; +import java.io.IOException; +import java.net.InetSocketAddress; + import com.caucho.burlap.client.BurlapProxyFactory; import com.caucho.hessian.client.HessianProxyFactory; +import com.sun.net.httpserver.HttpServer; import junit.framework.TestCase; +import org.junit.Ignore; +import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.ITestBean; import org.springframework.beans.TestBean; +import org.springframework.core.JdkVersion; import org.springframework.remoting.RemoteAccessException; /** @@ -181,6 +188,36 @@ public class CauchoRemotingTests extends TestCase { } } + @Ignore("Using the JDK 1.6 HttpServer breaks when running multiple test methods") + public void testSimpleHessianServiceExporter() throws IOException { + if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_16) { + return; + } + TestBean tb = new TestBean("tb"); + SimpleHessianServiceExporter exporter = new SimpleHessianServiceExporter(); + exporter.setService(tb); + exporter.setServiceInterface(ITestBean.class); + exporter.setDebug(true); + exporter.prepare(); + HttpServer server = HttpServer.create(new InetSocketAddress(8889), -1); + server.createContext("/hessian", exporter); + server.start(); + try { + HessianClientInterceptor client = new HessianClientInterceptor(); + client.setServiceUrl("http://localhost:8889/hessian"); + client.setServiceInterface(ITestBean.class); + //client.setHessian2(true); + client.prepare(); + ITestBean proxy = ProxyFactory.getProxy(ITestBean.class, client); + assertEquals("tb", proxy.getName()); + proxy.setName("test"); + assertEquals("test", proxy.getName()); + } + finally { + server.stop(Integer.MAX_VALUE); + } + } + private static class TestHessianProxyFactory extends HessianProxyFactory { diff --git a/org.springframework.web/src/test/java/org/springframework/remoting/jaxws/JaxWsSupportTests.java b/org.springframework.web/src/test/java/org/springframework/remoting/jaxws/JaxWsSupportTests.java index a15d7c314f6..9d71d62b846 100644 --- a/org.springframework.web/src/test/java/org/springframework/remoting/jaxws/JaxWsSupportTests.java +++ b/org.springframework.web/src/test/java/org/springframework/remoting/jaxws/JaxWsSupportTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -75,7 +75,7 @@ public class JaxWsSupportTests extends TestCase { try { ac.refresh(); - OrderService orderService = (OrderService) ac.getBean("client", OrderService.class); + OrderService orderService = ac.getBean("client", OrderService.class); assertTrue(orderService instanceof BindingProvider); ((BindingProvider) orderService).getRequestContext(); @@ -89,7 +89,7 @@ public class JaxWsSupportTests extends TestCase { // expected } - ServiceAccessor serviceAccessor = (ServiceAccessor) ac.getBean("accessor", ServiceAccessor.class); + ServiceAccessor serviceAccessor = ac.getBean("accessor", ServiceAccessor.class); order = serviceAccessor.orderService.getOrder(1000); assertEquals("order 1000", order); try {