/* * Copyright 2002-2017 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. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.web.method.support; import java.lang.reflect.Method; import org.junit.Before; import org.junit.Test; import org.springframework.core.MethodParameter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.mock.web.test.MockHttpServletResponse; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.ServletWebRequest; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; /** * Test fixture for {@link InvocableHandlerMethod} unit tests. * * @author Rossen Stoyanchev */ public class InvocableHandlerMethodTests { private InvocableHandlerMethod handlerMethod; private NativeWebRequest webRequest; @Before public void setUp() throws Exception { Method method = Handler.class.getDeclaredMethod("handle", Integer.class, String.class); this.handlerMethod = new InvocableHandlerMethod(new Handler(), method); this.webRequest = new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse()); } @Test public void resolveArg() throws Exception { StubArgumentResolver intResolver = new StubArgumentResolver(Integer.class, 99); StubArgumentResolver stringResolver = new StubArgumentResolver(String.class, "value"); HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite(); composite.addResolver(intResolver); composite.addResolver(stringResolver); handlerMethod.setHandlerMethodArgumentResolvers(composite); Object returnValue = handlerMethod.invokeForRequest(webRequest, null); assertEquals(1, intResolver.getResolvedParameters().size()); assertEquals(1, stringResolver.getResolvedParameters().size()); assertEquals("99-value", returnValue); assertEquals("intArg", intResolver.getResolvedParameters().get(0).getParameterName()); assertEquals("stringArg", stringResolver.getResolvedParameters().get(0).getParameterName()); } @Test public void resolveNullArg() throws Exception { StubArgumentResolver intResolver = new StubArgumentResolver(Integer.class, null); StubArgumentResolver stringResolver = new StubArgumentResolver(String.class, null); HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite(); composite.addResolver(intResolver); composite.addResolver(stringResolver); handlerMethod.setHandlerMethodArgumentResolvers(composite); Object returnValue = handlerMethod.invokeForRequest(webRequest, null); assertEquals(1, intResolver.getResolvedParameters().size()); assertEquals(1, stringResolver.getResolvedParameters().size()); assertEquals("null-null", returnValue); } @Test public void cannotResolveArg() throws Exception { try { handlerMethod.invokeForRequest(webRequest, null); fail("Expected exception"); } catch (IllegalStateException ex) { assertTrue(ex.getMessage().contains("No suitable resolver for argument 0 of type 'java.lang.Integer'")); } } @Test public void resolveProvidedArg() throws Exception { Object returnValue = handlerMethod.invokeForRequest(webRequest, null, 99, "value"); assertEquals(String.class, returnValue.getClass()); assertEquals("99-value", returnValue); } @Test public void resolveProvidedArgFirst() throws Exception { StubArgumentResolver intResolver = new StubArgumentResolver(Integer.class, 1); StubArgumentResolver stringResolver = new StubArgumentResolver(String.class, "value1"); HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite(); composite.addResolver(intResolver); composite.addResolver(stringResolver); handlerMethod.setHandlerMethodArgumentResolvers(composite); Object returnValue = handlerMethod.invokeForRequest(webRequest, null, 2, "value2"); assertEquals("2-value2", returnValue); } @Test public void exceptionInResolvingArg() throws Exception { HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite(); composite.addResolver(new ExceptionRaisingArgumentResolver()); handlerMethod.setHandlerMethodArgumentResolvers(composite); try { handlerMethod.invokeForRequest(webRequest, null); fail("Expected exception"); } catch (HttpMessageNotReadableException ex) { // expected - allow HandlerMethodArgumentResolver exceptions to propagate } } @Test public void illegalArgumentException() throws Exception { StubArgumentResolver intResolver = new StubArgumentResolver(Integer.class, "__invalid__"); StubArgumentResolver stringResolver = new StubArgumentResolver(String.class, "value"); HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite(); composite.addResolver(intResolver); composite.addResolver(stringResolver); handlerMethod.setHandlerMethodArgumentResolvers(composite); try { handlerMethod.invokeForRequest(webRequest, null); fail("Expected exception"); } catch (IllegalStateException ex) { assertNotNull("Exception not wrapped", ex.getCause()); assertTrue(ex.getCause() instanceof IllegalArgumentException); assertTrue(ex.getMessage().contains("Controller [")); assertTrue(ex.getMessage().contains("Method [")); assertTrue(ex.getMessage().contains("Resolved arguments: ")); assertTrue(ex.getMessage().contains("[0] [type=java.lang.String] [value=__invalid__]")); assertTrue(ex.getMessage().contains("[1] [type=java.lang.String] [value=value")); } } @Test public void invocationTargetException() throws Exception { Throwable expected = new RuntimeException("error"); try { invokeExceptionRaisingHandler(expected); } catch (RuntimeException actual) { assertSame(expected, actual); } expected = new Error("error"); try { invokeExceptionRaisingHandler(expected); } catch (Error actual) { assertSame(expected, actual); } expected = new Exception("error"); try { invokeExceptionRaisingHandler(expected); } catch (Exception actual) { assertSame(expected, actual); } expected = new Throwable("error"); try { invokeExceptionRaisingHandler(expected); } catch (IllegalStateException actual) { assertNotNull(actual.getCause()); assertSame(expected, actual.getCause()); assertTrue(actual.getMessage().contains("Failed to invoke handler method")); } } @Test // SPR-13917 public void invocationErrorMessage() throws Exception { HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite(); composite.addResolver(new StubArgumentResolver(double.class, null)); Method method = Handler.class.getDeclaredMethod("handle", double.class); Object handler = new Handler(); InvocableHandlerMethod hm = new InvocableHandlerMethod(handler, method); hm.setHandlerMethodArgumentResolvers(composite); try { hm.invokeForRequest(this.webRequest, new ModelAndViewContainer()); fail(); } catch (IllegalStateException ex) { assertThat(ex.getMessage(), containsString("Illegal argument")); } } private void invokeExceptionRaisingHandler(Throwable expected) throws Exception { Method method = ExceptionRaisingHandler.class.getDeclaredMethod("raiseException"); Object handler = new ExceptionRaisingHandler(expected); new InvocableHandlerMethod(handler, method).invokeForRequest(webRequest, null); fail("Expected exception"); } @SuppressWarnings("unused") private static class Handler { public String handle(Integer intArg, String stringArg) { return intArg + "-" + stringArg; } public void handle(double amount) { } } @SuppressWarnings("unused") private static class ExceptionRaisingHandler { private final Throwable t; public ExceptionRaisingHandler(Throwable t) { this.t = t; } public void raiseException() throws Throwable { throw t; } } private static class ExceptionRaisingArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { return true; } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { throw new HttpMessageNotReadableException("oops, can't read"); } } }