From 9f52a0dbd7b94a8af497254f1a5b23382d69a385 Mon Sep 17 00:00:00 2001 From: Roy Clarkson Date: Thu, 22 May 2014 16:52:27 -0500 Subject: [PATCH] Add Spring Mobile SitePreferenceAutoConfiguration New feature in Spring Mobile (user preferences per site). Fixes gh-946 --- .../DeviceResolverAutoConfiguration.java | 10 +- .../SitePreferenceAutoConfiguration.java | 85 ++++++++++ .../main/resources/META-INF/spring.factories | 1 + .../DeviceResolverAutoConfigurationTests.java | 9 +- .../SitePreferenceAutoConfigurationTests.java | 153 ++++++++++++++++++ 5 files changed, 250 insertions(+), 8 deletions(-) create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mobile/SitePreferenceAutoConfiguration.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mobile/SitePreferenceAutoConfigurationTests.java diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mobile/DeviceResolverAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mobile/DeviceResolverAutoConfiguration.java index 98c6a118b9..86a9389188 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mobile/DeviceResolverAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mobile/DeviceResolverAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * Copyright 2012-2014 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. @@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.mobile; import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -41,7 +42,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter */ @Configuration @ConditionalOnClass({ DeviceResolverHandlerInterceptor.class, - DeviceHandlerMethodArgumentResolver.class }) + DeviceHandlerMethodArgumentResolver.class }) @AutoConfigureAfter(WebMvcAutoConfiguration.class) public class DeviceResolverAutoConfiguration { @@ -50,6 +51,9 @@ public class DeviceResolverAutoConfiguration { protected static class DeviceResolverAutoConfigurationAdapter extends WebMvcConfigurerAdapter { + @Autowired + private DeviceResolverHandlerInterceptor deviceResolverHandlerInterceptor; + @Bean @ConditionalOnMissingBean(DeviceResolverHandlerInterceptor.class) public DeviceResolverHandlerInterceptor deviceResolverHandlerInterceptor() { @@ -63,7 +67,7 @@ public class DeviceResolverAutoConfiguration { @Override public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(deviceResolverHandlerInterceptor()); + registry.addInterceptor(deviceResolverHandlerInterceptor); } @Override diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mobile/SitePreferenceAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mobile/SitePreferenceAutoConfiguration.java new file mode 100644 index 0000000000..2d6a944be0 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mobile/SitePreferenceAutoConfiguration.java @@ -0,0 +1,85 @@ +/* + * Copyright 2012-2014 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.boot.autoconfigure.mobile; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.mobile.device.DeviceResolver; +import org.springframework.mobile.device.site.SitePreferenceHandler; +import org.springframework.mobile.device.site.SitePreferenceHandlerInterceptor; +import org.springframework.mobile.device.site.SitePreferenceHandlerMethodArgumentResolver; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for Spring Mobile's + * {@link SitePreferenceHandler}. The site preference feature depends on a + * {@link DeviceResolver} first being registered. + * + * @author Roy Clarkson + * @since 1.1 + */ +@Configuration +@ConditionalOnClass({ SitePreferenceHandlerInterceptor.class, + SitePreferenceHandlerMethodArgumentResolver.class }) +@AutoConfigureAfter(DeviceResolverAutoConfiguration.class) +@ConditionalOnExpression("${spring.mobile.enableSitePreference:true}") +public class SitePreferenceAutoConfiguration { + + @Configuration + @ConditionalOnWebApplication + protected static class SitePreferenceAutoConfigurationAdapter extends + WebMvcConfigurerAdapter { + + @Autowired + private SitePreferenceHandlerInterceptor sitePreferenceHandlerInterceptor; + + @Bean + @ConditionalOnMissingBean(SitePreferenceHandlerInterceptor.class) + public SitePreferenceHandlerInterceptor sitePreferenceHandlerInterceptor() { + return new SitePreferenceHandlerInterceptor(); + } + + @Bean + public SitePreferenceHandlerMethodArgumentResolver sitePreferenceHandlerMethodArgumentResolver() { + return new SitePreferenceHandlerMethodArgumentResolver(); + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(sitePreferenceHandlerInterceptor); + } + + @Override + public void addArgumentResolvers( + List argumentResolvers) { + argumentResolvers.add(sitePreferenceHandlerMethodArgumentResolver()); + } + + } + +} diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index d62e1de642..6077195426 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -25,6 +25,7 @@ org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\ +org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mobile/DeviceResolverAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mobile/DeviceResolverAutoConfigurationTests.java index 175b96fcd6..4348e74799 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mobile/DeviceResolverAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mobile/DeviceResolverAutoConfigurationTests.java @@ -16,6 +16,9 @@ package org.springframework.boot.autoconfigure.mobile; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + import java.lang.reflect.Field; import java.util.List; @@ -36,9 +39,6 @@ import org.springframework.util.ReflectionUtils; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; - /** * Tests for {@link DeviceResolverAutoConfiguration}. * @@ -82,8 +82,7 @@ public class DeviceResolverAutoConfigurationTests { DeviceResolverAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); context.refresh(); - RequestMappingHandlerMapping mapping = (RequestMappingHandlerMapping) context - .getBean("requestMappingHandlerMapping"); + RequestMappingHandlerMapping mapping = (RequestMappingHandlerMapping) context.getBean("requestMappingHandlerMapping"); Field interceptorsField = ReflectionUtils.findField( RequestMappingHandlerMapping.class, "interceptors"); interceptorsField.setAccessible(true); diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mobile/SitePreferenceAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mobile/SitePreferenceAutoConfigurationTests.java new file mode 100644 index 0000000000..e5aa394a8b --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mobile/SitePreferenceAutoConfigurationTests.java @@ -0,0 +1,153 @@ +/* + * Copyright 2012-2014 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.boot.autoconfigure.mobile; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.lang.reflect.Field; +import java.util.List; + +import org.junit.After; +import org.junit.Test; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration; +import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; +import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext; +import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor; +import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; +import org.springframework.boot.context.embedded.MockEmbeddedServletContainerFactory; +import org.springframework.boot.test.EnvironmentTestUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.mobile.device.site.SitePreferenceHandlerInterceptor; +import org.springframework.mobile.device.site.SitePreferenceHandlerMethodArgumentResolver; +import org.springframework.util.ReflectionUtils; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +/** + * Tests for {@link SitePreferenceAutoConfiguration}. + * + * @author Roy Clarkson + */ +public class SitePreferenceAutoConfigurationTests { + + private static final MockEmbeddedServletContainerFactory containerFactory = new MockEmbeddedServletContainerFactory(); + + private AnnotationConfigWebApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void sitePreferenceHandlerInterceptorCreated() { + this.context = new AnnotationConfigWebApplicationContext(); + this.context.register(SitePreferenceAutoConfiguration.class); + this.context.refresh(); + assertNotNull(this.context.getBean(SitePreferenceHandlerInterceptor.class)); + } + + @Test + public void sitePreferenceHandlerInterceptorEnabled() throws Exception { + this.context = new AnnotationConfigWebApplicationContext(); + EnvironmentTestUtils.addEnvironment(context, "spring.mobile.enableSitePreference:true"); + this.context.register(SitePreferenceAutoConfiguration.class); + this.context.refresh(); + assertNotNull(this.context.getBean(SitePreferenceHandlerInterceptor.class)); + } + + @Test(expected = NoSuchBeanDefinitionException.class) + public void sitePreferenceHandlerInterceptorDisabled() { + this.context = new AnnotationConfigWebApplicationContext(); + EnvironmentTestUtils.addEnvironment(context, "spring.mobile.enableSitePreference:false"); + this.context.register(SitePreferenceAutoConfiguration.class); + this.context.refresh(); + this.context.getBean(SitePreferenceHandlerInterceptor.class); + } + + @Test + public void sitePreferenceMethodArgumentResolverCreated() throws Exception { + this.context = new AnnotationConfigWebApplicationContext(); + this.context.register(SitePreferenceAutoConfiguration.class); + this.context.refresh(); + assertNotNull(this.context.getBean(SitePreferenceHandlerMethodArgumentResolver.class)); + } + + @Test + public void sitePreferenceMethodArgumentResolverEnabled() throws Exception { + this.context = new AnnotationConfigWebApplicationContext(); + EnvironmentTestUtils.addEnvironment(context, "spring.mobile.enableSitePreference:true"); + this.context.register(SitePreferenceAutoConfiguration.class); + this.context.refresh(); + assertNotNull(this.context.getBean(SitePreferenceHandlerMethodArgumentResolver.class)); + } + + @Test(expected = NoSuchBeanDefinitionException.class) + public void sitePreferenceMethodArgumentResolverDisabled() { + this.context = new AnnotationConfigWebApplicationContext(); + EnvironmentTestUtils.addEnvironment(context, "spring.mobile.enableSitePreference:false"); + this.context.register(SitePreferenceAutoConfiguration.class); + this.context.refresh(); + this.context.getBean(SitePreferenceHandlerMethodArgumentResolver.class); + } + + @Test + @SuppressWarnings("unchecked") + public void sitePreferenceHandlerInterceptorRegistered() throws Exception { + AnnotationConfigEmbeddedWebApplicationContext context = new AnnotationConfigEmbeddedWebApplicationContext(); + context.register(Config.class, WebMvcAutoConfiguration.class, + HttpMessageConvertersAutoConfiguration.class, + SitePreferenceAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + context.refresh(); + RequestMappingHandlerMapping mapping = (RequestMappingHandlerMapping) context.getBean("requestMappingHandlerMapping"); + Field interceptorsField = ReflectionUtils.findField( + RequestMappingHandlerMapping.class, "interceptors"); + interceptorsField.setAccessible(true); + List interceptors = (List) ReflectionUtils.getField( + interceptorsField, mapping); + context.close(); + for (Object o : interceptors) { + if (o instanceof SitePreferenceHandlerInterceptor) { + return; + } + } + fail("SitePreferenceHandlerInterceptor was not registered."); + } + + @Configuration + protected static class Config { + + @Bean + public EmbeddedServletContainerFactory containerFactory() { + return containerFactory; + } + + @Bean + public EmbeddedServletContainerCustomizerBeanPostProcessor embeddedServletContainerCustomizerBeanPostProcessor() { + return new EmbeddedServletContainerCustomizerBeanPostProcessor(); + } + + } + +}