4 changed files with 371 additions and 32 deletions
@ -0,0 +1,158 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2011 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.config; |
||||||
|
|
||||||
|
import java.util.Collections; |
||||||
|
import java.util.LinkedHashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinitionHolder; |
||||||
|
import org.springframework.beans.factory.parsing.ProblemCollector; |
||||||
|
import org.springframework.context.config.AbstractFeatureSpecification; |
||||||
|
import org.springframework.context.config.FeatureSpecificationExecutor; |
||||||
|
import org.springframework.util.StringUtils; |
||||||
|
import org.springframework.web.context.request.WebRequestInterceptor; |
||||||
|
import org.springframework.web.servlet.HandlerInterceptor; |
||||||
|
import org.springframework.web.servlet.handler.MappedInterceptor; |
||||||
|
|
||||||
|
/** |
||||||
|
* Specifies the Spring MVC "interceptors" container feature. The feature |
||||||
|
* registers one or more {@link MappedInterceptor} bean definitions. A |
||||||
|
* MappedInterceptor encapsulates an interceptor and one or more (optional) |
||||||
|
* path patterns to which the interceptor is mapped. The interceptor can be |
||||||
|
* of type {@link HandlerInterceptor} or {@link WebRequestInterceptor}. |
||||||
|
* An interceptor can also be provided without path patterns in which case |
||||||
|
* it applies globally to all handler invocations. |
||||||
|
* |
||||||
|
* @author Rossen Stoyanchev |
||||||
|
* @since 3.1 |
||||||
|
*/ |
||||||
|
public class MvcInterceptors extends AbstractFeatureSpecification { |
||||||
|
|
||||||
|
private static final Class<? extends FeatureSpecificationExecutor> EXECUTOR_TYPE = MvcInterceptorsExecutor.class; |
||||||
|
|
||||||
|
private Map<Object, String[]> interceptorMappings = new LinkedHashMap<Object, String[]>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates an MvcInterceptors instance. |
||||||
|
*/ |
||||||
|
public MvcInterceptors() { |
||||||
|
super(EXECUTOR_TYPE); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add one or more {@link HandlerInterceptor HandlerInterceptors} that should |
||||||
|
* intercept all handler invocations. |
||||||
|
* |
||||||
|
* @param interceptors one or more interceptors |
||||||
|
*/ |
||||||
|
public MvcInterceptors globalInterceptors(HandlerInterceptor... interceptors) { |
||||||
|
addInterceptorMappings(null, interceptors); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add one or more {@link WebRequestInterceptor WebRequestInterceptors} that should |
||||||
|
* intercept all handler invocations. |
||||||
|
* |
||||||
|
* @param interceptors one or more interceptors |
||||||
|
*/ |
||||||
|
public MvcInterceptors globalInterceptors(WebRequestInterceptor... interceptors) { |
||||||
|
addInterceptorMappings(null, interceptors); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add one or more interceptors by bean name that should intercept all handler |
||||||
|
* invocations. |
||||||
|
* |
||||||
|
* @param interceptors interceptor bean names |
||||||
|
*/ |
||||||
|
public MvcInterceptors globalInterceptors(String... interceptors) { |
||||||
|
addInterceptorMappings(null, interceptors); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add one or more {@link HandlerInterceptor HandlerInterceptors} and map |
||||||
|
* them to the specified path patterns. |
||||||
|
* |
||||||
|
* @param pathPatterns the pathPatterns to map the interceptor to |
||||||
|
* @param interceptors the interceptors |
||||||
|
*/ |
||||||
|
public MvcInterceptors mappedInterceptors(String[] pathPatterns, HandlerInterceptor... interceptors) { |
||||||
|
addInterceptorMappings(pathPatterns, interceptors); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add one or more {@link WebRequestInterceptor WebRequestInterceptors} and |
||||||
|
* map them to the specified path patterns. |
||||||
|
* |
||||||
|
* @param pathPatterns the pathPatterns to map the interceptor to |
||||||
|
* @param interceptors the interceptors |
||||||
|
*/ |
||||||
|
public MvcInterceptors mappedInterceptors(String[] pathPatterns, WebRequestInterceptor... interceptors) { |
||||||
|
addInterceptorMappings(pathPatterns, interceptors); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add one or more interceptors by bean name and map them to the specified |
||||||
|
* path patterns. |
||||||
|
* |
||||||
|
* @param pathPatterns the pathPatterns to map to |
||||||
|
* @param interceptors the interceptors |
||||||
|
*/ |
||||||
|
public MvcInterceptors mappedInterceptors(String[] pathPatterns, String... interceptors) { |
||||||
|
addInterceptorMappings(pathPatterns, interceptors); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
void interceptor(String[] pathPatterns, BeanDefinitionHolder interceptor) { |
||||||
|
addInterceptorMappings(pathPatterns, new Object[] { interceptor }); |
||||||
|
} |
||||||
|
|
||||||
|
Map<Object, String[]> interceptorMappings() { |
||||||
|
return Collections.unmodifiableMap(interceptorMappings); |
||||||
|
} |
||||||
|
|
||||||
|
private void addInterceptorMappings(String[] pathPatterns, Object[] interceptors) { |
||||||
|
for (Object interceptor : interceptors) { |
||||||
|
interceptorMappings.put(interceptor, pathPatterns); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void doValidate(ProblemCollector problems) { |
||||||
|
if (interceptorMappings.size() == 0) { |
||||||
|
problems.error("No interceptors defined."); |
||||||
|
} |
||||||
|
for (Object interceptor : interceptorMappings.keySet()) { |
||||||
|
if (interceptor == null) { |
||||||
|
problems.error("Null interceptor provided."); |
||||||
|
} |
||||||
|
if (interceptorMappings.get(interceptor) != null) { |
||||||
|
for (String pattern : interceptorMappings.get(interceptor)) { |
||||||
|
if (!StringUtils.hasText(pattern)) { |
||||||
|
problems.error("Empty path pattern specified for " + interceptor); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,60 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2011 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.config; |
||||||
|
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinition; |
||||||
|
import org.springframework.beans.factory.parsing.BeanComponentDefinition; |
||||||
|
import org.springframework.beans.factory.parsing.ComponentRegistrar; |
||||||
|
import org.springframework.beans.factory.parsing.CompositeComponentDefinition; |
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition; |
||||||
|
import org.springframework.context.config.AbstractSpecificationExecutor; |
||||||
|
import org.springframework.context.config.SpecificationContext; |
||||||
|
import org.springframework.web.servlet.handler.MappedInterceptor; |
||||||
|
|
||||||
|
/** |
||||||
|
* Executes {@link MvcInterceptors} specification, creating and registering |
||||||
|
* bean definitions as appropriate based on the configuration within. |
||||||
|
* |
||||||
|
* @author Keith Donald |
||||||
|
* @author Rossen Stoyanchev |
||||||
|
* |
||||||
|
* @since 3.1 |
||||||
|
*/ |
||||||
|
final class MvcInterceptorsExecutor extends AbstractSpecificationExecutor<MvcInterceptors> { |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void doExecute(MvcInterceptors spec, SpecificationContext specContext) { |
||||||
|
ComponentRegistrar registrar = specContext.getRegistrar(); |
||||||
|
Object source = spec.source(); |
||||||
|
|
||||||
|
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(spec.sourceName(), source); |
||||||
|
|
||||||
|
for (Object interceptor : spec.interceptorMappings().keySet()) { |
||||||
|
RootBeanDefinition beanDef = new RootBeanDefinition(MappedInterceptor.class); |
||||||
|
beanDef.setSource(source); |
||||||
|
beanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); |
||||||
|
beanDef.getConstructorArgumentValues().addIndexedArgumentValue(0, |
||||||
|
spec.interceptorMappings().get(interceptor)); |
||||||
|
beanDef.getConstructorArgumentValues().addIndexedArgumentValue(1, interceptor); |
||||||
|
|
||||||
|
String beanName = registrar.registerWithGeneratedName(beanDef); |
||||||
|
compDefinition.addNestedComponent(new BeanComponentDefinition(beanDef, beanName)); |
||||||
|
} |
||||||
|
|
||||||
|
registrar.registerComponent(compDefinition); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,126 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2011 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.config; |
||||||
|
|
||||||
|
import static org.easymock.EasyMock.capture; |
||||||
|
import static org.easymock.EasyMock.createMock; |
||||||
|
import static org.easymock.EasyMock.replay; |
||||||
|
import static org.junit.Assert.assertArrayEquals; |
||||||
|
import static org.junit.Assert.assertEquals; |
||||||
|
import static org.junit.Assert.assertFalse; |
||||||
|
import static org.junit.Assert.assertNull; |
||||||
|
import static org.junit.Assert.assertTrue; |
||||||
|
|
||||||
|
import java.util.Iterator; |
||||||
|
|
||||||
|
import org.easymock.Capture; |
||||||
|
import org.junit.Test; |
||||||
|
import org.springframework.beans.factory.parsing.Problem; |
||||||
|
import org.springframework.beans.factory.parsing.ProblemReporter; |
||||||
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
||||||
|
import org.springframework.context.annotation.Feature; |
||||||
|
import org.springframework.context.annotation.FeatureConfiguration; |
||||||
|
import org.springframework.web.servlet.HandlerInterceptor; |
||||||
|
import org.springframework.web.servlet.handler.MappedInterceptor; |
||||||
|
import org.springframework.web.servlet.handler.UserRoleAuthorizationInterceptor; |
||||||
|
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; |
||||||
|
import org.springframework.web.servlet.theme.ThemeChangeInterceptor; |
||||||
|
|
||||||
|
/** |
||||||
|
* Test fixture for {@link MvcInterceptors}. |
||||||
|
* @author Rossen Stoyanchev |
||||||
|
*/ |
||||||
|
public class MvcInterceptorsTests { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testInterceptors() { |
||||||
|
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); |
||||||
|
ctx.register(MvcInterceptorsFeature.class); |
||||||
|
ctx.refresh(); |
||||||
|
|
||||||
|
Iterator<MappedInterceptor> itr = ctx.getBeansOfType(MappedInterceptor.class).values().iterator(); |
||||||
|
|
||||||
|
MappedInterceptor interceptor = itr.next(); |
||||||
|
assertTrue(interceptor.getInterceptor() instanceof UserRoleAuthorizationInterceptor); |
||||||
|
assertNull(interceptor.getPathPatterns()); |
||||||
|
|
||||||
|
interceptor = itr.next(); |
||||||
|
assertTrue(interceptor.getInterceptor() instanceof LocaleChangeInterceptor); |
||||||
|
assertArrayEquals(new String[] { "/locale", "/locale/**" }, interceptor.getPathPatterns()); |
||||||
|
|
||||||
|
interceptor = itr.next(); |
||||||
|
assertTrue(interceptor.getInterceptor() instanceof ThemeChangeInterceptor); |
||||||
|
assertArrayEquals(new String[] { "/theme", "/theme/**" }, interceptor.getPathPatterns()); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void validateNoInterceptors() { |
||||||
|
ProblemReporter reporter = createMock(ProblemReporter.class); |
||||||
|
Capture<Problem> captured = new Capture<Problem>(); |
||||||
|
reporter.error(capture(captured)); |
||||||
|
replay(reporter); |
||||||
|
|
||||||
|
boolean result = new MvcInterceptors().validate(reporter); |
||||||
|
|
||||||
|
assertFalse(result); |
||||||
|
assertEquals("No interceptors defined.", captured.getValue().getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void validateNullHandler() { |
||||||
|
ProblemReporter reporter = createMock(ProblemReporter.class); |
||||||
|
Capture<Problem> captured = new Capture<Problem>(); |
||||||
|
reporter.error(capture(captured)); |
||||||
|
replay(reporter); |
||||||
|
|
||||||
|
HandlerInterceptor[] interceptors = new HandlerInterceptor[] { null }; |
||||||
|
boolean result = new MvcInterceptors().globalInterceptors(interceptors).validate(reporter); |
||||||
|
|
||||||
|
assertFalse(result); |
||||||
|
assertTrue(captured.getValue().getMessage().contains("Null interceptor")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void validateEmptyPath() { |
||||||
|
ProblemReporter reporter = createMock(ProblemReporter.class); |
||||||
|
Capture<Problem> captured = new Capture<Problem>(); |
||||||
|
reporter.error(capture(captured)); |
||||||
|
replay(reporter); |
||||||
|
|
||||||
|
HandlerInterceptor[] interceptors = new HandlerInterceptor[] { new LocaleChangeInterceptor() }; |
||||||
|
String[] patterns = new String[] { "" }; |
||||||
|
boolean result = new MvcInterceptors().mappedInterceptors(patterns, interceptors).validate(reporter); |
||||||
|
|
||||||
|
assertFalse(result); |
||||||
|
assertTrue(captured.getValue().getMessage().startsWith("Empty path pattern specified for ")); |
||||||
|
} |
||||||
|
|
||||||
|
@FeatureConfiguration |
||||||
|
private static class MvcInterceptorsFeature { |
||||||
|
|
||||||
|
@SuppressWarnings("unused") |
||||||
|
@Feature |
||||||
|
public MvcInterceptors interceptors() { |
||||||
|
return new MvcInterceptors() |
||||||
|
.globalInterceptors(new UserRoleAuthorizationInterceptor()) |
||||||
|
.mappedInterceptors(new String[] { "/locale", "/locale/**" }, new LocaleChangeInterceptor()) |
||||||
|
.mappedInterceptors(new String[] { "/theme", "/theme/**"}, new ThemeChangeInterceptor()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue