1 /* 2 * Copyright 2002-2009 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.remoting.httpinvoker; 18 19 import java.io.ByteArrayInputStream; 20 import java.io.ByteArrayOutputStream; 21 import java.io.IOException; 22 import java.io.InputStream; 23 import java.io.ObjectInputStream; 24 import java.io.ObjectOutputStream; 25 import java.io.OutputStream; 26 import java.io.Serializable; 27 import java.lang.reflect.InvocationTargetException; 28 import java.util.Arrays; 29 import java.util.zip.GZIPInputStream; 30 import java.util.zip.GZIPOutputStream; 31 import javax.servlet.ServletException; 32 import javax.servlet.http.HttpServletRequest; 33 import javax.servlet.http.HttpServletResponse; 34 35 import junit.framework.TestCase; 36 import org.aopalliance.intercept.MethodInvocation; 37 38 import org.springframework.beans.ITestBean; 39 import org.springframework.beans.TestBean; 40 import org.springframework.beans.factory.BeanClassLoaderAware; 41 import org.springframework.mock.web.MockHttpServletRequest; 42 import org.springframework.mock.web.MockHttpServletResponse; 43 import org.springframework.remoting.RemoteAccessException; 44 import org.springframework.remoting.support.DefaultRemoteInvocationExecutor; 45 import org.springframework.remoting.support.RemoteInvocation; 46 import org.springframework.remoting.support.RemoteInvocationFactory; 47 import org.springframework.remoting.support.RemoteInvocationResult; 48 49 /** 50 * @author Juergen Hoeller 51 * @since 09.08.2004 52 */ 53 public class HttpInvokerTests extends TestCase { 54 testHttpInvokerProxyFactoryBeanAndServiceExporter()55 public void testHttpInvokerProxyFactoryBeanAndServiceExporter() throws Throwable { 56 doTestHttpInvokerProxyFactoryBeanAndServiceExporter(false); 57 } 58 testHttpInvokerProxyFactoryBeanAndServiceExporterWithExplicitClassLoader()59 public void testHttpInvokerProxyFactoryBeanAndServiceExporterWithExplicitClassLoader() throws Throwable { 60 doTestHttpInvokerProxyFactoryBeanAndServiceExporter(true); 61 } 62 doTestHttpInvokerProxyFactoryBeanAndServiceExporter(boolean explicitClassLoader)63 private void doTestHttpInvokerProxyFactoryBeanAndServiceExporter(boolean explicitClassLoader) throws Throwable { 64 TestBean target = new TestBean("myname", 99); 65 66 final HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter(); 67 exporter.setServiceInterface(ITestBean.class); 68 exporter.setService(target); 69 exporter.afterPropertiesSet(); 70 71 HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean(); 72 pfb.setServiceInterface(ITestBean.class); 73 pfb.setServiceUrl("http://myurl"); 74 75 pfb.setHttpInvokerRequestExecutor(new AbstractHttpInvokerRequestExecutor() { 76 protected RemoteInvocationResult doExecuteRequest( 77 HttpInvokerClientConfiguration config, ByteArrayOutputStream baos) throws Exception { 78 assertEquals("http://myurl", config.getServiceUrl()); 79 MockHttpServletRequest request = new MockHttpServletRequest(); 80 MockHttpServletResponse response = new MockHttpServletResponse(); 81 request.setContent(baos.toByteArray()); 82 exporter.handleRequest(request, response); 83 return readRemoteInvocationResult( 84 new ByteArrayInputStream(response.getContentAsByteArray()), config.getCodebaseUrl()); 85 } 86 }); 87 if (explicitClassLoader) { 88 ((BeanClassLoaderAware) pfb.getHttpInvokerRequestExecutor()).setBeanClassLoader(getClass().getClassLoader()); 89 } 90 91 pfb.afterPropertiesSet(); 92 ITestBean proxy = (ITestBean) pfb.getObject(); 93 assertEquals("myname", proxy.getName()); 94 assertEquals(99, proxy.getAge()); 95 proxy.setAge(50); 96 assertEquals(50, proxy.getAge()); 97 proxy.setStringArray(new String[] {"str1", "str2"}); 98 assertTrue(Arrays.equals(new String[] {"str1", "str2"}, proxy.getStringArray())); 99 proxy.setSomeIntegerArray(new Integer[] {1, 2, 3}); 100 assertTrue(Arrays.equals(new Integer[] {1, 2, 3}, proxy.getSomeIntegerArray())); 101 proxy.setNestedIntegerArray(new Integer[][] {{1, 2, 3}, {4, 5, 6}}); 102 Integer[][] integerArray = proxy.getNestedIntegerArray(); 103 assertTrue(Arrays.equals(new Integer[] {1, 2, 3}, integerArray[0])); 104 assertTrue(Arrays.equals(new Integer[] {4, 5, 6}, integerArray[1])); 105 proxy.setSomeIntArray(new int[] {1, 2, 3}); 106 assertTrue(Arrays.equals(new int[] {1, 2, 3}, proxy.getSomeIntArray())); 107 proxy.setNestedIntArray(new int[][] {{1, 2, 3}, {4, 5, 6}}); 108 int[][] intArray = proxy.getNestedIntArray(); 109 assertTrue(Arrays.equals(new int[] {1, 2, 3}, intArray[0])); 110 assertTrue(Arrays.equals(new int[] {4, 5, 6}, intArray[1])); 111 112 try { 113 proxy.exceptional(new IllegalStateException()); 114 fail("Should have thrown IllegalStateException"); 115 } 116 catch (IllegalStateException ex) { 117 // expected 118 } 119 try { 120 proxy.exceptional(new IllegalAccessException()); 121 fail("Should have thrown IllegalAccessException"); 122 } 123 catch (IllegalAccessException ex) { 124 // expected 125 } 126 } 127 testHttpInvokerProxyFactoryBeanAndServiceExporterWithIOException()128 public void testHttpInvokerProxyFactoryBeanAndServiceExporterWithIOException() throws Exception { 129 TestBean target = new TestBean("myname", 99); 130 131 final HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter(); 132 exporter.setServiceInterface(ITestBean.class); 133 exporter.setService(target); 134 exporter.afterPropertiesSet(); 135 136 HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean(); 137 pfb.setServiceInterface(ITestBean.class); 138 pfb.setServiceUrl("http://myurl"); 139 140 pfb.setHttpInvokerRequestExecutor(new HttpInvokerRequestExecutor() { 141 public RemoteInvocationResult executeRequest( 142 HttpInvokerClientConfiguration config, RemoteInvocation invocation) throws IOException { 143 throw new IOException("argh"); 144 } 145 }); 146 147 pfb.afterPropertiesSet(); 148 ITestBean proxy = (ITestBean) pfb.getObject(); 149 try { 150 proxy.setAge(50); 151 fail("Should have thrown RemoteAccessException"); 152 } 153 catch (RemoteAccessException ex) { 154 // expected 155 assertTrue(ex.getCause() instanceof IOException); 156 } 157 } 158 testHttpInvokerProxyFactoryBeanAndServiceExporterWithGzipCompression()159 public void testHttpInvokerProxyFactoryBeanAndServiceExporterWithGzipCompression() throws Throwable { 160 TestBean target = new TestBean("myname", 99); 161 162 final HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter() { 163 protected InputStream decorateInputStream(HttpServletRequest request, InputStream is) throws IOException { 164 if ("gzip".equals(request.getHeader("Compression"))) { 165 return new GZIPInputStream(is); 166 } 167 else { 168 return is; 169 } 170 } 171 protected OutputStream decorateOutputStream( 172 HttpServletRequest request, HttpServletResponse response, OutputStream os) throws IOException { 173 if ("gzip".equals(request.getHeader("Compression"))) { 174 return new GZIPOutputStream(os); 175 } 176 else { 177 return os; 178 } 179 } 180 }; 181 exporter.setServiceInterface(ITestBean.class); 182 exporter.setService(target); 183 exporter.afterPropertiesSet(); 184 185 HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean(); 186 pfb.setServiceInterface(ITestBean.class); 187 pfb.setServiceUrl("http://myurl"); 188 189 pfb.setHttpInvokerRequestExecutor(new AbstractHttpInvokerRequestExecutor() { 190 protected RemoteInvocationResult doExecuteRequest( 191 HttpInvokerClientConfiguration config, ByteArrayOutputStream baos) 192 throws IOException, ClassNotFoundException { 193 assertEquals("http://myurl", config.getServiceUrl()); 194 MockHttpServletRequest request = new MockHttpServletRequest(); 195 request.addHeader("Compression", "gzip"); 196 MockHttpServletResponse response = new MockHttpServletResponse(); 197 request.setContent(baos.toByteArray()); 198 try { 199 exporter.handleRequest(request, response); 200 } 201 catch (ServletException ex) { 202 throw new IOException(ex.toString()); 203 } 204 return readRemoteInvocationResult( 205 new ByteArrayInputStream(response.getContentAsByteArray()), config.getCodebaseUrl()); 206 } 207 protected OutputStream decorateOutputStream(OutputStream os) throws IOException { 208 return new GZIPOutputStream(os); 209 } 210 protected InputStream decorateInputStream(InputStream is) throws IOException { 211 return new GZIPInputStream(is); 212 } 213 }); 214 215 pfb.afterPropertiesSet(); 216 ITestBean proxy = (ITestBean) pfb.getObject(); 217 assertEquals("myname", proxy.getName()); 218 assertEquals(99, proxy.getAge()); 219 proxy.setAge(50); 220 assertEquals(50, proxy.getAge()); 221 222 try { 223 proxy.exceptional(new IllegalStateException()); 224 fail("Should have thrown IllegalStateException"); 225 } 226 catch (IllegalStateException ex) { 227 // expected 228 } 229 try { 230 proxy.exceptional(new IllegalAccessException()); 231 fail("Should have thrown IllegalAccessException"); 232 } 233 catch (IllegalAccessException ex) { 234 // expected 235 } 236 } 237 testHttpInvokerProxyFactoryBeanAndServiceExporterWithWrappedInvocations()238 public void testHttpInvokerProxyFactoryBeanAndServiceExporterWithWrappedInvocations() throws Throwable { 239 TestBean target = new TestBean("myname", 99); 240 241 final HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter() { 242 protected RemoteInvocation doReadRemoteInvocation(ObjectInputStream ois) 243 throws IOException, ClassNotFoundException { 244 Object obj = ois.readObject(); 245 if (!(obj instanceof TestRemoteInvocationWrapper)) { 246 throw new IOException("Deserialized object needs to be assignable to type [" + 247 TestRemoteInvocationWrapper.class.getName() + "]: " + obj); 248 } 249 return ((TestRemoteInvocationWrapper) obj).remoteInvocation; 250 } 251 protected void doWriteRemoteInvocationResult(RemoteInvocationResult result, ObjectOutputStream oos) 252 throws IOException { 253 oos.writeObject(new TestRemoteInvocationResultWrapper(result)); 254 } 255 }; 256 exporter.setServiceInterface(ITestBean.class); 257 exporter.setService(target); 258 exporter.afterPropertiesSet(); 259 260 HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean(); 261 pfb.setServiceInterface(ITestBean.class); 262 pfb.setServiceUrl("http://myurl"); 263 264 pfb.setHttpInvokerRequestExecutor(new AbstractHttpInvokerRequestExecutor() { 265 protected RemoteInvocationResult doExecuteRequest( 266 HttpInvokerClientConfiguration config, ByteArrayOutputStream baos) throws Exception { 267 assertEquals("http://myurl", config.getServiceUrl()); 268 MockHttpServletRequest request = new MockHttpServletRequest(); 269 MockHttpServletResponse response = new MockHttpServletResponse(); 270 request.setContent(baos.toByteArray()); 271 exporter.handleRequest(request, response); 272 return readRemoteInvocationResult( 273 new ByteArrayInputStream(response.getContentAsByteArray()), config.getCodebaseUrl()); 274 } 275 protected void doWriteRemoteInvocation(RemoteInvocation invocation, ObjectOutputStream oos) throws IOException { 276 oos.writeObject(new TestRemoteInvocationWrapper(invocation)); 277 } 278 protected RemoteInvocationResult doReadRemoteInvocationResult(ObjectInputStream ois) 279 throws IOException, ClassNotFoundException { 280 Object obj = ois.readObject(); 281 if (!(obj instanceof TestRemoteInvocationResultWrapper)) { 282 throw new IOException("Deserialized object needs to be assignable to type [" 283 + TestRemoteInvocationResultWrapper.class.getName() + "]: " + obj); 284 } 285 return ((TestRemoteInvocationResultWrapper) obj).remoteInvocationResult; 286 } 287 }); 288 289 pfb.afterPropertiesSet(); 290 ITestBean proxy = (ITestBean) pfb.getObject(); 291 assertEquals("myname", proxy.getName()); 292 assertEquals(99, proxy.getAge()); 293 proxy.setAge(50); 294 assertEquals(50, proxy.getAge()); 295 296 try { 297 proxy.exceptional(new IllegalStateException()); 298 fail("Should have thrown IllegalStateException"); 299 } 300 catch (IllegalStateException ex) { 301 // expected 302 } 303 try { 304 proxy.exceptional(new IllegalAccessException()); 305 fail("Should have thrown IllegalAccessException"); 306 } 307 catch (IllegalAccessException ex) { 308 // expected 309 } 310 } 311 testHttpInvokerProxyFactoryBeanAndServiceExporterWithInvocationAttributes()312 public void testHttpInvokerProxyFactoryBeanAndServiceExporterWithInvocationAttributes() throws Exception { 313 TestBean target = new TestBean("myname", 99); 314 315 final HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter(); 316 exporter.setServiceInterface(ITestBean.class); 317 exporter.setService(target); 318 exporter.setRemoteInvocationExecutor(new DefaultRemoteInvocationExecutor() { 319 public Object invoke(RemoteInvocation invocation, Object targetObject) 320 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 321 assertNotNull(invocation.getAttributes()); 322 assertEquals(1, invocation.getAttributes().size()); 323 assertEquals("myValue", invocation.getAttributes().get("myKey")); 324 assertEquals("myValue", invocation.getAttribute("myKey")); 325 return super.invoke(invocation, targetObject); 326 } 327 }); 328 exporter.afterPropertiesSet(); 329 330 HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean(); 331 pfb.setServiceInterface(ITestBean.class); 332 pfb.setServiceUrl("http://myurl"); 333 pfb.setRemoteInvocationFactory(new RemoteInvocationFactory() { 334 public RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation) { 335 RemoteInvocation invocation = new RemoteInvocation(methodInvocation); 336 invocation.addAttribute("myKey", "myValue"); 337 try { 338 invocation.addAttribute("myKey", "myValue"); 339 fail("Should have thrown IllegalStateException"); 340 } 341 catch (IllegalStateException ex) { 342 // expected: already defined 343 } 344 assertNotNull(invocation.getAttributes()); 345 assertEquals(1, invocation.getAttributes().size()); 346 assertEquals("myValue", invocation.getAttributes().get("myKey")); 347 assertEquals("myValue", invocation.getAttribute("myKey")); 348 return invocation; 349 } 350 }); 351 352 pfb.setHttpInvokerRequestExecutor(new AbstractHttpInvokerRequestExecutor() { 353 protected RemoteInvocationResult doExecuteRequest( 354 HttpInvokerClientConfiguration config, ByteArrayOutputStream baos) throws Exception { 355 assertEquals("http://myurl", config.getServiceUrl()); 356 MockHttpServletRequest request = new MockHttpServletRequest(); 357 MockHttpServletResponse response = new MockHttpServletResponse(); 358 request.setContent(baos.toByteArray()); 359 exporter.handleRequest(request, response); 360 return readRemoteInvocationResult( 361 new ByteArrayInputStream(response.getContentAsByteArray()), config.getCodebaseUrl()); 362 } 363 }); 364 365 pfb.afterPropertiesSet(); 366 ITestBean proxy = (ITestBean) pfb.getObject(); 367 assertEquals("myname", proxy.getName()); 368 assertEquals(99, proxy.getAge()); 369 } 370 testHttpInvokerProxyFactoryBeanAndServiceExporterWithCustomInvocationObject()371 public void testHttpInvokerProxyFactoryBeanAndServiceExporterWithCustomInvocationObject() throws Exception { 372 TestBean target = new TestBean("myname", 99); 373 374 final HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter(); 375 exporter.setServiceInterface(ITestBean.class); 376 exporter.setService(target); 377 exporter.setRemoteInvocationExecutor(new DefaultRemoteInvocationExecutor() { 378 public Object invoke(RemoteInvocation invocation, Object targetObject) 379 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 380 assertTrue(invocation instanceof TestRemoteInvocation); 381 assertNull(invocation.getAttributes()); 382 assertNull(invocation.getAttribute("myKey")); 383 return super.invoke(invocation, targetObject); 384 } 385 }); 386 exporter.afterPropertiesSet(); 387 388 HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean(); 389 pfb.setServiceInterface(ITestBean.class); 390 pfb.setServiceUrl("http://myurl"); 391 pfb.setRemoteInvocationFactory(new RemoteInvocationFactory() { 392 public RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation) { 393 RemoteInvocation invocation = new TestRemoteInvocation(methodInvocation); 394 assertNull(invocation.getAttributes()); 395 assertNull(invocation.getAttribute("myKey")); 396 return invocation; 397 } 398 }); 399 400 pfb.setHttpInvokerRequestExecutor(new AbstractHttpInvokerRequestExecutor() { 401 protected RemoteInvocationResult doExecuteRequest( 402 HttpInvokerClientConfiguration config, ByteArrayOutputStream baos) throws Exception { 403 assertEquals("http://myurl", config.getServiceUrl()); 404 MockHttpServletRequest request = new MockHttpServletRequest(); 405 MockHttpServletResponse response = new MockHttpServletResponse(); 406 request.setContent(baos.toByteArray()); 407 exporter.handleRequest(request, response); 408 return readRemoteInvocationResult( 409 new ByteArrayInputStream(response.getContentAsByteArray()), config.getCodebaseUrl()); 410 } 411 }); 412 413 pfb.afterPropertiesSet(); 414 ITestBean proxy = (ITestBean) pfb.getObject(); 415 assertEquals("myname", proxy.getName()); 416 assertEquals(99, proxy.getAge()); 417 } 418 testHttpInvokerWithSpecialLocalMethods()419 public void testHttpInvokerWithSpecialLocalMethods() throws Exception { 420 String serviceUrl = "http://myurl"; 421 HttpInvokerProxyFactoryBean pfb = new HttpInvokerProxyFactoryBean(); 422 pfb.setServiceInterface(ITestBean.class); 423 pfb.setServiceUrl(serviceUrl); 424 425 pfb.setHttpInvokerRequestExecutor(new HttpInvokerRequestExecutor() { 426 public RemoteInvocationResult executeRequest( 427 HttpInvokerClientConfiguration config, RemoteInvocation invocation) throws IOException { 428 throw new IOException("argh"); 429 } 430 }); 431 432 pfb.afterPropertiesSet(); 433 ITestBean proxy = (ITestBean) pfb.getObject(); 434 435 // shouldn't go through to remote service 436 assertTrue(proxy.toString().indexOf("HTTP invoker") != -1); 437 assertTrue(proxy.toString().indexOf(serviceUrl) != -1); 438 assertEquals(proxy.hashCode(), proxy.hashCode()); 439 assertTrue(proxy.equals(proxy)); 440 441 // should go through 442 try { 443 proxy.setAge(50); 444 fail("Should have thrown RemoteAccessException"); 445 } 446 catch (RemoteAccessException ex) { 447 // expected 448 assertTrue(ex.getCause() instanceof IOException); 449 } 450 } 451 452 453 private static class TestRemoteInvocation extends RemoteInvocation { 454 TestRemoteInvocation(MethodInvocation methodInvocation)455 public TestRemoteInvocation(MethodInvocation methodInvocation) { 456 super(methodInvocation); 457 } 458 } 459 460 461 private static class TestRemoteInvocationWrapper implements Serializable { 462 463 private final RemoteInvocation remoteInvocation; 464 TestRemoteInvocationWrapper(RemoteInvocation remoteInvocation)465 public TestRemoteInvocationWrapper(RemoteInvocation remoteInvocation) { 466 this.remoteInvocation = remoteInvocation; 467 } 468 } 469 470 471 private static class TestRemoteInvocationResultWrapper implements Serializable { 472 473 private final RemoteInvocationResult remoteInvocationResult; 474 TestRemoteInvocationResultWrapper(RemoteInvocationResult remoteInvocationResult)475 public TestRemoteInvocationResultWrapper(RemoteInvocationResult remoteInvocationResult) { 476 this.remoteInvocationResult = remoteInvocationResult; 477 } 478 } 479 480 } 481