/* * Copyright 2002-2015 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.servlet.handler; import java.lang.reflect.Method; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Set; import javax.servlet.http.HttpServletRequest; import org.junit.Before; import org.junit.Test; import org.springframework.context.support.StaticApplicationContext; import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.stereotype.Controller; import org.springframework.util.AntPathMatcher; import org.springframework.util.PathMatcher; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.context.support.StaticWebApplicationContext; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.method.HandlerMethod; import org.springframework.web.util.UrlPathHelper; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; /** * Test for {@link AbstractHandlerMethodMapping}. * * @author Arjen Poutsma * @author Rossen Stoyanchev */ @SuppressWarnings("unused") public class HandlerMethodMappingTests { private AbstractHandlerMethodMapping<String> mapping; private MyHandler handler; private Method method1; private Method method2; @Before public void setUp() throws Exception { this.mapping = new MyHandlerMethodMapping(); this.handler = new MyHandler(); this.method1 = handler.getClass().getMethod("handlerMethod1"); this.method2 = handler.getClass().getMethod("handlerMethod2"); } @Test(expected = IllegalStateException.class) public void registerDuplicates() { this.mapping.registerMapping("foo", this.handler, this.method1); this.mapping.registerMapping("foo", this.handler, this.method2); } @Test public void directMatch() throws Exception { String key = "foo"; this.mapping.registerMapping(key, this.handler, this.method1); HandlerMethod result = this.mapping.getHandlerInternal(new MockHttpServletRequest("GET", key)); assertEquals(method1, result.getMethod()); } @Test public void patternMatch() throws Exception { this.mapping.registerMapping("/fo*", this.handler, this.method1); this.mapping.registerMapping("/f*", this.handler, this.method2); HandlerMethod result = this.mapping.getHandlerInternal(new MockHttpServletRequest("GET", "/foo")); assertEquals(method1, result.getMethod()); } @Test(expected = IllegalStateException.class) public void ambiguousMatch() throws Exception { this.mapping.registerMapping("/f?o", this.handler, this.method1); this.mapping.registerMapping("/fo?", this.handler, this.method2); this.mapping.getHandlerInternal(new MockHttpServletRequest("GET", "/foo")); } @Test public void detectHandlerMethodsInAncestorContexts() { StaticApplicationContext cxt = new StaticApplicationContext(); cxt.registerSingleton("myHandler", MyHandler.class); AbstractHandlerMethodMapping<String> mapping1 = new MyHandlerMethodMapping(); mapping1.setApplicationContext(new StaticApplicationContext(cxt)); mapping1.afterPropertiesSet(); assertEquals(0, mapping1.getHandlerMethods().size()); AbstractHandlerMethodMapping<String> mapping2 = new MyHandlerMethodMapping(); mapping2.setDetectHandlerMethodsInAncestorContexts(true); mapping2.setApplicationContext(new StaticApplicationContext(cxt)); mapping2.afterPropertiesSet(); assertEquals(2, mapping2.getHandlerMethods().size()); } @Test public void registerMapping() throws Exception { String key1 = "/foo"; String key2 = "/foo*"; this.mapping.registerMapping(key1, this.handler, this.method1); this.mapping.registerMapping(key2, this.handler, this.method2); // Direct URL lookup List directUrlMatches = this.mapping.getMappingRegistry().getMappingsByUrl(key1); assertNotNull(directUrlMatches); assertEquals(1, directUrlMatches.size()); assertEquals(key1, directUrlMatches.get(0)); // Mapping name lookup HandlerMethod handlerMethod1 = new HandlerMethod(this.handler, this.method1); HandlerMethod handlerMethod2 = new HandlerMethod(this.handler, this.method2); String name1 = this.method1.getName(); List<HandlerMethod> handlerMethods = this.mapping.getMappingRegistry().getHandlerMethodsByMappingName(name1); assertNotNull(handlerMethods); assertEquals(1, handlerMethods.size()); assertEquals(handlerMethod1, handlerMethods.get(0)); String name2 = this.method2.getName(); handlerMethods = this.mapping.getMappingRegistry().getHandlerMethodsByMappingName(name2); assertNotNull(handlerMethods); assertEquals(1, handlerMethods.size()); assertEquals(handlerMethod2, handlerMethods.get(0)); // CORS lookup CorsConfiguration config = this.mapping.getMappingRegistry().getCorsConfiguration(handlerMethod1); assertNotNull(config); assertEquals("http://" + handler.hashCode() + name1, config.getAllowedOrigins().get(0)); config = this.mapping.getMappingRegistry().getCorsConfiguration(handlerMethod2); assertNotNull(config); assertEquals("http://" + handler.hashCode() + name2, config.getAllowedOrigins().get(0)); } @Test public void registerMappingWithSameMethodAndTwoHandlerInstances() throws Exception { String key1 = "foo"; String key2 = "bar"; MyHandler handler1 = new MyHandler(); MyHandler handler2 = new MyHandler(); HandlerMethod handlerMethod1 = new HandlerMethod(handler1, this.method1); HandlerMethod handlerMethod2 = new HandlerMethod(handler2, this.method1); this.mapping.registerMapping(key1, handler1, this.method1); this.mapping.registerMapping(key2, handler2, this.method1); // Direct URL lookup List directUrlMatches = this.mapping.getMappingRegistry().getMappingsByUrl(key1); assertNotNull(directUrlMatches); assertEquals(1, directUrlMatches.size()); assertEquals(key1, directUrlMatches.get(0)); // Mapping name lookup String name = this.method1.getName(); List<HandlerMethod> handlerMethods = this.mapping.getMappingRegistry().getHandlerMethodsByMappingName(name); assertNotNull(handlerMethods); assertEquals(2, handlerMethods.size()); assertEquals(handlerMethod1, handlerMethods.get(0)); assertEquals(handlerMethod2, handlerMethods.get(1)); // CORS lookup CorsConfiguration config = this.mapping.getMappingRegistry().getCorsConfiguration(handlerMethod1); assertNotNull(config); assertEquals("http://" + handler1.hashCode() + name, config.getAllowedOrigins().get(0)); config = this.mapping.getMappingRegistry().getCorsConfiguration(handlerMethod2); assertNotNull(config); assertEquals("http://" + handler2.hashCode() + name, config.getAllowedOrigins().get(0)); } @Test public void unregisterMapping() throws Exception { String key = "foo"; HandlerMethod handlerMethod = new HandlerMethod(this.handler, this.method1); this.mapping.registerMapping(key, this.handler, this.method1); assertNotNull(this.mapping.getHandlerInternal(new MockHttpServletRequest("GET", key))); this.mapping.unregisterMapping(key); assertNull(mapping.getHandlerInternal(new MockHttpServletRequest("GET", key))); assertNull(this.mapping.getMappingRegistry().getMappingsByUrl(key)); assertNull(this.mapping.getMappingRegistry().getHandlerMethodsByMappingName(this.method1.getName())); assertNull(this.mapping.getMappingRegistry().getCorsConfiguration(handlerMethod)); } @Test public void getCorsConfigWithBeanNameHandler() throws Exception { String key = "foo"; String beanName = "handler1"; StaticWebApplicationContext context = new StaticWebApplicationContext(); context.registerSingleton(beanName, MyHandler.class); this.mapping.setApplicationContext(context); this.mapping.registerMapping(key, beanName, this.method1); HandlerMethod handlerMethod = this.mapping.getHandlerInternal(new MockHttpServletRequest("GET", key)); CorsConfiguration config = this.mapping.getMappingRegistry().getCorsConfiguration(handlerMethod); assertNotNull(config); assertEquals("http://" + beanName.hashCode() + this.method1.getName(), config.getAllowedOrigins().get(0)); } private static class MyHandlerMethodMapping extends AbstractHandlerMethodMapping<String> { private UrlPathHelper pathHelper = new UrlPathHelper(); private PathMatcher pathMatcher = new AntPathMatcher(); public MyHandlerMethodMapping() { setHandlerMethodMappingNamingStrategy(new SimpleMappingNamingStrategy()); } @Override protected boolean isHandler(Class<?> beanType) { return true; } @Override protected String getMappingForMethod(Method method, Class<?> handlerType) { String methodName = method.getName(); return methodName.startsWith("handler") ? methodName : null; } @Override protected Set<String> getMappingPathPatterns(String key) { return (this.pathMatcher.isPattern(key) ? Collections.<String>emptySet() : Collections.singleton(key)); } @Override protected CorsConfiguration initCorsConfiguration(Object handler, Method method, String mapping) { CorsConfiguration corsConfig = new CorsConfiguration(); corsConfig.setAllowedOrigins(Collections.singletonList("http://" + handler.hashCode() + method.getName())); return corsConfig; } @Override protected String getMatchingMapping(String pattern, HttpServletRequest request) { String lookupPath = this.pathHelper.getLookupPathForRequest(request); return this.pathMatcher.match(pattern, lookupPath) ? pattern : null; } @Override protected Comparator<String> getMappingComparator(HttpServletRequest request) { String lookupPath = this.pathHelper.getLookupPathForRequest(request); return this.pathMatcher.getPatternComparator(lookupPath); } } private static class SimpleMappingNamingStrategy implements HandlerMethodMappingNamingStrategy<String> { @Override public String getName(HandlerMethod handlerMethod, String mapping) { return handlerMethod.getMethod().getName(); } } @Controller static class MyHandler { @RequestMapping public void handlerMethod1() { } @RequestMapping public void handlerMethod2() { } } }