mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2239ddf6f4 | |||
| d77af71e9c | |||
| 5db4e4bab0 | |||
| 7cc56e1630 | |||
| b5e80390de | |||
| 55a14eb684 | |||
| d8269dd9fe | |||
| aae7583141 | |||
| 6fafe63311 | |||
| f1e406c63b | |||
| c8a4d1649f | |||
| 8dbe753963 | |||
| 23fa37b08b | |||
| cc78d40c6b | |||
| 1dc39324b9 | |||
| 2a240b0dc8 | |||
| 3b8d878285 | |||
| d98f626be1 | |||
| b8dbf2345c | |||
| 2b135f0d51 | |||
| e5d41d91d5 | |||
| f398dd03a7 | |||
| f82c6635d7 | |||
| 9499e4166c | |||
| bf442c093c | |||
| fab8dd3d0b | |||
| 1646f62700 | |||
| c8d9bfadad | |||
| 0706889222 | |||
| 25644dbdb9 | |||
| 181b35243e | |||
| 6c47b5f5d3 | |||
| 4a423e50a8 | |||
| 0225a7776c | |||
| d57f7f8783 | |||
| 4550b4a254 | |||
| f902fb911c | |||
| 77c8aa53ae | |||
| bb5da15e1c | |||
| 0c8d07fcff | |||
| 47fdac9214 | |||
| 7ffa845aa8 | |||
| 09bd7037e9 | |||
| 49c21a0a06 | |||
| d9d8a79c30 | |||
| 04840166af | |||
| 6fce6d4668 | |||
| 0c856b3d22 | |||
| d63cfc8eeb | |||
| 7621cc787d | |||
| 2c1682af34 | |||
| dcaa0987ee | |||
| 14a3bf3941 | |||
| 23b09015fe | |||
| b82e9c4d46 | |||
| c158874d82 | |||
| 87b7cd6368 | |||
| 901d7d07db | |||
| bfba988a0d | |||
| a7975c685d | |||
| cdaf4497b6 | |||
| 84807c5d61 | |||
| a76a6509e5 | |||
| 7d171e6722 | |||
| 36da551280 | |||
| ed665a12a3 | |||
| adec0265a4 | |||
| 8c700b19da | |||
| 9542313710 | |||
| dcb821edb5 | |||
| daa4adf2ed |
+18
-16
@@ -27,34 +27,34 @@ configure(allprojects) { project ->
|
||||
group = "org.springframework"
|
||||
version = qualifyVersionIfNecessary(version)
|
||||
|
||||
ext.aspectjVersion = "1.8.4"
|
||||
ext.aspectjVersion = "1.8.5"
|
||||
ext.eclipseLinkVersion = "2.4.2"
|
||||
ext.ehcacheVersion = "2.9.0"
|
||||
ext.ehcacheJCacheVersion = "1.0.0"
|
||||
ext.groovyVersion = "2.3.9"
|
||||
ext.groovyVersion = "2.3.10"
|
||||
ext.gsonVersion = "2.3.1"
|
||||
ext.hibernate3Version = "3.6.10.Final"
|
||||
ext.hibernate4Version = "4.3.7.Final"
|
||||
ext.hibernate4Version = "4.3.8.Final"
|
||||
ext.hibVal4Version = "4.3.2.Final"
|
||||
ext.hibVal5Version = "5.1.3.Final"
|
||||
ext.hsqldbVersion = "2.3.2"
|
||||
ext.jackson2Version = "2.4.4"
|
||||
ext.jasperReportsVersion = "6.0.0"
|
||||
ext.jettyVersion = "9.2.6.v20141205"
|
||||
ext.jodaVersion = "2.6"
|
||||
ext.jackson2Version = "2.4.5"
|
||||
ext.jasperReportsVersion = "6.0.3"
|
||||
ext.jettyVersion = "9.2.7.v20150116"
|
||||
ext.jodaVersion = "2.7"
|
||||
ext.junitVersion = "4.12"
|
||||
ext.nettyVersion = "4.0.24.Final"
|
||||
ext.nettyVersion = "4.0.25.Final"
|
||||
ext.openJpaVersion = "2.2.2" // 2.3.0 not Java 8 compatible (based on ASM 4)
|
||||
ext.protobufVersion = "2.6.1"
|
||||
ext.reactorVersion = "1.1.5.RELEASE"
|
||||
ext.slf4jVersion = "1.7.9"
|
||||
ext.slf4jVersion = "1.7.10"
|
||||
ext.snakeYamlVersion = "1.14"
|
||||
ext.snifferVersion = "1.11"
|
||||
ext.tiles2Version = "2.2.2"
|
||||
ext.tiles3Version = "3.0.5"
|
||||
ext.tomcatVersion = "8.0.15"
|
||||
ext.tomcatVersion = "8.0.18"
|
||||
ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support
|
||||
ext.undertowVersion = "1.1.1.Final"
|
||||
ext.undertowVersion = "1.1.2.Final"
|
||||
ext.woodstoxVersion = "4.4.1"
|
||||
ext.xstreamVersion = "1.4.7"
|
||||
|
||||
@@ -143,7 +143,6 @@ configure(allprojects) { project ->
|
||||
|
||||
inputs.dir sourceSets.main.output.classesDir
|
||||
inputs.dir copyJavaApiSignature.to
|
||||
outputs.upToDateWhen { true }
|
||||
|
||||
doLast {
|
||||
ant.taskdef(
|
||||
@@ -595,7 +594,7 @@ project("spring-jdbc") {
|
||||
optional("javax.transaction:javax.transaction-api:1.2")
|
||||
optional("com.mchange:c3p0:0.9.2.1")
|
||||
optional("org.hsqldb:hsqldb:${hsqldbVersion}")
|
||||
optional("com.h2database:h2:1.4.182")
|
||||
optional("com.h2database:h2:1.4.185")
|
||||
optional("org.apache.derby:derby:10.11.1.1")
|
||||
optional("org.apache.derby:derbyclient:10.11.1.1")
|
||||
}
|
||||
@@ -625,9 +624,10 @@ project("spring-context-support") {
|
||||
exclude group: "com.fasterxml.jackson.core", module: "jackson-databind"
|
||||
exclude group: "org.olap4j", module: "olap4j"
|
||||
exclude group: "xml-apis", module: "xml-apis"
|
||||
exclude group: "org.springframework", module: "spring-context"
|
||||
}
|
||||
testCompile(project(":spring-context"))
|
||||
testCompile("org.apache.poi:poi:3.10.1")
|
||||
testCompile("org.apache.poi:poi:3.11")
|
||||
testCompile("commons-beanutils:commons-beanutils:1.8.0") // for Velocity/JasperReports
|
||||
testCompile("commons-digester:commons-digester:1.8.1") // for Velocity/JasperReports
|
||||
testCompile("org.hsqldb:hsqldb:${hsqldbVersion}")
|
||||
@@ -697,6 +697,7 @@ project("spring-web") {
|
||||
testCompile("org.apache.taglibs:taglibs-standard-jstlel:1.2.1") {
|
||||
exclude group: "org.apache.taglibs", module: "taglibs-standard-spec"
|
||||
}
|
||||
testCompile("com.fasterxml.jackson.datatype:jackson-datatype-joda:${jackson2Version}")
|
||||
testRuntime("com.sun.mail:javax.mail:1.5.2")
|
||||
}
|
||||
|
||||
@@ -826,10 +827,10 @@ project("spring-webmvc") {
|
||||
optional("javax.servlet.jsp:javax.servlet.jsp-api:2.2.1")
|
||||
optional("javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.1")
|
||||
optional("net.sourceforge.jexcelapi:jxl:2.6.12")
|
||||
optional("org.apache.poi:poi:3.10.1")
|
||||
optional("org.apache.poi:poi:3.11")
|
||||
optional("org.apache.velocity:velocity:1.7")
|
||||
optional("velocity-tools:velocity-tools-view:1.4")
|
||||
optional("org.freemarker:freemarker:2.3.20")
|
||||
optional("org.freemarker:freemarker:2.3.21")
|
||||
optional("org.codehaus.groovy:groovy-all:${groovyVersion}")
|
||||
optional("com.lowagie:itext:2.1.7")
|
||||
optional("net.sf.jasperreports:jasperreports:$jasperReportsVersion") {
|
||||
@@ -838,6 +839,7 @@ project("spring-webmvc") {
|
||||
exclude group: "com.fasterxml.jackson.core", module: "jackson-databind"
|
||||
exclude group: "org.olap4j", module: "olap4j"
|
||||
exclude group: "xml-apis", module: "xml-apis"
|
||||
exclude group: "org.springframework", module: "spring-context"
|
||||
}
|
||||
optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}")
|
||||
optional("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:${jackson2Version}")
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
version=4.1.4.BUILD-SNAPSHOT
|
||||
version=4.1.5.RELEASE
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -78,6 +78,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Rob Harrop
|
||||
* @author Stephane Nicoll
|
||||
* @since 15 April 2001
|
||||
* @see #registerCustomEditor
|
||||
* @see #setPropertyValues
|
||||
@@ -978,6 +979,14 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
|
||||
}
|
||||
Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(),
|
||||
requiredType, TypeDescriptor.nested(property(pd), tokens.keys.length));
|
||||
int length = Array.getLength(propValue);
|
||||
if (arrayIndex >= length && arrayIndex < this.autoGrowCollectionLimit) {
|
||||
Class<?> componentType = propValue.getClass().getComponentType();
|
||||
Object newArray = Array.newInstance(componentType, arrayIndex + 1);
|
||||
System.arraycopy(propValue, 0, newArray, 0, length);
|
||||
setPropertyValue(actualName, newArray);
|
||||
propValue = getPropertyValue(actualName);
|
||||
}
|
||||
Array.set(propValue, arrayIndex, convertedValue);
|
||||
}
|
||||
catch (IndexOutOfBoundsException ex) {
|
||||
|
||||
+12
-4
@@ -130,9 +130,10 @@ public abstract class YamlProcessor {
|
||||
* Properties. Depending on the {@link #setResolutionMethod(ResolutionMethod)} not all
|
||||
* of the documents will be parsed.
|
||||
* @param callback a callback to delegate to once matching documents are found
|
||||
* @see #createYaml()
|
||||
*/
|
||||
protected void process(MatchCallback callback) {
|
||||
Yaml yaml = new Yaml(new StrictMapAppenderConstructor());
|
||||
Yaml yaml = createYaml();
|
||||
for (Resource resource : this.resources) {
|
||||
boolean found = process(callback, yaml, resource);
|
||||
if (this.resolutionMethod == ResolutionMethod.FIRST_FOUND && found) {
|
||||
@@ -141,6 +142,13 @@ public abstract class YamlProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the {@link Yaml} instance to use.
|
||||
*/
|
||||
protected Yaml createYaml() {
|
||||
return new Yaml(new StrictMapAppenderConstructor());
|
||||
}
|
||||
|
||||
private boolean process(MatchCallback callback, Yaml yaml, Resource resource) {
|
||||
int count = 0;
|
||||
try {
|
||||
@@ -331,7 +339,7 @@ public abstract class YamlProcessor {
|
||||
/**
|
||||
* Status returned from {@link DocumentMatcher#matches(java.util.Properties)}
|
||||
*/
|
||||
public static enum MatchStatus {
|
||||
public enum MatchStatus {
|
||||
|
||||
/**
|
||||
* A match was found.
|
||||
@@ -360,7 +368,7 @@ public abstract class YamlProcessor {
|
||||
/**
|
||||
* Method to use for resolving resources.
|
||||
*/
|
||||
public static enum ResolutionMethod {
|
||||
public enum ResolutionMethod {
|
||||
|
||||
/**
|
||||
* Replace values from earlier in the list.
|
||||
@@ -382,7 +390,7 @@ public abstract class YamlProcessor {
|
||||
/**
|
||||
* A specialized {@link Constructor} that checks for duplicate keys.
|
||||
*/
|
||||
private static class StrictMapAppenderConstructor extends Constructor {
|
||||
protected static class StrictMapAppenderConstructor extends Constructor {
|
||||
|
||||
public StrictMapAppenderConstructor() {
|
||||
super();
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* 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.
|
||||
@@ -150,8 +150,8 @@ public class BeanDefinitionReaderUtils {
|
||||
// Register aliases for bean name, if any.
|
||||
String[] aliases = definitionHolder.getAliases();
|
||||
if (aliases != null) {
|
||||
for (String aliase : aliases) {
|
||||
registry.registerAlias(beanName, aliase);
|
||||
for (String alias : aliases) {
|
||||
registry.registerAlias(beanName, alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -562,17 +562,10 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) {
|
||||
Map<String, Object> results = new LinkedHashMap<String, Object>();
|
||||
for (String beanName : this.beanDefinitionNames) {
|
||||
BeanDefinition beanDefinition = getBeanDefinition(beanName);
|
||||
if (!beanDefinition.isAbstract() && findAnnotationOnBean(beanName, annotationType) != null) {
|
||||
results.put(beanName, getBean(beanName));
|
||||
}
|
||||
}
|
||||
for (String beanName : this.manualSingletonNames) {
|
||||
if (!results.containsKey(beanName) && findAnnotationOnBean(beanName, annotationType) != null) {
|
||||
results.put(beanName, getBean(beanName));
|
||||
}
|
||||
String[] beanNames = getBeanNamesForAnnotation(annotationType);
|
||||
Map<String, Object> results = new LinkedHashMap<String, Object>(beanNames.length);
|
||||
for (String beanName : beanNames) {
|
||||
results.put(beanName, getBean(beanName));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
+22
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* 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.
|
||||
@@ -49,12 +49,13 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public abstract class AbstractBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
/** Constant for the id attribute */
|
||||
/** Constant for the "id" attribute */
|
||||
public static final String ID_ATTRIBUTE = "id";
|
||||
|
||||
/** Constant for the name attribute */
|
||||
/** Constant for the "name" attribute */
|
||||
public static final String NAME_ATTRIBUTE = "name";
|
||||
|
||||
|
||||
@Override
|
||||
public final BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
AbstractBeanDefinition definition = parseInternal(element, parserContext);
|
||||
@@ -66,10 +67,12 @@ public abstract class AbstractBeanDefinitionParser implements BeanDefinitionPars
|
||||
"Id is required for element '" + parserContext.getDelegate().getLocalName(element)
|
||||
+ "' when used as a top-level tag", element);
|
||||
}
|
||||
String[] aliases = new String[0];
|
||||
String name = element.getAttribute(NAME_ATTRIBUTE);
|
||||
if (StringUtils.hasLength(name)) {
|
||||
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
|
||||
String[] aliases = null;
|
||||
if (shouldParseNameAsAliases()) {
|
||||
String name = element.getAttribute(NAME_ATTRIBUTE);
|
||||
if (StringUtils.hasLength(name)) {
|
||||
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
|
||||
}
|
||||
}
|
||||
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
|
||||
registerBeanDefinition(holder, parserContext.getRegistry());
|
||||
@@ -170,7 +173,18 @@ public abstract class AbstractBeanDefinitionParser implements BeanDefinitionPars
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls whether this parser is supposed to fire a
|
||||
* Determine whether the element's "name" attribute should get parsed as
|
||||
* bean definition aliases, i.e. alternative bean definition names.
|
||||
* <p>The default implementation returns {@code true}.
|
||||
* @return whether the parser should evaluate the "name" attribute as aliases
|
||||
* @since 4.1.5
|
||||
*/
|
||||
protected boolean shouldParseNameAsAliases() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether this parser is supposed to fire a
|
||||
* {@link org.springframework.beans.factory.parsing.BeanComponentDefinition}
|
||||
* event after parsing the bean definition.
|
||||
* <p>This implementation returns {@code true} by default; that is,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -518,6 +518,36 @@ public final class BeanWrapperTests extends AbstractConfigurablePropertyAccessor
|
||||
assertTrue("correct values", pt.stringArray[0].equals("a1") && pt.stringArray[1].equals("b2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringArrayAutoGrow() throws Exception {
|
||||
StringArrayBean target = new StringArrayBean();
|
||||
BeanWrapper bw = new BeanWrapperImpl(target);
|
||||
bw.setAutoGrowNestedPaths(true);
|
||||
|
||||
bw.setPropertyValue("array[0]", "Test0");
|
||||
assertEquals(1, target.getArray().length);
|
||||
|
||||
bw.setPropertyValue("array[2]", "Test2");
|
||||
assertEquals(3, target.getArray().length);
|
||||
assertTrue("correct values", target.getArray()[0].equals("Test0") && target.getArray()[1] == null &&
|
||||
target.getArray()[2].equals("Test2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrimitiveArrayAutoGrow() throws Exception {
|
||||
PrimitiveArrayBean target = new PrimitiveArrayBean();
|
||||
BeanWrapper bw = new BeanWrapperImpl(target);
|
||||
bw.setAutoGrowNestedPaths(true);
|
||||
|
||||
bw.setPropertyValue("array[0]", 1);
|
||||
assertEquals(1, target.getArray().length);
|
||||
|
||||
bw.setPropertyValue("array[2]", 3);
|
||||
assertEquals(3, target.getArray().length);
|
||||
assertTrue("correct values", target.getArray()[0] == 1 && target.getArray()[1] == 0 &&
|
||||
target.getArray()[2] == 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringPropertyWithCustomEditor() throws Exception {
|
||||
TestBean tb = new TestBean();
|
||||
@@ -1723,6 +1753,20 @@ public final class BeanWrapperTests extends AbstractConfigurablePropertyAccessor
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class StringArrayBean {
|
||||
|
||||
private String[] array;
|
||||
|
||||
public String[] getArray() {
|
||||
return array;
|
||||
}
|
||||
|
||||
public void setArray(String[] array) {
|
||||
this.array = array;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class NumberPropertyBean {
|
||||
|
||||
@@ -841,19 +841,24 @@ public class ExtendedBeanInfoTests {
|
||||
public String getAddress(int index){ return null; }
|
||||
}
|
||||
|
||||
{ // baseline. ExtendedBeanInfo needs to behave exactly like the following
|
||||
// Baseline: ExtendedBeanInfo needs to behave exactly like the following...
|
||||
boolean hasReadMethod;
|
||||
boolean hasWriteMethod;
|
||||
boolean hasIndexedReadMethod;
|
||||
boolean hasIndexedWriteMethod;
|
||||
{
|
||||
BeanInfo bi = Introspector.getBeanInfo(A.class);
|
||||
assertThat(hasReadMethodForProperty(bi, "address"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "address"), is(false));
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "address"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "address"), is(true));
|
||||
hasReadMethod = hasReadMethodForProperty(bi, "address");
|
||||
hasWriteMethod = hasWriteMethodForProperty(bi, "address");
|
||||
hasIndexedReadMethod = hasIndexedReadMethodForProperty(bi, "address");
|
||||
hasIndexedWriteMethod = hasIndexedWriteMethodForProperty(bi, "address");
|
||||
}
|
||||
{
|
||||
BeanInfo bi = new ExtendedBeanInfo(Introspector.getBeanInfo(A.class));
|
||||
assertThat(hasReadMethodForProperty(bi, "address"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "address"), is(false));
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "address"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "address"), is(true));
|
||||
assertEquals(hasReadMethodForProperty(bi, "address"), hasReadMethod);
|
||||
assertEquals(hasWriteMethodForProperty(bi, "address"), hasWriteMethod);
|
||||
assertEquals(hasIndexedReadMethodForProperty(bi, "address"), hasIndexedReadMethod);
|
||||
assertEquals(hasIndexedWriteMethodForProperty(bi, "address"), hasIndexedWriteMethod);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -381,17 +381,20 @@ public class QuartzSupportTests {
|
||||
|
||||
/**
|
||||
* SPR-6038: detect HSQL and stop illegal locks being taken.
|
||||
* TODO: Against Quartz 2.2, this test's job doesn't actually execute anymore...
|
||||
*/
|
||||
@Test
|
||||
public void schedulerWithHsqlDataSource() throws Exception {
|
||||
Assume.group(TestGroup.PERFORMANCE);
|
||||
// Assume.group(TestGroup.PERFORMANCE);
|
||||
|
||||
DummyJob.param = 0;
|
||||
DummyJob.count = 0;
|
||||
|
||||
ClassPathXmlApplicationContext ctx = context("databasePersistence.xml");
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(ctx.getBean(DataSource.class));
|
||||
assertTrue("No triggers were persisted", jdbcTemplate.queryForList("SELECT * FROM qrtz_triggers").size()>0);
|
||||
assertFalse("No triggers were persisted", jdbcTemplate.queryForList("SELECT * FROM qrtz_triggers").isEmpty());
|
||||
|
||||
/*
|
||||
Thread.sleep(3000);
|
||||
try {
|
||||
assertTrue(DummyJob.count > 0);
|
||||
@@ -399,6 +402,7 @@ public class QuartzSupportTests {
|
||||
finally {
|
||||
ctx.close();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private ClassPathXmlApplicationContext context(String path) {
|
||||
|
||||
+4
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
@@ -63,6 +64,8 @@ final class ConfigurationClass {
|
||||
private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars =
|
||||
new LinkedHashMap<ImportBeanDefinitionRegistrar, AnnotationMetadata>();
|
||||
|
||||
final Set<String> skippedBeans = new HashSet<String>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link ConfigurationClass} with the given name.
|
||||
|
||||
+25
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -182,16 +182,37 @@ class ConfigurationClassBeanDefinitionReader {
|
||||
* with the BeanDefinitionRegistry based on its contents.
|
||||
*/
|
||||
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
|
||||
if (this.conditionEvaluator.shouldSkip(beanMethod.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
|
||||
ConfigurationClass configClass = beanMethod.getConfigurationClass();
|
||||
MethodMetadata metadata = beanMethod.getMetadata();
|
||||
|
||||
// Consider name and any aliases
|
||||
AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
|
||||
List<String> names = new ArrayList<String>(Arrays.asList(bean.getStringArray("name")));
|
||||
String beanName = (names.size() > 0 ? names.remove(0) : beanMethod.getMetadata().getMethodName());
|
||||
|
||||
// Do we need to mark the bean as skipped by its condition?
|
||||
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
|
||||
configClass.skippedBeans.add(beanName);
|
||||
return;
|
||||
}
|
||||
if (configClass.skippedBeans.contains(beanName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigurationClass configClass = beanMethod.getConfigurationClass();
|
||||
MethodMetadata metadata = beanMethod.getMetadata();
|
||||
// Register aliases even when overridden
|
||||
for (String alias : names) {
|
||||
this.registry.registerAlias(beanName, alias);
|
||||
}
|
||||
|
||||
// Has this effectively been overridden before (e.g. via XML)?
|
||||
if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
|
||||
beanDef.setResource(configClass.getResource());
|
||||
beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
|
||||
|
||||
if (metadata.isStatic()) {
|
||||
// static @Bean method
|
||||
beanDef.setBeanClassName(configClass.getMetadata().getClassName());
|
||||
@@ -205,19 +226,6 @@ class ConfigurationClassBeanDefinitionReader {
|
||||
beanDef.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||
beanDef.setAttribute(RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
|
||||
|
||||
// Consider name and any aliases
|
||||
AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
|
||||
List<String> names = new ArrayList<String>(Arrays.asList(bean.getStringArray("name")));
|
||||
String beanName = (names.size() > 0 ? names.remove(0) : beanMethod.getMetadata().getMethodName());
|
||||
for (String alias : names) {
|
||||
this.registry.registerAlias(beanName, alias);
|
||||
}
|
||||
|
||||
// Has this effectively been overridden before (e.g. via XML)?
|
||||
if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);
|
||||
|
||||
Autowire autowire = bean.getEnum("autowire");
|
||||
|
||||
+4
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* 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.
|
||||
@@ -394,6 +394,8 @@ public class ResourceBundleMessageSource extends AbstractMessageSource implement
|
||||
@Override
|
||||
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
|
||||
throws IllegalAccessException, InstantiationException, IOException {
|
||||
|
||||
// Special handling of default encoding
|
||||
if (format.equals("java.properties")) {
|
||||
String bundleName = toBundleName(baseName, locale);
|
||||
final String resourceName = toResourceName(bundleName, "properties");
|
||||
@@ -441,6 +443,7 @@ public class ResourceBundleMessageSource extends AbstractMessageSource implement
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Delegate handling of "java.class" format to standard Control
|
||||
return super.newBundle(baseName, locale, format, loader, reload);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo
|
||||
private boolean exposeManagedResourceClassLoader = true;
|
||||
|
||||
/** A set of bean names that should be excluded from autodetection */
|
||||
private Set<String> excludedBeans;
|
||||
private Set<String> excludedBeans = new HashSet<String>();
|
||||
|
||||
/** The MBeanExporterListeners registered with this exporter. */
|
||||
private MBeanExporterListener[] listeners;
|
||||
@@ -314,7 +314,18 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo
|
||||
* Set the list of names for beans that should be excluded from autodetection.
|
||||
*/
|
||||
public void setExcludedBeans(String... excludedBeans) {
|
||||
this.excludedBeans = (excludedBeans != null ? new HashSet<String>(Arrays.asList(excludedBeans)) : null);
|
||||
this.excludedBeans.clear();
|
||||
if (excludedBeans != null) {
|
||||
this.excludedBeans.addAll(Arrays.asList(excludedBeans));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the name of bean that should be excluded from autodetection.
|
||||
*/
|
||||
public void addExcludedBean(String excludedBean) {
|
||||
Assert.notNull(excludedBean, "ExcludedBean must not be null");
|
||||
this.excludedBeans.add(excludedBean);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -922,10 +933,9 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo
|
||||
* Indicates whether or not a particular bean name is present in the excluded beans list.
|
||||
*/
|
||||
private boolean isExcluded(String beanName) {
|
||||
return (this.excludedBeans != null &&
|
||||
(this.excludedBeans.contains(beanName) ||
|
||||
(beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX) &&
|
||||
this.excludedBeans.contains(beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length())))));
|
||||
return (this.excludedBeans.contains(beanName) ||
|
||||
(beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX) &&
|
||||
this.excludedBeans.contains(beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length()))));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+38
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -39,7 +39,10 @@ import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.EmbeddedValueResolverAware;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
@@ -61,12 +64,12 @@ import org.springframework.util.StringValueResolver;
|
||||
* to the "fixedRate", "fixedDelay", or "cron" expression provided via the annotation.
|
||||
*
|
||||
* <p>This post-processor is automatically registered by Spring's
|
||||
* {@code <task:annotation-driven>} XML element, and also by the @{@link EnableScheduling}
|
||||
* annotation.
|
||||
* {@code <task:annotation-driven>} XML element, and also by the
|
||||
* @{@link EnableScheduling} annotation.
|
||||
*
|
||||
* <p>Auto-detects any {@link SchedulingConfigurer} instances in the container,
|
||||
* allowing for customization of the scheduler to be used or for fine-grained control
|
||||
* over task registration (e.g. registration of {@link Trigger} tasks.
|
||||
* <p>Autodetects any {@link SchedulingConfigurer} instances in the container,
|
||||
* allowing for customization of the scheduler to be used or for fine-grained
|
||||
* control over task registration (e.g. registration of {@link Trigger} tasks.
|
||||
* See the @{@link EnableScheduling} javadocs for complete usage details.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
@@ -80,7 +83,8 @@ import org.springframework.util.StringValueResolver;
|
||||
* @see org.springframework.scheduling.config.ScheduledTaskRegistrar
|
||||
*/
|
||||
public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered,
|
||||
EmbeddedValueResolverAware, BeanFactoryAware, SmartInitializingSingleton, DisposableBean {
|
||||
EmbeddedValueResolverAware, BeanFactoryAware, ApplicationContextAware,
|
||||
SmartInitializingSingleton, ApplicationListener<ContextRefreshedEvent>, DisposableBean {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@@ -90,6 +94,8 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor,
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private final ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar();
|
||||
|
||||
private final Set<Class<?>> nonAnnotatedClasses =
|
||||
@@ -126,10 +132,13 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor,
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated as of Spring 4.1, in favor of {@link #setBeanFactory}
|
||||
* Setting an {@link ApplicationContext} is optional: If set, registered
|
||||
* tasks will be activated in the {@link ContextRefreshedEvent} phase;
|
||||
* if not set, it will happen at {@link #afterSingletonsInstantiated} time.
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
if (this.beanFactory == null) {
|
||||
this.beanFactory = applicationContext;
|
||||
}
|
||||
@@ -138,6 +147,23 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor,
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
if (this.applicationContext == null) {
|
||||
// Not running in an ApplicationContext -> register tasks early...
|
||||
finishRegistration();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
if (event.getApplicationContext() == this.applicationContext) {
|
||||
// Running in an ApplicationContext -> register tasks this late...
|
||||
// giving other ContextRefreshedEvent listeners a chance to perform
|
||||
// their work at the same time (e.g. Spring Batch's job registration).
|
||||
finishRegistration();
|
||||
}
|
||||
}
|
||||
|
||||
private void finishRegistration() {
|
||||
if (this.scheduler != null) {
|
||||
this.registrar.setScheduler(this.scheduler);
|
||||
}
|
||||
@@ -190,9 +216,9 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor,
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(final Object bean, String beanName) {
|
||||
if (!this.nonAnnotatedClasses.contains(bean.getClass())) {
|
||||
Class<?> targetClass = AopUtils.getTargetClass(bean);
|
||||
if (!this.nonAnnotatedClasses.contains(targetClass)) {
|
||||
final Set<Method> annotatedMethods = new LinkedHashSet<Method>(1);
|
||||
Class<?> targetClass = AopUtils.getTargetClass(bean);
|
||||
ReflectionUtils.doWithMethods(targetClass, new MethodCallback() {
|
||||
@Override
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
@@ -204,7 +230,7 @@ public class ScheduledAnnotationBeanPostProcessor implements BeanPostProcessor,
|
||||
}
|
||||
});
|
||||
if (annotatedMethods.isEmpty()) {
|
||||
this.nonAnnotatedClasses.add(bean.getClass());
|
||||
this.nonAnnotatedClasses.add(targetClass);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No @Scheduled annotations found on bean class: " + bean.getClass());
|
||||
}
|
||||
|
||||
+44
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* 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.
|
||||
@@ -39,6 +39,7 @@ import static org.junit.Assert.*;
|
||||
* Test for {@link Conditional} beans.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public class ConfigurationClassWithConditionTests {
|
||||
@@ -117,6 +118,19 @@ public class ConfigurationClassWithConditionTests {
|
||||
ctx.refresh();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void conditionOnOverriddenMethodHonored() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigWithBeanSkipped.class);
|
||||
assertEquals(0, context.getBeansOfType(ExampleBean.class).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noConditionOnOverriddenMethodHonored() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigWithBeanReactivated.class);
|
||||
assertEquals(1, context.getBeansOfType(ExampleBean.class).size());
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class BeanOneConfiguration {
|
||||
@Bean
|
||||
@@ -197,6 +211,7 @@ public class ConfigurationClassWithConditionTests {
|
||||
}
|
||||
|
||||
static class NeverCondition implements Condition {
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
return false;
|
||||
@@ -261,4 +276,32 @@ public class ConfigurationClassWithConditionTests {
|
||||
static class ExampleBean {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
private static class ConfigWithBeanActive {
|
||||
|
||||
@Bean
|
||||
public ExampleBean baz() {
|
||||
return new ExampleBean();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ConfigWithBeanSkipped extends ConfigWithBeanActive {
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
@Conditional(NeverCondition.class)
|
||||
public ExampleBean baz() {
|
||||
return new ExampleBean();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ConfigWithBeanReactivated extends ConfigWithBeanSkipped {
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public ExampleBean baz() {
|
||||
return new ExampleBean();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+28
-4
@@ -17,6 +17,8 @@
|
||||
package org.springframework.jmx.export;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -643,6 +645,28 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
|
||||
ObjectNameManager.getInstance(objectName2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIgnoreBeanName() throws MalformedObjectNameException {
|
||||
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
|
||||
String firstBeanName = "spring:type=TestBean";
|
||||
factory.registerSingleton(firstBeanName, new TestBean("test"));
|
||||
String secondBeanName = "spring:type=TestBean2";
|
||||
factory.registerSingleton(secondBeanName, new TestBean("test2"));
|
||||
|
||||
MBeanExporter exporter = new MBeanExporter();
|
||||
exporter.setServer(getServer());
|
||||
exporter.setAssembler(new NamedBeanAutodetectCapableMBeanInfoAssemblerStub(firstBeanName, secondBeanName));
|
||||
exporter.setBeanFactory(factory);
|
||||
exporter.setAutodetectMode(MBeanExporter.AUTODETECT_ALL);
|
||||
exporter.addExcludedBean(secondBeanName);
|
||||
|
||||
start(exporter);
|
||||
assertIsRegistered("Bean not autodetected in (AUTODETECT_ALL) mode",
|
||||
ObjectNameManager.getInstance(firstBeanName));
|
||||
assertIsNotRegistered("Bean should have been excluded",
|
||||
ObjectNameManager.getInstance(secondBeanName));
|
||||
}
|
||||
|
||||
private ConfigurableApplicationContext load(String context) {
|
||||
return new ClassPathXmlApplicationContext(context, getClass());
|
||||
}
|
||||
@@ -763,15 +787,15 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests {
|
||||
private static final class NamedBeanAutodetectCapableMBeanInfoAssemblerStub extends
|
||||
SimpleReflectiveMBeanInfoAssembler implements AutodetectCapableMBeanInfoAssembler {
|
||||
|
||||
private String namedBean;
|
||||
private Collection<String> namedBeans;
|
||||
|
||||
public NamedBeanAutodetectCapableMBeanInfoAssemblerStub(String namedBean) {
|
||||
this.namedBean = namedBean;
|
||||
public NamedBeanAutodetectCapableMBeanInfoAssemblerStub(String... namedBeans) {
|
||||
this.namedBeans = Arrays.asList(namedBeans);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean includeBean(Class<?> beanClass, String beanName) {
|
||||
return this.namedBean.equals(beanName);
|
||||
return this.namedBeans.contains(beanName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -309,6 +309,7 @@ public class MethodParameter {
|
||||
/**
|
||||
* Return the generic type of the method/constructor parameter.
|
||||
* @return the parameter type (never {@code null})
|
||||
* @since 3.0
|
||||
*/
|
||||
public Type getGenericParameterType() {
|
||||
if (this.genericParameterType == null) {
|
||||
@@ -324,6 +325,12 @@ public class MethodParameter {
|
||||
return this.genericParameterType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the nested type of the method/constructor parameter.
|
||||
* @return the parameter type (never {@code null})
|
||||
* @see #getNestingLevel()
|
||||
* @since 3.1
|
||||
*/
|
||||
public Class<?> getNestedParameterType() {
|
||||
if (this.nestingLevel > 1) {
|
||||
Type type = getGenericParameterType();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -1092,9 +1092,8 @@ public final class ResolvableType implements Serializable {
|
||||
public static ResolvableType forMethodParameter(MethodParameter methodParameter, Type targetType) {
|
||||
Assert.notNull(methodParameter, "MethodParameter must not be null");
|
||||
ResolvableType owner = forType(methodParameter.getContainingClass()).as(methodParameter.getDeclaringClass());
|
||||
return forType(targetType, new MethodParameterTypeProvider(methodParameter),
|
||||
owner.asVariableResolver()).getNested(methodParameter.getNestingLevel(),
|
||||
methodParameter.typeIndexesPerLevel);
|
||||
return forType(targetType, new MethodParameterTypeProvider(methodParameter), owner.asVariableResolver()).
|
||||
getNested(methodParameter.getNestingLevel(), methodParameter.typeIndexesPerLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* General utility methods for working with annotations, handling bridge methods
|
||||
@@ -654,6 +655,9 @@ public abstract class AnnotationUtils {
|
||||
* @see #getValue(Annotation)
|
||||
*/
|
||||
public static Object getValue(Annotation annotation, String attributeName) {
|
||||
if (annotation == null || !StringUtils.hasLength(attributeName)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Method method = annotation.annotationType().getDeclaredMethod(attributeName);
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
@@ -683,6 +687,9 @@ public abstract class AnnotationUtils {
|
||||
* @see #getDefaultValue(Class, String)
|
||||
*/
|
||||
public static Object getDefaultValue(Annotation annotation, String attributeName) {
|
||||
if (annotation == null) {
|
||||
return null;
|
||||
}
|
||||
return getDefaultValue(annotation.annotationType(), attributeName);
|
||||
}
|
||||
|
||||
@@ -706,6 +713,9 @@ public abstract class AnnotationUtils {
|
||||
* @see #getDefaultValue(Annotation, String)
|
||||
*/
|
||||
public static Object getDefaultValue(Class<? extends Annotation> annotationType, String attributeName) {
|
||||
if (annotationType == null || !StringUtils.hasLength(attributeName)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return annotationType.getDeclaredMethod(attributeName).getDefaultValue();
|
||||
}
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ final class ObjectToOptionalConverter implements ConditionalGenericConverter {
|
||||
}
|
||||
else {
|
||||
Object target = this.conversionService.convert(source, sourceType, new GenericTypeDescriptor(targetType));
|
||||
return Optional.of(target);
|
||||
return Optional.ofNullable(target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* 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.
|
||||
@@ -22,9 +22,10 @@ import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A variation of {@link UUID#randomUUID()} that uses {@link SecureRandom} only for the
|
||||
* initial seed and {@link Random} thereafter. This provides better performance in
|
||||
* exchange for less securely random id's.
|
||||
* An {@link IdGenerator} that uses {@link SecureRandom} for the initial seed and
|
||||
* {@link Random} thereafter, instead of calling {@link UUID#randomUUID()} every
|
||||
* time as {@link org.springframework.util.JdkIdGenerator JdkIdGenerator} does.
|
||||
* This provides a better balance between securely random ids and performance.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Rob Winch
|
||||
@@ -43,8 +44,8 @@ public class AlternativeJdkIdGenerator implements IdGenerator {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public UUID generateId() {
|
||||
|
||||
byte[] randomBytes = new byte[16];
|
||||
this.random.nextBytes(randomBytes);
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* An {@link IdGenerator} that calls {@link java.util.UUID#randomUUID()}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.1.5
|
||||
*/
|
||||
public class JdkIdGenerator implements IdGenerator {
|
||||
|
||||
@Override
|
||||
public UUID generateId() {
|
||||
return UUID.randomUUID();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* A simple {@link IdGenerator} that starts at 1 and increments by 1 with each call.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.1.5
|
||||
*/
|
||||
public class SimpleIdGenerator implements IdGenerator {
|
||||
|
||||
private final AtomicLong mostSigBits = new AtomicLong(0);
|
||||
|
||||
private final AtomicLong leastSigBits = new AtomicLong(0);
|
||||
|
||||
|
||||
@Override
|
||||
public UUID generateId() {
|
||||
long leastSigBits = this.leastSigBits.incrementAndGet();
|
||||
if (leastSigBits == 0) {
|
||||
this.mostSigBits.incrementAndGet();
|
||||
}
|
||||
return new UUID(this.mostSigBits.get(), leastSigBits);
|
||||
}
|
||||
|
||||
}
|
||||
+19
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
@@ -27,15 +29,20 @@ import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
|
||||
/**
|
||||
* Implements the matches operator. Matches takes two operands. The first is a string and
|
||||
* the second is a java regex. It will return true when getValue() is called if the first
|
||||
* operand matches the regex.
|
||||
* Implements the matches operator. Matches takes two operands:
|
||||
* The first is a String and the second is a Java regex.
|
||||
* It will return {@code true} when {@link #getValue} is called
|
||||
* if the first operand matches the regex.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OperatorMatches extends Operator {
|
||||
|
||||
private final ConcurrentMap<String, Pattern> patternCache = new ConcurrentHashMap<String, Pattern>();
|
||||
|
||||
|
||||
public OperatorMatches(int pos, SpelNodeImpl... operands) {
|
||||
super("matches", pos, operands);
|
||||
}
|
||||
@@ -66,8 +73,14 @@ public class OperatorMatches extends Operator {
|
||||
}
|
||||
|
||||
try {
|
||||
Pattern pattern = Pattern.compile((String) right);
|
||||
Matcher matcher = pattern.matcher((String) left);
|
||||
String leftString = (String) left;
|
||||
String rightString = (String) right;
|
||||
Pattern pattern = this.patternCache.get(rightString);
|
||||
if (pattern == null) {
|
||||
pattern = Pattern.compile(rightString);
|
||||
this.patternCache.putIfAbsent(rightString, pattern);
|
||||
}
|
||||
Matcher matcher = pattern.matcher(leftString);
|
||||
return BooleanTypedValue.forValue(matcher.matches());
|
||||
}
|
||||
catch (PatternSyntaxException ex) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -70,16 +70,8 @@ import org.springframework.util.IdGenerator;
|
||||
*/
|
||||
public class MessageHeaders implements Map<String, Object>, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 7035068984263400920L;
|
||||
|
||||
private static final Log logger = LogFactory.getLog(MessageHeaders.class);
|
||||
|
||||
public static final UUID ID_VALUE_NONE = new UUID(0,0);
|
||||
|
||||
private static volatile IdGenerator idGenerator = null;
|
||||
|
||||
private static final IdGenerator defaultIdGenerator = new AlternativeJdkIdGenerator();
|
||||
|
||||
/**
|
||||
* The key for the Message ID. This is an automatically generated UUID and
|
||||
* should never be explicitly set in the header map <b>except</b> in the
|
||||
@@ -90,11 +82,20 @@ public class MessageHeaders implements Map<String, Object>, Serializable {
|
||||
|
||||
public static final String TIMESTAMP = "timestamp";
|
||||
|
||||
public static final String CONTENT_TYPE = "contentType";
|
||||
|
||||
public static final String REPLY_CHANNEL = "replyChannel";
|
||||
|
||||
public static final String ERROR_CHANNEL = "errorChannel";
|
||||
|
||||
public static final String CONTENT_TYPE = "contentType";
|
||||
|
||||
private static final long serialVersionUID = 7035068984263400920L;
|
||||
|
||||
private static final Log logger = LogFactory.getLog(MessageHeaders.class);
|
||||
|
||||
private static final IdGenerator defaultIdGenerator = new AlternativeJdkIdGenerator();
|
||||
|
||||
private static volatile IdGenerator idGenerator = null;
|
||||
|
||||
|
||||
private final Map<String, Object> headers;
|
||||
|
||||
+37
-42
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -32,10 +32,10 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link MessageConverter} implementations including support for
|
||||
* common properties and a partial implementation of the conversion methods mainly to
|
||||
* check if the converter supports the conversion based on the payload class and MIME
|
||||
* type.
|
||||
* Abstract base class for {@link MessageConverter} implementations including support
|
||||
* for common properties and a partial implementation of the conversion methods,
|
||||
* mainly to check if the converter supports the conversion based on the payload class
|
||||
* and MIME type.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
@@ -68,7 +68,7 @@ public abstract class AbstractMessageConverter implements MessageConverter {
|
||||
* @param supportedMimeTypes the supported MIME types
|
||||
*/
|
||||
protected AbstractMessageConverter(Collection<MimeType> supportedMimeTypes) {
|
||||
Assert.notNull(supportedMimeTypes, "SupportedMimeTypes must not be null");
|
||||
Assert.notNull(supportedMimeTypes, "supportedMimeTypes must not be null");
|
||||
this.supportedMimeTypes = new ArrayList<MimeType>(supportedMimeTypes);
|
||||
}
|
||||
|
||||
@@ -83,13 +83,11 @@ public abstract class AbstractMessageConverter implements MessageConverter {
|
||||
/**
|
||||
* Configure the {@link ContentTypeResolver} to use to resolve the content
|
||||
* type of an input message.
|
||||
* <p>
|
||||
* Note that if no resolver is configured, then
|
||||
* <p>Note that if no resolver is configured, then
|
||||
* {@link #setStrictContentTypeMatch(boolean) strictContentTypeMatch} should
|
||||
* be left as {@code false} (the default) or otherwise this converter will
|
||||
* ignore all messages.
|
||||
* <p>
|
||||
* By default, a {@code DefaultContentTypeResolver} instance is used.
|
||||
* <p>By default, a {@code DefaultContentTypeResolver} instance is used.
|
||||
*/
|
||||
public void setContentTypeResolver(ContentTypeResolver resolver) {
|
||||
this.contentTypeResolver = resolver;
|
||||
@@ -106,20 +104,17 @@ public abstract class AbstractMessageConverter implements MessageConverter {
|
||||
* Whether this converter should convert messages for which no content type
|
||||
* could be resolved through the configured
|
||||
* {@link org.springframework.messaging.converter.ContentTypeResolver}.
|
||||
* A converter can configured to be strict only when a
|
||||
* {@link #setContentTypeResolver(ContentTypeResolver) contentTypeResolver}
|
||||
* is configured and the list of {@link #getSupportedMimeTypes() supportedMimeTypes}
|
||||
* is not be empty.
|
||||
*
|
||||
* then requires the content type of a message to be resolved
|
||||
*
|
||||
* When set to true, #supportsMimeType(MessageHeaders) will return false if the
|
||||
* contentTypeResolver is not defined or if no content-type header is present.
|
||||
* <p>A converter can configured to be strict only when a
|
||||
* {@link #setContentTypeResolver contentTypeResolver} is configured and the
|
||||
* list of {@link #getSupportedMimeTypes() supportedMimeTypes} is not be empty.
|
||||
* <p>When this flag is set to {@code true}, {@link #supportsMimeType(MessageHeaders)}
|
||||
* will return {@code false} if the {@link #setContentTypeResolver contentTypeResolver}
|
||||
* is not defined or if no content-type header is present.
|
||||
*/
|
||||
public void setStrictContentTypeMatch(boolean strictContentTypeMatch) {
|
||||
if (strictContentTypeMatch) {
|
||||
Assert.notEmpty(getSupportedMimeTypes(), "Strict match requires non-empty list of supported mime types.");
|
||||
Assert.notNull(getContentTypeResolver(), "Strict match requires ContentTypeResolver.");
|
||||
Assert.notEmpty(getSupportedMimeTypes(), "Strict match requires non-empty list of supported mime types");
|
||||
Assert.notNull(getContentTypeResolver(), "Strict match requires ContentTypeResolver");
|
||||
}
|
||||
this.strictContentTypeMatch = strictContentTypeMatch;
|
||||
}
|
||||
@@ -166,14 +161,6 @@ public abstract class AbstractMessageConverter implements MessageConverter {
|
||||
return (!mimeTypes.isEmpty() ? mimeTypes.get(0) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given class is supported by this converter.
|
||||
* @param clazz the class to test for support
|
||||
* @return {@code true} if supported; {@code false} otherwise
|
||||
*/
|
||||
protected abstract boolean supports(Class<?> clazz);
|
||||
|
||||
|
||||
@Override
|
||||
public final Object fromMessage(Message<?> message, Class<?> targetClass) {
|
||||
if (!canConvertFrom(message, targetClass)) {
|
||||
@@ -186,14 +173,8 @@ public abstract class AbstractMessageConverter implements MessageConverter {
|
||||
return (supports(targetClass) && supportsMimeType(message.getHeaders()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the message payload from serialized form to an Object.
|
||||
*/
|
||||
public abstract Object convertFromInternal(Message<?> message, Class<?> targetClass);
|
||||
|
||||
@Override
|
||||
public final Message<?> toMessage(Object payload, MessageHeaders headers) {
|
||||
|
||||
if (!canConvertTo(payload, headers)) {
|
||||
return null;
|
||||
}
|
||||
@@ -218,15 +199,10 @@ public abstract class AbstractMessageConverter implements MessageConverter {
|
||||
}
|
||||
|
||||
protected boolean canConvertTo(Object payload, MessageHeaders headers) {
|
||||
Class<?> clazz = (payload != null) ? payload.getClass() : null;
|
||||
Class<?> clazz = (payload != null ? payload.getClass() : null);
|
||||
return (supports(clazz) && supportsMimeType(headers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the payload object to serialized form.
|
||||
*/
|
||||
public abstract Object convertToInternal(Object payload, MessageHeaders headers);
|
||||
|
||||
protected boolean supportsMimeType(MessageHeaders headers) {
|
||||
if (getSupportedMimeTypes().isEmpty()) {
|
||||
return true;
|
||||
@@ -249,7 +225,26 @@ public abstract class AbstractMessageConverter implements MessageConverter {
|
||||
}
|
||||
|
||||
protected MimeType getMimeType(MessageHeaders headers) {
|
||||
return (this.contentTypeResolver != null) ? this.contentTypeResolver.resolve(headers) : null;
|
||||
return (this.contentTypeResolver != null ? this.contentTypeResolver.resolve(headers) : null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Whether the given class is supported by this converter.
|
||||
* @param clazz the class to test for support
|
||||
* @return {@code true} if supported; {@code false} otherwise
|
||||
*/
|
||||
protected abstract boolean supports(Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Convert the message payload from serialized form to an Object.
|
||||
*/
|
||||
public abstract Object convertFromInternal(Message<?> message, Class<?> targetClass);
|
||||
|
||||
|
||||
/**
|
||||
* Convert the payload object to serialized form.
|
||||
*/
|
||||
public abstract Object convertToInternal(Object payload, MessageHeaders headers);
|
||||
|
||||
}
|
||||
|
||||
+23
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -21,6 +21,7 @@ import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonEncoding;
|
||||
@@ -51,6 +52,7 @@ import org.springframework.util.MimeType;
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
* @author Sebastien Deleuze
|
||||
* @since 4.0
|
||||
*/
|
||||
public class MappingJackson2MessageConverter extends AbstractMessageConverter {
|
||||
@@ -65,8 +67,28 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter {
|
||||
private Boolean prettyPrint;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a {@code MappingJackson2MessageConverter} supporting
|
||||
* the {@code application/json} MIME type.
|
||||
*/
|
||||
public MappingJackson2MessageConverter() {
|
||||
super(new MimeType("application", "json", Charset.forName("UTF-8")));
|
||||
initObjectMapper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a {@code MappingJackson2MessageConverter} supporting
|
||||
* one or more custom MIME types.
|
||||
* @param supportedMimeTypes the supported MIME types
|
||||
* @since 4.1.5
|
||||
*/
|
||||
public MappingJackson2MessageConverter(MimeType... supportedMimeTypes) {
|
||||
super(Arrays.asList(supportedMimeTypes));
|
||||
initObjectMapper();
|
||||
}
|
||||
|
||||
|
||||
private void initObjectMapper() {
|
||||
this.objectMapper = new ObjectMapper();
|
||||
this.objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
|
||||
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -42,23 +42,23 @@ public abstract class AbstractMethodArgumentResolutionException extends Messagin
|
||||
|
||||
/**
|
||||
* Create a new instance providing the invalid {@code MethodParameter} and
|
||||
* a prepared description. Sub-classes should prepend the description with
|
||||
* a prepared description. Subclasses should prepend the description with
|
||||
* the help of {@link #getMethodParamMessage(org.springframework.core.MethodParameter)}.
|
||||
*/
|
||||
protected AbstractMethodArgumentResolutionException(Message<?> message,
|
||||
MethodParameter parameter, String description) {
|
||||
|
||||
protected AbstractMethodArgumentResolutionException(Message<?> message, MethodParameter param, String description) {
|
||||
super(message, description);
|
||||
this.parameter = parameter;
|
||||
this.parameter = param;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the MethodParameter that was rejected.
|
||||
*/
|
||||
public MethodParameter getMethodParameter() {
|
||||
public final MethodParameter getMethodParameter() {
|
||||
return this.parameter;
|
||||
}
|
||||
|
||||
|
||||
protected static String getMethodParamMessage(MethodParameter param) {
|
||||
return new StringBuilder("Could not resolve method parameter at index ")
|
||||
.append(param.getParameterIndex()).append(" in method: ")
|
||||
|
||||
+6
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -49,22 +49,21 @@ public class MethodArgumentNotValidException extends AbstractMethodArgumentResol
|
||||
public MethodArgumentNotValidException(Message<?> message, MethodParameter parameter,
|
||||
BindingResult bindingResult) {
|
||||
|
||||
super(message, parameter, getMethodParamMessage(parameter) +
|
||||
getValidationErrorMessage(parameter, bindingResult));
|
||||
|
||||
super(message, parameter, getMethodParamMessage(parameter) + getValidationErrorMessage(bindingResult));
|
||||
this.bindingResult = bindingResult;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the BindingResult if the failure is validation-related or {@code null}.
|
||||
* Return the BindingResult if the failure is validation-related,
|
||||
* or {@code null} if none.
|
||||
*/
|
||||
public BindingResult getBindingResult() {
|
||||
public final BindingResult getBindingResult() {
|
||||
return this.bindingResult;
|
||||
}
|
||||
|
||||
|
||||
private static String getValidationErrorMessage(MethodParameter parameter, BindingResult bindingResult) {
|
||||
private static String getValidationErrorMessage(BindingResult bindingResult) {
|
||||
if (bindingResult != null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(", with ").append(bindingResult.getErrorCount()).append(" error(s): ");
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -29,11 +29,11 @@ import org.springframework.messaging.Message;
|
||||
@SuppressWarnings("serial")
|
||||
public class MethodArgumentTypeMismatchException extends AbstractMethodArgumentResolutionException {
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance with the invalid {@code MethodParameter}.
|
||||
*/
|
||||
public MethodArgumentTypeMismatchException(Message<?> message, MethodParameter parameter, String description) {
|
||||
super(message, parameter, getMethodParamMessage(parameter) + description);
|
||||
public MethodArgumentTypeMismatchException(Message<?> message, MethodParameter param, String description) {
|
||||
super(message, param, getMethodParamMessage(param) + description);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+11
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -144,6 +144,16 @@ public class PayloadArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the payload if applicable.
|
||||
* <p>The default implementation checks for {@code @javax.validation.Valid},
|
||||
* Spring's {@link org.springframework.validation.annotation.Validated},
|
||||
* and custom annotations whose name starts with "Valid".
|
||||
* @param message the currently processed message
|
||||
* @param parameter the method parameter
|
||||
* @param target the target payload object
|
||||
* @throws MethodArgumentNotValidException in case of binding errors
|
||||
*/
|
||||
protected void validate(Message<?> message, MethodParameter parameter, Object target) {
|
||||
if (this.validator == null) {
|
||||
return;
|
||||
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* 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.
|
||||
@@ -20,8 +20,8 @@ import org.springframework.core.MethodParameter;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* Strategy interface for resolving method parameters into argument values in
|
||||
* the context of a given {@link Message}.
|
||||
* Strategy interface for resolving method parameters into argument values
|
||||
* in the context of a given {@link Message}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
@@ -39,12 +39,12 @@ public interface HandlerMethodArgumentResolver {
|
||||
|
||||
/**
|
||||
* Resolves a method parameter into an argument value from a given message.
|
||||
* @param parameter the method parameter to resolve. This parameter must
|
||||
* have previously been passed to
|
||||
* @param parameter the method parameter to resolve.
|
||||
* This parameter must have previously been passed to
|
||||
* {@link #supportsParameter(org.springframework.core.MethodParameter)}
|
||||
* and it must have returned {@code true}
|
||||
* which must have returned {@code true}.
|
||||
* @param message the currently processed message
|
||||
* @return the resolved argument value, or {@code null}.
|
||||
* @return the resolved argument value, or {@code null}
|
||||
* @throws Exception in case of errors with the preparation of argument values
|
||||
*/
|
||||
Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception;
|
||||
|
||||
+9
-9
@@ -188,7 +188,7 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
|
||||
}
|
||||
|
||||
/**
|
||||
* The configured {@link ConversionService}.
|
||||
* Return the configured {@link ConversionService}.
|
||||
*/
|
||||
public ConversionService getConversionService() {
|
||||
return this.conversionService;
|
||||
@@ -206,14 +206,14 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the PathMatcher implementation to use for matching destinations
|
||||
* Return the PathMatcher implementation to use for matching destinations.
|
||||
*/
|
||||
public PathMatcher getPathMatcher() {
|
||||
return this.pathMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* The configured Validator instance
|
||||
* Return the configured Validator instance.
|
||||
*/
|
||||
public Validator getValidator() {
|
||||
return this.validator;
|
||||
@@ -343,17 +343,17 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
|
||||
@Override
|
||||
protected SimpMessageMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
|
||||
MessageMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, MessageMapping.class);
|
||||
MessageMapping messageAnnot = AnnotationUtils.findAnnotation(method, MessageMapping.class);
|
||||
if (messageAnnot != null) {
|
||||
SimpMessageMappingInfo result = createMessageMappingCondition(messageAnnot);
|
||||
MessageMapping messageAnnotation = AnnotationUtils.findAnnotation(method, MessageMapping.class);
|
||||
if (messageAnnotation != null) {
|
||||
SimpMessageMappingInfo result = createMessageMappingCondition(messageAnnotation);
|
||||
if (typeAnnotation != null) {
|
||||
result = createMessageMappingCondition(typeAnnotation).combine(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
SubscribeMapping subsribeAnnotation = AnnotationUtils.findAnnotation(method, SubscribeMapping.class);
|
||||
if (subsribeAnnotation != null) {
|
||||
SimpMessageMappingInfo result = createSubscribeCondition(subsribeAnnotation);
|
||||
SubscribeMapping subscribeAnnotation = AnnotationUtils.findAnnotation(method, SubscribeMapping.class);
|
||||
if (subscribeAnnotation != null) {
|
||||
SimpMessageMappingInfo result = createSubscribeCondition(subscribeAnnotation);
|
||||
if (typeAnnotation != null) {
|
||||
result = createMessageMappingCondition(typeAnnotation).combine(result);
|
||||
}
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -32,6 +32,7 @@ import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.PathMatcher;
|
||||
|
||||
|
||||
/**
|
||||
* A default, simple in-memory implementation of {@link SubscriptionRegistry}.
|
||||
*
|
||||
@@ -165,8 +166,8 @@ public class DefaultSubscriptionRegistry extends AbstractSubscriptionRegistry {
|
||||
|
||||
public void addSubscriptions(String destination, MultiValueMap<String, String> subscriptions) {
|
||||
synchronized (this.updateCache) {
|
||||
this.updateCache.put(destination, subscriptions);
|
||||
this.accessCache.put(destination, new LinkedMultiValueMap<String, String>(subscriptions));
|
||||
this.updateCache.put(destination, new LinkedMultiValueMap<String, String>(subscriptions));
|
||||
this.accessCache.put(destination, subscriptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -68,7 +68,6 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler {
|
||||
|
||||
/**
|
||||
* Configure a custom SubscriptionRegistry to use for storing subscriptions.
|
||||
*
|
||||
* <p><strong>Note</strong> that when a custom PathMatcher is configured via
|
||||
* {@link #setPathMatcher}, if the custom registry is not an instance of
|
||||
* {@link DefaultSubscriptionRegistry}, the provided PathMatcher is not used
|
||||
@@ -102,9 +101,8 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a {@link MessageHeaderInitializer} to apply to the headers of all
|
||||
* messages sent to the client outbound channel.
|
||||
*
|
||||
* Configure a {@link MessageHeaderInitializer} to apply to the headers
|
||||
* of all messages sent to the client outbound channel.
|
||||
* <p>By default this property is not set.
|
||||
*/
|
||||
public void setHeaderInitializer(MessageHeaderInitializer headerInitializer) {
|
||||
@@ -112,7 +110,7 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the configured header initializer.
|
||||
* Return the configured header initializer.
|
||||
*/
|
||||
public MessageHeaderInitializer getHeaderInitializer() {
|
||||
return this.headerInitializer;
|
||||
@@ -131,7 +129,6 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler {
|
||||
|
||||
@Override
|
||||
protected void handleMessageInternal(Message<?> message) {
|
||||
|
||||
MessageHeaders headers = message.getHeaders();
|
||||
SimpMessageType messageType = SimpMessageHeaderAccessor.getMessageType(headers);
|
||||
String destination = SimpMessageHeaderAccessor.getDestination(headers);
|
||||
@@ -197,7 +194,7 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler {
|
||||
|
||||
protected void sendMessageToSubscribers(String destination, Message<?> message) {
|
||||
MultiValueMap<String,String> subscriptions = this.subscriptionRegistry.findSubscriptions(message);
|
||||
if ((subscriptions.size() > 0) && logger.isDebugEnabled()) {
|
||||
if (!subscriptions.isEmpty() && logger.isDebugEnabled()) {
|
||||
logger.debug("Broadcasting to " + subscriptions.size() + " sessions.");
|
||||
}
|
||||
for (String sessionId : subscriptions.keySet()) {
|
||||
|
||||
+5
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* 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.
|
||||
@@ -45,9 +45,11 @@ public interface SubscriptionRegistry {
|
||||
void unregisterAllSubscriptions(String sessionId);
|
||||
|
||||
/**
|
||||
* Find all subscriptions that should receive the given message.
|
||||
* Find all subscriptions that should receive the given message. The map
|
||||
* returned is safe to iterate and will never be modified.
|
||||
* @param message the message
|
||||
* @return a {@link MultiValueMap} from sessionId to subscriptionId's, possibly empty.
|
||||
* @return a {@code MultiValueMap} with sessionId-subscriptionId pairs,
|
||||
* possibly empty.
|
||||
*/
|
||||
MultiValueMap<String, String> findSubscriptions(Message<?> message);
|
||||
|
||||
|
||||
+24
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -418,11 +418,31 @@ public class MessageHeaderAccessor {
|
||||
// Specific header accessors
|
||||
|
||||
public UUID getId() {
|
||||
return (UUID) getHeader(MessageHeaders.ID);
|
||||
Object value = getHeader(MessageHeaders.ID);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return (value instanceof UUID ? (UUID) value : UUID.fromString(value.toString()));
|
||||
}
|
||||
|
||||
public Long getTimestamp() {
|
||||
return (Long) getHeader(MessageHeaders.TIMESTAMP);
|
||||
Object value = getHeader(MessageHeaders.TIMESTAMP);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return (value instanceof Long ? (Long) value : Long.parseLong(value.toString()));
|
||||
}
|
||||
|
||||
public void setContentType(MimeType contentType) {
|
||||
setHeader(MessageHeaders.CONTENT_TYPE, contentType);
|
||||
}
|
||||
|
||||
public MimeType getContentType() {
|
||||
Object value = getHeader(MessageHeaders.CONTENT_TYPE);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return (value instanceof MimeType ? (MimeType) value : MimeType.valueOf(value.toString()));
|
||||
}
|
||||
|
||||
public void setReplyChannelName(String replyChannelName) {
|
||||
@@ -449,14 +469,6 @@ public class MessageHeaderAccessor {
|
||||
return getHeader(MessageHeaders.ERROR_CHANNEL);
|
||||
}
|
||||
|
||||
public void setContentType(MimeType contentType) {
|
||||
setHeader(MessageHeaders.CONTENT_TYPE, contentType);
|
||||
}
|
||||
|
||||
public MimeType getContentType() {
|
||||
return (MimeType) getHeader(MessageHeaders.CONTENT_TYPE);
|
||||
}
|
||||
|
||||
|
||||
// Log message stuff
|
||||
|
||||
@@ -508,7 +520,7 @@ public class MessageHeaderAccessor {
|
||||
|
||||
protected String getDetailedPayloadLogMessage(Object payload) {
|
||||
if (payload instanceof String) {
|
||||
return " payload=" + ((String) payload);
|
||||
return " payload=" + payload;
|
||||
}
|
||||
else if (payload instanceof byte[]) {
|
||||
byte[] bytes = (byte[]) payload;
|
||||
|
||||
+40
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -22,7 +22,7 @@ import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
@@ -30,31 +30,51 @@ import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link org.springframework.messaging.converter.MappingJackson2MessageConverter}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
public class MappingJackson2MessageConverterTests {
|
||||
|
||||
private static Charset UTF_8 = Charset.forName("UTF-8");
|
||||
|
||||
private MappingJackson2MessageConverter converter;
|
||||
|
||||
@Test
|
||||
public void defaultConstructor() {
|
||||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
|
||||
assertThat(converter.getSupportedMimeTypes(), contains(new MimeType("application", "json", UTF_8)));
|
||||
assertFalse(converter.getObjectMapper().getDeserializationConfig().isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.converter = new MappingJackson2MessageConverter();
|
||||
@Test // SPR-12724
|
||||
public void mimetypeParametrizedConstructor() {
|
||||
MimeType mimetype = new MimeType("application", "xml", UTF_8);
|
||||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(mimetype);
|
||||
assertThat(converter.getSupportedMimeTypes(), contains(mimetype));
|
||||
assertFalse(converter.getObjectMapper().getDeserializationConfig().isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
|
||||
}
|
||||
|
||||
@Test // SPR-12724
|
||||
public void mimetypesParametrizedConstructor() {
|
||||
MimeType jsonMimetype = new MimeType("application", "json", UTF_8);
|
||||
MimeType xmlMimetype = new MimeType("application", "xml", UTF_8);
|
||||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(jsonMimetype, xmlMimetype);
|
||||
assertThat(converter.getSupportedMimeTypes(), contains(jsonMimetype, xmlMimetype));
|
||||
assertFalse(converter.getObjectMapper().getDeserializationConfig().isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMessage() throws Exception {
|
||||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
|
||||
String payload = "{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"],"
|
||||
+ "\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}";
|
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(UTF_8)).build();
|
||||
MyBean actual = (MyBean) this.converter.fromMessage(message, MyBean.class);
|
||||
MyBean actual = (MyBean) converter.fromMessage(message, MyBean.class);
|
||||
|
||||
assertEquals("Foo", actual.getString());
|
||||
assertEquals(42, actual.getNumber());
|
||||
@@ -66,11 +86,12 @@ public class MappingJackson2MessageConverterTests {
|
||||
|
||||
@Test
|
||||
public void fromMessageUntyped() throws Exception {
|
||||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
|
||||
String payload = "{\"bytes\":\"AQI=\",\"array\":[\"Foo\",\"Bar\"],"
|
||||
+ "\"number\":42,\"string\":\"Foo\",\"bool\":true,\"fraction\":42.0}";
|
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(UTF_8)).build();
|
||||
@SuppressWarnings("unchecked")
|
||||
HashMap<String, Object> actual = (HashMap<String, Object>) this.converter.fromMessage(message, HashMap.class);
|
||||
HashMap<String, Object> actual = (HashMap<String, Object>) converter.fromMessage(message, HashMap.class);
|
||||
|
||||
assertEquals("Foo", actual.get("string"));
|
||||
assertEquals(42, actual.get("number"));
|
||||
@@ -82,21 +103,24 @@ public class MappingJackson2MessageConverterTests {
|
||||
|
||||
@Test(expected = MessageConversionException.class)
|
||||
public void fromMessageInvalidJson() throws Exception {
|
||||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
|
||||
String payload = "FooBar";
|
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(UTF_8)).build();
|
||||
this.converter.fromMessage(message, MyBean.class);
|
||||
converter.fromMessage(message, MyBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMessageValidJsonWithUnknownProperty() throws IOException {
|
||||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
|
||||
String payload = "{\"string\":\"string\",\"unknownProperty\":\"value\"}";
|
||||
Message<?> message = MessageBuilder.withPayload(payload.getBytes(UTF_8)).build();
|
||||
MyBean myBean = (MyBean)this.converter.fromMessage(message, MyBean.class);
|
||||
MyBean myBean = (MyBean)converter.fromMessage(message, MyBean.class);
|
||||
assertEquals("string", myBean.getString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toMessage() throws Exception {
|
||||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
|
||||
MyBean payload = new MyBean();
|
||||
payload.setString("Foo");
|
||||
payload.setNumber(42);
|
||||
@@ -105,7 +129,7 @@ public class MappingJackson2MessageConverterTests {
|
||||
payload.setBool(true);
|
||||
payload.setBytes(new byte[]{0x1, 0x2});
|
||||
|
||||
Message<?> message = this.converter.toMessage(payload, null);
|
||||
Message<?> message = converter.toMessage(payload, null);
|
||||
String actual = new String((byte[]) message.getPayload(), UTF_8);
|
||||
|
||||
assertTrue(actual.contains("\"string\":\"Foo\""));
|
||||
@@ -120,13 +144,14 @@ public class MappingJackson2MessageConverterTests {
|
||||
|
||||
@Test
|
||||
public void toMessageUtf16() {
|
||||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
|
||||
Charset utf16 = Charset.forName("UTF-16BE");
|
||||
MimeType contentType = new MimeType("application", "json", utf16);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put(MessageHeaders.CONTENT_TYPE, contentType);
|
||||
MessageHeaders headers = new MessageHeaders(map);
|
||||
String payload = "H\u00e9llo W\u00f6rld";
|
||||
Message<?> message = this.converter.toMessage(payload, headers);
|
||||
Message<?> message = converter.toMessage(payload, headers);
|
||||
|
||||
assertEquals("\"" + payload + "\"", new String((byte[]) message.getPayload(), utf16));
|
||||
assertEquals(contentType, message.getHeaders().get(MessageHeaders.CONTENT_TYPE));
|
||||
@@ -134,7 +159,8 @@ public class MappingJackson2MessageConverterTests {
|
||||
|
||||
@Test
|
||||
public void toMessageUtf16String() {
|
||||
this.converter.setSerializedPayloadClass(String.class);
|
||||
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
|
||||
converter.setSerializedPayloadClass(String.class);
|
||||
|
||||
Charset utf16 = Charset.forName("UTF-16BE");
|
||||
MimeType contentType = new MimeType("application", "json", utf16);
|
||||
@@ -142,7 +168,7 @@ public class MappingJackson2MessageConverterTests {
|
||||
map.put(MessageHeaders.CONTENT_TYPE, contentType);
|
||||
MessageHeaders headers = new MessageHeaders(map);
|
||||
String payload = "H\u00e9llo W\u00f6rld";
|
||||
Message<?> message = this.converter.toMessage(payload, headers);
|
||||
Message<?> message = converter.toMessage(payload, headers);
|
||||
|
||||
assertEquals("\"" + payload + "\"", message.getPayload());
|
||||
assertEquals(contentType, message.getHeaders().get(MessageHeaders.CONTENT_TYPE));
|
||||
|
||||
+22
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.messaging.simp.broker;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -31,6 +32,7 @@ import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
||||
/**
|
||||
* Test fixture for {@link org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry}.
|
||||
*
|
||||
@@ -266,7 +268,7 @@ public class DefaultSubscriptionRegistryTests {
|
||||
|
||||
MultiValueMap<String, String> actual = this.registry.findSubscriptions(message(dest));
|
||||
|
||||
assertEquals("Expected three elements " + actual, 2, actual.size());
|
||||
assertEquals("Expected two elements: " + actual, 2, actual.size());
|
||||
assertEquals(subscriptionIds, sort(actual.get(sessIds.get(1))));
|
||||
assertEquals(subscriptionIds, sort(actual.get(sessIds.get(2))));
|
||||
}
|
||||
@@ -310,7 +312,7 @@ public class DefaultSubscriptionRegistryTests {
|
||||
|
||||
MultiValueMap<String, String> actual = this.registry.findSubscriptions(message(dest));
|
||||
|
||||
assertEquals("Expected three elements " + actual, 1, actual.size());
|
||||
assertEquals("Expected one element: " + actual, 1, actual.size());
|
||||
assertEquals(subscriptionIds, sort(actual.get(sessIds.get(2))));
|
||||
}
|
||||
|
||||
@@ -326,6 +328,23 @@ public class DefaultSubscriptionRegistryTests {
|
||||
assertEquals("Expected no elements " + actual, 0, actual.size());
|
||||
}
|
||||
|
||||
// SPR-12665
|
||||
|
||||
@Test
|
||||
public void findSubscriptionsReturnsMapSafeToIterate() throws Exception {
|
||||
this.registry.registerSubscription(subscribeMessage("sess1", "1", "/foo"));
|
||||
this.registry.registerSubscription(subscribeMessage("sess2", "1", "/foo"));
|
||||
MultiValueMap<String, String> subscriptions = this.registry.findSubscriptions(message("/foo"));
|
||||
assertEquals(2, subscriptions.size());
|
||||
|
||||
Iterator iterator = subscriptions.entrySet().iterator();
|
||||
iterator.next();
|
||||
|
||||
this.registry.registerSubscription(subscribeMessage("sess3", "1", "/foo"));
|
||||
|
||||
iterator.next();
|
||||
// no ConcurrentModificationException
|
||||
}
|
||||
|
||||
private Message<?> subscribeMessage(String sessionId, String subscriptionId, String destination) {
|
||||
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.SUBSCRIBE);
|
||||
|
||||
+15
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.messaging.simp.broker;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@@ -84,12 +85,12 @@ public class SimpleBrokerMessageHandlerTests {
|
||||
this.messageHandler.handleMessage(createMessage("/bar", "message2"));
|
||||
|
||||
verify(this.clientOutboundChannel, times(6)).send(this.messageCaptor.capture());
|
||||
assertCapturedMessage("sess1", "sub1", "/foo");
|
||||
assertCapturedMessage("sess1", "sub2", "/foo");
|
||||
assertCapturedMessage("sess2", "sub1", "/foo");
|
||||
assertCapturedMessage("sess2", "sub2", "/foo");
|
||||
assertCapturedMessage("sess1", "sub3", "/bar");
|
||||
assertCapturedMessage("sess2", "sub3", "/bar");
|
||||
assertTrue(messageCaptured("sess1", "sub1", "/foo"));
|
||||
assertTrue(messageCaptured("sess1", "sub2", "/foo"));
|
||||
assertTrue(messageCaptured("sess2", "sub1", "/foo"));
|
||||
assertTrue(messageCaptured("sess2", "sub2", "/foo"));
|
||||
assertTrue(messageCaptured("sess1", "sub3", "/bar"));
|
||||
assertTrue(messageCaptured("sess2", "sub3", "/bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -124,9 +125,9 @@ public class SimpleBrokerMessageHandlerTests {
|
||||
assertEquals(sess1, SimpMessageHeaderAccessor.getSessionId(captured.getHeaders()));
|
||||
assertEquals("joe", SimpMessageHeaderAccessor.getUser(captured.getHeaders()).getName());
|
||||
|
||||
assertCapturedMessage(sess2, "sub1", "/foo");
|
||||
assertCapturedMessage(sess2, "sub2", "/foo");
|
||||
assertCapturedMessage(sess2, "sub3", "/bar");
|
||||
assertTrue(messageCaptured(sess2, "sub1", "/foo"));
|
||||
assertTrue(messageCaptured(sess2, "sub2", "/foo"));
|
||||
assertTrue(messageCaptured(sess2, "sub3", "/bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -149,7 +150,7 @@ public class SimpleBrokerMessageHandlerTests {
|
||||
}
|
||||
|
||||
|
||||
protected Message<String> createSubscriptionMessage(String sessionId, String subcriptionId, String destination) {
|
||||
private Message<String> createSubscriptionMessage(String sessionId, String subcriptionId, String destination) {
|
||||
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.SUBSCRIBE);
|
||||
headers.setSubscriptionId(subcriptionId);
|
||||
headers.setDestination(destination);
|
||||
@@ -157,20 +158,20 @@ public class SimpleBrokerMessageHandlerTests {
|
||||
return MessageBuilder.createMessage("", headers.getMessageHeaders());
|
||||
}
|
||||
|
||||
protected Message<String> createConnectMessage(String sessionId) {
|
||||
private Message<String> createConnectMessage(String sessionId) {
|
||||
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);
|
||||
headers.setSessionId(sessionId);
|
||||
headers.setUser(new TestPrincipal("joe"));
|
||||
return MessageBuilder.createMessage("", headers.getMessageHeaders());
|
||||
}
|
||||
|
||||
protected Message<String> createMessage(String destination, String payload) {
|
||||
private Message<String> createMessage(String destination, String payload) {
|
||||
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
|
||||
headers.setDestination(destination);
|
||||
return MessageBuilder.createMessage("", headers.getMessageHeaders());
|
||||
}
|
||||
|
||||
protected boolean assertCapturedMessage(String sessionId, String subcriptionId, String destination) {
|
||||
private boolean messageCaptured(String sessionId, String subcriptionId, String destination) {
|
||||
for (Message<?> message : this.messageCaptor.getAllValues()) {
|
||||
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(message);
|
||||
if (sessionId.equals(headers.getSessionId())) {
|
||||
|
||||
+91
-101
@@ -68,35 +68,47 @@ public class MergedContextConfiguration implements Serializable {
|
||||
private static final long serialVersionUID = -3290560718464957422L;
|
||||
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
|
||||
private static final Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> EMPTY_INITIALIZER_CLASSES = //
|
||||
Collections.<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> emptySet();
|
||||
|
||||
private static final Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> EMPTY_INITIALIZER_CLASSES =
|
||||
Collections.<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> emptySet();
|
||||
|
||||
|
||||
private final Class<?> testClass;
|
||||
|
||||
private final String[] locations;
|
||||
|
||||
private final Class<?>[] classes;
|
||||
|
||||
private final Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses;
|
||||
|
||||
private final String[] activeProfiles;
|
||||
|
||||
private final String[] propertySourceLocations;
|
||||
|
||||
private final String[] propertySourceProperties;
|
||||
|
||||
private final ContextLoader contextLoader;
|
||||
|
||||
private final CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate;
|
||||
|
||||
private final MergedContextConfiguration parent;
|
||||
|
||||
|
||||
private static String[] processStrings(String[] array) {
|
||||
return array == null ? EMPTY_STRING_ARRAY : array;
|
||||
return (array != null ? array : EMPTY_STRING_ARRAY);
|
||||
}
|
||||
|
||||
private static Class<?>[] processClasses(Class<?>[] classes) {
|
||||
return classes == null ? EMPTY_CLASS_ARRAY : classes;
|
||||
return (classes != null ? classes : EMPTY_CLASS_ARRAY);
|
||||
}
|
||||
|
||||
private static Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> processContextInitializerClasses(
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses) {
|
||||
return contextInitializerClasses == null ? EMPTY_INITIALIZER_CLASSES
|
||||
: Collections.unmodifiableSet(contextInitializerClasses);
|
||||
|
||||
return (contextInitializerClasses != null ?
|
||||
Collections.unmodifiableSet(contextInitializerClasses) : EMPTY_INITIALIZER_CLASSES);
|
||||
}
|
||||
|
||||
private static String[] processActiveProfiles(String[] activeProfiles) {
|
||||
@@ -117,15 +129,15 @@ public class MergedContextConfiguration implements Serializable {
|
||||
* loader or "null" if the supplied loaded is {@code null}.
|
||||
*/
|
||||
protected static String nullSafeToString(ContextLoader contextLoader) {
|
||||
return contextLoader == null ? "null" : contextLoader.getClass().getName();
|
||||
return (contextLoader != null ? contextLoader.getClass().getName() : "null");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code MergedContextConfiguration} instance for the
|
||||
* supplied parameters.
|
||||
* <p>Delegates to
|
||||
* {@link #MergedContextConfiguration(Class, String[], Class[], Set, String[], String[], String[], ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)}.
|
||||
*
|
||||
* @param testClass the test class for which the configuration was merged
|
||||
* @param locations the merged context resource locations
|
||||
* @param classes the merged annotated classes
|
||||
@@ -134,6 +146,7 @@ public class MergedContextConfiguration implements Serializable {
|
||||
*/
|
||||
public MergedContextConfiguration(Class<?> testClass, String[] locations, Class<?>[] classes,
|
||||
String[] activeProfiles, ContextLoader contextLoader) {
|
||||
|
||||
this(testClass, locations, classes, null, activeProfiles, contextLoader);
|
||||
}
|
||||
|
||||
@@ -142,7 +155,6 @@ public class MergedContextConfiguration implements Serializable {
|
||||
* supplied parameters.
|
||||
* <p>Delegates to
|
||||
* {@link #MergedContextConfiguration(Class, String[], Class[], Set, String[], String[], String[], ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)}.
|
||||
*
|
||||
* @param testClass the test class for which the configuration was merged
|
||||
* @param locations the merged context resource locations
|
||||
* @param classes the merged annotated classes
|
||||
@@ -151,12 +163,10 @@ public class MergedContextConfiguration implements Serializable {
|
||||
* @param contextLoader the resolved {@code ContextLoader}
|
||||
* @see #MergedContextConfiguration(Class, String[], Class[], Set, String[], ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)
|
||||
*/
|
||||
public MergedContextConfiguration(
|
||||
Class<?> testClass,
|
||||
String[] locations,
|
||||
Class<?>[] classes,
|
||||
public MergedContextConfiguration(Class<?> testClass, String[] locations, Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
|
||||
String[] activeProfiles, ContextLoader contextLoader) {
|
||||
|
||||
this(testClass, locations, classes, contextInitializerClasses, activeProfiles, contextLoader, null, null);
|
||||
}
|
||||
|
||||
@@ -165,7 +175,6 @@ public class MergedContextConfiguration implements Serializable {
|
||||
* supplied parameters.
|
||||
* <p>Delegates to
|
||||
* {@link #MergedContextConfiguration(Class, String[], Class[], Set, String[], String[], String[], ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)}.
|
||||
*
|
||||
* @param testClass the test class for which the configuration was merged
|
||||
* @param locations the merged context resource locations
|
||||
* @param classes the merged annotated classes
|
||||
@@ -177,13 +186,11 @@ public class MergedContextConfiguration implements Serializable {
|
||||
* @param parent the parent configuration or {@code null} if there is no parent
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public MergedContextConfiguration(
|
||||
Class<?> testClass,
|
||||
String[] locations,
|
||||
Class<?>[] classes,
|
||||
public MergedContextConfiguration(Class<?> testClass, String[] locations, Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
|
||||
String[] activeProfiles, ContextLoader contextLoader,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parent) {
|
||||
|
||||
this(testClass, locations, classes, contextInitializerClasses, activeProfiles, null, null, contextLoader,
|
||||
cacheAwareContextLoaderDelegate, parent);
|
||||
}
|
||||
@@ -203,7 +210,6 @@ public class MergedContextConfiguration implements Serializable {
|
||||
/**
|
||||
* Create a new {@code MergedContextConfiguration} instance for the
|
||||
* supplied parameters.
|
||||
*
|
||||
* <p>If a {@code null} value is supplied for {@code locations},
|
||||
* {@code classes}, {@code activeProfiles}, {@code propertySourceLocations},
|
||||
* or {@code propertySourceProperties} an empty array will be stored instead.
|
||||
@@ -211,7 +217,6 @@ public class MergedContextConfiguration implements Serializable {
|
||||
* {@code contextInitializerClasses} an empty set will be stored instead.
|
||||
* Furthermore, active profiles will be sorted, and duplicate profiles
|
||||
* will be removed.
|
||||
*
|
||||
* @param testClass the test class for which the configuration was merged
|
||||
* @param locations the merged context resource locations
|
||||
* @param classes the merged annotated classes
|
||||
@@ -225,14 +230,12 @@ public class MergedContextConfiguration implements Serializable {
|
||||
* @param parent the parent configuration or {@code null} if there is no parent
|
||||
* @since 4.1
|
||||
*/
|
||||
public MergedContextConfiguration(
|
||||
Class<?> testClass,
|
||||
String[] locations,
|
||||
Class<?>[] classes,
|
||||
public MergedContextConfiguration(Class<?> testClass, String[] locations, Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
|
||||
String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties,
|
||||
ContextLoader contextLoader, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate,
|
||||
MergedContextConfiguration parent) {
|
||||
|
||||
this.testClass = testClass;
|
||||
this.locations = processStrings(locations);
|
||||
this.classes = processClasses(classes);
|
||||
@@ -245,11 +248,13 @@ public class MergedContextConfiguration implements Serializable {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the {@linkplain Class test class} associated with this {@code MergedContextConfiguration}.
|
||||
* Get the {@linkplain Class test class} associated with this
|
||||
* {@code MergedContextConfiguration}.
|
||||
*/
|
||||
public Class<?> getTestClass() {
|
||||
return testClass;
|
||||
return this.testClass;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,20 +264,19 @@ public class MergedContextConfiguration implements Serializable {
|
||||
* files or Groovy scripts.
|
||||
*/
|
||||
public String[] getLocations() {
|
||||
return locations;
|
||||
return this.locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the merged annotated classes for the {@linkplain #getTestClass() test class}.
|
||||
*/
|
||||
public Class<?>[] getClasses() {
|
||||
return classes;
|
||||
return this.classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this {@code MergedContextConfiguration} instance has
|
||||
* path-based context resource locations.
|
||||
*
|
||||
* @return {@code true} if the {@link #getLocations() locations} array is not empty
|
||||
* @since 4.0.4
|
||||
* @see #hasResources()
|
||||
@@ -285,7 +289,6 @@ public class MergedContextConfiguration implements Serializable {
|
||||
/**
|
||||
* Determine if this {@code MergedContextConfiguration} instance has
|
||||
* class-based resources.
|
||||
*
|
||||
* @return {@code true} if the {@link #getClasses() classes} array is not empty
|
||||
* @since 4.0.4
|
||||
* @see #hasResources()
|
||||
@@ -298,7 +301,6 @@ public class MergedContextConfiguration implements Serializable {
|
||||
/**
|
||||
* Determine if this {@code MergedContextConfiguration} instance has
|
||||
* either path-based context resource locations or class-based resources.
|
||||
*
|
||||
* @return {@code true} if either the {@link #getLocations() locations}
|
||||
* or the {@link #getClasses() classes} array is not empty
|
||||
* @since 4.0.4
|
||||
@@ -306,7 +308,7 @@ public class MergedContextConfiguration implements Serializable {
|
||||
* @see #hasClasses()
|
||||
*/
|
||||
public boolean hasResources() {
|
||||
return hasLocations() || hasClasses();
|
||||
return (hasLocations() || hasClasses());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -314,7 +316,7 @@ public class MergedContextConfiguration implements Serializable {
|
||||
* {@linkplain #getTestClass() test class}.
|
||||
*/
|
||||
public Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> getContextInitializerClasses() {
|
||||
return contextInitializerClasses;
|
||||
return this.contextInitializerClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -323,17 +325,17 @@ public class MergedContextConfiguration implements Serializable {
|
||||
* @see ActiveProfiles
|
||||
*/
|
||||
public String[] getActiveProfiles() {
|
||||
return activeProfiles;
|
||||
return this.activeProfiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the merged resource locations for test {@code PropertySources} for the
|
||||
* {@linkplain #getTestClass() test class}.
|
||||
* Get the merged resource locations for test {@code PropertySources}
|
||||
* for the {@linkplain #getTestClass() test class}.
|
||||
* @see TestPropertySource#locations
|
||||
* @see java.util.Properties
|
||||
*/
|
||||
public String[] getPropertySourceLocations() {
|
||||
return propertySourceLocations;
|
||||
return this.propertySourceLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -345,20 +347,19 @@ public class MergedContextConfiguration implements Serializable {
|
||||
* @see java.util.Properties
|
||||
*/
|
||||
public String[] getPropertySourceProperties() {
|
||||
return propertySourceProperties;
|
||||
return this.propertySourceProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resolved {@link ContextLoader} for the {@linkplain #getTestClass() test class}.
|
||||
*/
|
||||
public ContextLoader getContextLoader() {
|
||||
return contextLoader;
|
||||
return this.contextLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link MergedContextConfiguration} for the parent application context in a
|
||||
* context hierarchy.
|
||||
*
|
||||
* Get the {@link MergedContextConfiguration} for the parent application context
|
||||
* in a context hierarchy.
|
||||
* @return the parent configuration or {@code null} if there is no parent
|
||||
* @see #getParentApplicationContext()
|
||||
* @since 3.2.2
|
||||
@@ -370,43 +371,21 @@ public class MergedContextConfiguration implements Serializable {
|
||||
/**
|
||||
* Get the parent {@link ApplicationContext} for the context defined by this
|
||||
* {@code MergedContextConfiguration} from the context cache.
|
||||
* <p>
|
||||
* If the parent context has not yet been loaded, it will be loaded, stored in the
|
||||
* cache, and then returned.
|
||||
*
|
||||
* <p>If the parent context has not yet been loaded, it will be loaded, stored
|
||||
* in the cache, and then returned.
|
||||
* @return the parent {@code ApplicationContext} or {@code null} if there is no parent
|
||||
* @see #getParent()
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public ApplicationContext getParentApplicationContext() {
|
||||
if (parent == null) {
|
||||
if (this.parent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Assert.state(cacheAwareContextLoaderDelegate != null,
|
||||
"Cannot retrieve a parent application context without access to the CacheAwareContextLoaderDelegate.");
|
||||
return cacheAwareContextLoaderDelegate.loadContext(parent);
|
||||
Assert.state(this.cacheAwareContextLoaderDelegate != null,
|
||||
"Cannot retrieve a parent application context without access to the CacheAwareContextLoaderDelegate");
|
||||
return this.cacheAwareContextLoaderDelegate.loadContext(this.parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique hash code for all properties of this
|
||||
* {@code MergedContextConfiguration} excluding the
|
||||
* {@linkplain #getTestClass() test class}.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + Arrays.hashCode(locations);
|
||||
result = prime * result + Arrays.hashCode(classes);
|
||||
result = prime * result + contextInitializerClasses.hashCode();
|
||||
result = prime * result + Arrays.hashCode(activeProfiles);
|
||||
result = prime * result + Arrays.hashCode(propertySourceLocations);
|
||||
result = prime * result + Arrays.hashCode(propertySourceProperties);
|
||||
result = prime * result + (parent == null ? 0 : parent.hashCode());
|
||||
result = prime * result + nullSafeToString(contextLoader).hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the supplied object is equal to this {@code MergedContextConfiguration}
|
||||
@@ -420,57 +399,68 @@ public class MergedContextConfiguration implements Serializable {
|
||||
* {@link #getContextLoader() ContextLoaders}.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof MergedContextConfiguration)) {
|
||||
if (!(other instanceof MergedContextConfiguration)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final MergedContextConfiguration that = (MergedContextConfiguration) obj;
|
||||
|
||||
if (!Arrays.equals(this.locations, that.locations)) {
|
||||
MergedContextConfiguration otherConfig = (MergedContextConfiguration) other;
|
||||
if (!Arrays.equals(this.locations, otherConfig.locations)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Arrays.equals(this.classes, that.classes)) {
|
||||
if (!Arrays.equals(this.classes, otherConfig.classes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.contextInitializerClasses.equals(that.contextInitializerClasses)) {
|
||||
if (!this.contextInitializerClasses.equals(otherConfig.contextInitializerClasses)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Arrays.equals(this.activeProfiles, that.activeProfiles)) {
|
||||
if (!Arrays.equals(this.activeProfiles, otherConfig.activeProfiles)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Arrays.equals(this.propertySourceLocations, that.propertySourceLocations)) {
|
||||
if (!Arrays.equals(this.propertySourceLocations, otherConfig.propertySourceLocations)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Arrays.equals(this.propertySourceProperties, that.propertySourceProperties)) {
|
||||
if (!Arrays.equals(this.propertySourceProperties, otherConfig.propertySourceProperties)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.parent == null) {
|
||||
if (that.parent != null) {
|
||||
if (otherConfig.parent != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!this.parent.equals(that.parent)) {
|
||||
else if (!this.parent.equals(otherConfig.parent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!nullSafeToString(this.contextLoader).equals(nullSafeToString(that.contextLoader))) {
|
||||
if (!nullSafeToString(this.contextLoader).equals(nullSafeToString(otherConfig.contextLoader))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique hash code for all properties of this
|
||||
* {@code MergedContextConfiguration} excluding the
|
||||
* {@linkplain #getTestClass() test class}.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Arrays.hashCode(this.locations);
|
||||
result = 31 * result + Arrays.hashCode(this.classes);
|
||||
result = 31 * result + this.contextInitializerClasses.hashCode();
|
||||
result = 31 * result + Arrays.hashCode(this.activeProfiles);
|
||||
result = 31 * result + Arrays.hashCode(this.propertySourceLocations);
|
||||
result = 31 * result + Arrays.hashCode(this.propertySourceProperties);
|
||||
result = 31 * result + (this.parent != null ? this.parent.hashCode() : 0);
|
||||
result = 31 * result + nullSafeToString(this.contextLoader).hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a String representation of the {@linkplain #getTestClass() test class},
|
||||
* {@linkplain #getLocations() locations}, {@linkplain #getClasses() annotated classes},
|
||||
@@ -483,17 +473,17 @@ public class MergedContextConfiguration implements Serializable {
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this)//
|
||||
.append("testClass", testClass)//
|
||||
.append("locations", ObjectUtils.nullSafeToString(locations))//
|
||||
.append("classes", ObjectUtils.nullSafeToString(classes))//
|
||||
.append("contextInitializerClasses", ObjectUtils.nullSafeToString(contextInitializerClasses))//
|
||||
.append("activeProfiles", ObjectUtils.nullSafeToString(activeProfiles))//
|
||||
.append("propertySourceLocations", ObjectUtils.nullSafeToString(propertySourceLocations))//
|
||||
.append("propertySourceProperties", ObjectUtils.nullSafeToString(propertySourceProperties))//
|
||||
.append("contextLoader", nullSafeToString(contextLoader))//
|
||||
.append("parent", parent)//
|
||||
.toString();
|
||||
return new ToStringCreator(this)
|
||||
.append("testClass", this.testClass)
|
||||
.append("locations", ObjectUtils.nullSafeToString(this.locations))
|
||||
.append("classes", ObjectUtils.nullSafeToString(this.classes))
|
||||
.append("contextInitializerClasses", ObjectUtils.nullSafeToString(this.contextInitializerClasses))
|
||||
.append("activeProfiles", ObjectUtils.nullSafeToString(this.activeProfiles))
|
||||
.append("propertySourceLocations", ObjectUtils.nullSafeToString(this.propertySourceLocations))
|
||||
.append("propertySourceProperties", ObjectUtils.nullSafeToString(this.propertySourceProperties))
|
||||
.append("contextLoader", nullSafeToString(this.contextLoader))
|
||||
.append("parent", this.parent)
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -121,7 +121,10 @@ public @interface TestPropertySource {
|
||||
* {@code http:}, etc.) will be loaded using the specified resource protocol.
|
||||
* Resource location wildcards (e.g. <code>**/*.properties</code>)
|
||||
* are not permitted: each location must evaluate to exactly one
|
||||
* {@code .properties} or {@code .xml} resource.
|
||||
* {@code .properties} or {@code .xml} resource. Property placeholders
|
||||
* in paths (i.e., <code>${...}</code>) will be
|
||||
* {@linkplain org.springframework.core.env.Environment#resolveRequiredPlaceholders(String) resolved}
|
||||
* against the {@code Environment}.
|
||||
*
|
||||
* <h3>Default Properties File Detection</h3>
|
||||
* <p>See the class-level Javadoc for a discussion on detection of defaults.
|
||||
|
||||
+9
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -214,7 +214,14 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner {
|
||||
notifier.fireTestIgnored(description);
|
||||
}
|
||||
else {
|
||||
runLeaf(methodBlock(frameworkMethod), description, notifier);
|
||||
Statement statement;
|
||||
try {
|
||||
statement = methodBlock(frameworkMethod);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
statement = new Fail(ex);
|
||||
}
|
||||
runLeaf(statement, description, notifier);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-78
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,13 +16,8 @@
|
||||
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -34,12 +29,8 @@ import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.ResourcePropertySource;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
@@ -64,7 +55,6 @@ import org.springframework.util.ResourceUtils;
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @author Dave Syer
|
||||
* @since 2.5
|
||||
* @see #generateDefaultLocations
|
||||
* @see #getResourceSuffixes
|
||||
@@ -74,8 +64,6 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
|
||||
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AbstractContextLoader.class);
|
||||
|
||||
|
||||
@@ -131,80 +119,19 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
|
||||
* @param context the newly created application context
|
||||
* @param mergedConfig the merged context configuration
|
||||
* @since 3.2
|
||||
* @see TestPropertySourceUtils#addPropertiesFilesToEnvironment
|
||||
* @see TestPropertySourceUtils#addInlinedPropertiesToEnvironment
|
||||
* @see ApplicationContextInitializer#initialize(ConfigurableApplicationContext)
|
||||
* @see #loadContext(MergedContextConfiguration)
|
||||
* @see ConfigurableApplicationContext#setId
|
||||
*/
|
||||
protected void prepareContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
|
||||
context.getEnvironment().setActiveProfiles(mergedConfig.getActiveProfiles());
|
||||
addResourcePropertySourcesToEnvironment(context, mergedConfig);
|
||||
addInlinedPropertiesToEnvironment(context, mergedConfig);
|
||||
TestPropertySourceUtils.addPropertiesFilesToEnvironment(context, mergedConfig.getPropertySourceLocations());
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, mergedConfig.getPropertySourceProperties());
|
||||
invokeApplicationContextInitializers(context, mergedConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.1
|
||||
*/
|
||||
private void addResourcePropertySourcesToEnvironment(ConfigurableApplicationContext context,
|
||||
MergedContextConfiguration mergedConfig) {
|
||||
try {
|
||||
ConfigurableEnvironment environment = context.getEnvironment();
|
||||
String[] locations = mergedConfig.getPropertySourceLocations();
|
||||
for (String location : locations) {
|
||||
String resolvedLocation = environment.resolveRequiredPlaceholders(location);
|
||||
Resource resource = context.getResource(resolvedLocation);
|
||||
ResourcePropertySource ps = new ResourcePropertySource(resource);
|
||||
environment.getPropertySources().addFirst(ps);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to add PropertySource to Environment", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.1
|
||||
*/
|
||||
private void addInlinedPropertiesToEnvironment(ConfigurableApplicationContext context,
|
||||
MergedContextConfiguration mergedConfig) {
|
||||
String[] keyValuePairs = mergedConfig.getPropertySourceProperties();
|
||||
if (!ObjectUtils.isEmpty(keyValuePairs)) {
|
||||
String name = "test properties " + ObjectUtils.nullSafeToString(keyValuePairs);
|
||||
MapPropertySource ps = new MapPropertySource(name, extractEnvironmentProperties(keyValuePairs));
|
||||
context.getEnvironment().getPropertySources().addFirst(ps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract environment properties from the supplied key/value pairs.
|
||||
* <p>Parsing of the key/value pairs is achieved by converting all pairs
|
||||
* into a single <em>virtual</em> properties file in memory and delegating
|
||||
* to {@link Properties#load(java.io.Reader)} to parse that virtual file.
|
||||
* <p>This code has been adapted from Spring Boot's
|
||||
* {@link org.springframework.boot.test.SpringApplicationContextLoader SpringApplicationContextLoader}.
|
||||
* @since 4.1
|
||||
*/
|
||||
private Map<String, Object> extractEnvironmentProperties(String[] keyValuePairs) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String keyValuePair : keyValuePairs) {
|
||||
sb.append(keyValuePair).append(LINE_SEPARATOR);
|
||||
}
|
||||
String content = sb.toString();
|
||||
Properties props = new Properties();
|
||||
try {
|
||||
props.load(new StringReader(content));
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to load test environment properties from: " + content, e);
|
||||
}
|
||||
|
||||
Map<String, Object> properties = new HashMap<String, Object>();
|
||||
for (String name : props.stringPropertyNames()) {
|
||||
properties.put(name, props.getProperty(name));
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void invokeApplicationContextInitializers(ConfigurableApplicationContext context,
|
||||
MergedContextConfiguration mergedConfig) {
|
||||
|
||||
+4
-2
@@ -24,6 +24,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.test.context.SmartContextLoader;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -95,13 +96,14 @@ public abstract class AnnotationConfigContextLoaderUtils {
|
||||
* <li>must not be {@code private}</li>
|
||||
* <li>must not be {@code final}</li>
|
||||
* <li>must be {@code static}</li>
|
||||
* <li>must be annotated with {@code @Configuration}</li>
|
||||
* <li>must be annotated or meta-annotated with {@code @Configuration}</li>
|
||||
* </ul>
|
||||
* @param clazz the class to check
|
||||
* @return {@code true} if the supplied class meets the candidate criteria
|
||||
*/
|
||||
private static boolean isDefaultConfigurationClassCandidate(Class<?> clazz) {
|
||||
return (clazz != null && isStaticNonPrivateAndNonFinal(clazz) && clazz.isAnnotationPresent(Configuration.class));
|
||||
return (clazz != null && isStaticNonPrivateAndNonFinal(clazz) &&
|
||||
(AnnotationUtils.findAnnotation(clazz, Configuration.class) != null));
|
||||
}
|
||||
|
||||
private static boolean isStaticNonPrivateAndNonFinal(Class<?> clazz) {
|
||||
|
||||
+1
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -73,7 +73,6 @@ public class DefaultActiveProfilesResolver implements ActiveProfilesResolver {
|
||||
}
|
||||
}
|
||||
else {
|
||||
Class<?> rootDeclaringClass = descriptor.getRootDeclaringClass();
|
||||
Class<?> declaringClass = descriptor.getDeclaringClass();
|
||||
|
||||
AnnotationAttributes annAttrs = descriptor.getAnnotationAttributes();
|
||||
@@ -82,15 +81,6 @@ public class DefaultActiveProfilesResolver implements ActiveProfilesResolver {
|
||||
annAttrs, declaringClass.getName()));
|
||||
}
|
||||
|
||||
Class<? extends ActiveProfilesResolver> resolverClass = annAttrs.getClass("resolver");
|
||||
if (!ActiveProfilesResolver.class.equals(resolverClass)) {
|
||||
String msg = String.format("Configuration error for test class [%s]: %s cannot be used "
|
||||
+ "in conjunction with custom resolver [%s].", rootDeclaringClass.getName(),
|
||||
getClass().getSimpleName(), resolverClass.getName());
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
|
||||
String[] profiles = annAttrs.getStringArray("profiles");
|
||||
String[] valueProfiles = annAttrs.getStringArray("value");
|
||||
boolean valueDeclared = !ObjectUtils.isEmpty(valueProfiles);
|
||||
|
||||
+157
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,33 +16,57 @@
|
||||
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.PropertySources;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.ResourcePropertySource;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.util.TestContextResourceUtils;
|
||||
import org.springframework.test.util.MetaAnnotationUtils.*;
|
||||
import org.springframework.test.util.MetaAnnotationUtils.AnnotationDescriptor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.test.util.MetaAnnotationUtils.*;
|
||||
|
||||
/**
|
||||
* Utility methods for working with {@link TestPropertySource @TestPropertySource}.
|
||||
* Utility methods for working with {@link TestPropertySource @TestPropertySource}
|
||||
* and adding test {@link PropertySource PropertySources} to the {@code Environment}.
|
||||
*
|
||||
* <p>Primarily intended for use within the framework.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
* @see TestPropertySource
|
||||
*/
|
||||
abstract class TestPropertySourceUtils {
|
||||
public abstract class TestPropertySourceUtils {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(TestPropertySourceUtils.class);
|
||||
|
||||
/**
|
||||
* The name of the {@link MapPropertySource} created from <em>inlined properties</em>.
|
||||
* @since 4.1.5
|
||||
* @see {@link #addInlinedPropertiesToEnvironment(ConfigurableEnvironment, String[])}
|
||||
*/
|
||||
public static final String INLINED_PROPERTIES_PROPERTY_SOURCE_NAME = "Inlined Test Properties";
|
||||
|
||||
|
||||
private TestPropertySourceUtils() {
|
||||
/* no-op */
|
||||
@@ -131,4 +155,133 @@ abstract class TestPropertySourceUtils {
|
||||
return StringUtils.toStringArray(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the {@link Properties} files from the given resource {@code locations}
|
||||
* to the {@link Environment} of the supplied {@code context}.
|
||||
* <p>Property placeholders in resource locations (i.e., <code>${...}</code>)
|
||||
* will be {@linkplain Environment#resolveRequiredPlaceholders(String) resolved}
|
||||
* against the {@code Environment}.
|
||||
* <p>Each properties file will be converted to a {@link ResourcePropertySource}
|
||||
* that will be added to the {@link PropertySources} of the environment with
|
||||
* highest precedence.
|
||||
* @param context the application context whose environment should be updated;
|
||||
* never {@code null}
|
||||
* @param locations the resource locations of {@code Properties} files to add
|
||||
* to the environment; potentially empty but never {@code null}
|
||||
* @since 4.1.5
|
||||
* @see ResourcePropertySource
|
||||
* @see TestPropertySource#locations
|
||||
* @throws IllegalStateException if an error occurs while processing a properties file
|
||||
*/
|
||||
public static void addPropertiesFilesToEnvironment(ConfigurableApplicationContext context,
|
||||
String[] locations) {
|
||||
Assert.notNull(context, "context must not be null");
|
||||
Assert.notNull(locations, "locations must not be null");
|
||||
try {
|
||||
ConfigurableEnvironment environment = context.getEnvironment();
|
||||
for (String location : locations) {
|
||||
String resolvedLocation = environment.resolveRequiredPlaceholders(location);
|
||||
Resource resource = context.getResource(resolvedLocation);
|
||||
environment.getPropertySources().addFirst(new ResourcePropertySource(resource));
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to add PropertySource to Environment", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given <em>inlined properties</em> to the {@link Environment} of the
|
||||
* supplied {@code context}.
|
||||
* <p>This method simply delegates to
|
||||
* {@link #addInlinedPropertiesToEnvironment(ConfigurableEnvironment, String[])}.
|
||||
* @param context the application context whose environment should be updated;
|
||||
* never {@code null}
|
||||
* @param inlinedProperties the inlined properties to add to the environment;
|
||||
* potentially empty but never {@code null}
|
||||
* @since 4.1.5
|
||||
* @see TestPropertySource#properties
|
||||
* @see #addInlinedPropertiesToEnvironment(ConfigurableEnvironment, String[])
|
||||
*/
|
||||
public static void addInlinedPropertiesToEnvironment(ConfigurableApplicationContext context,
|
||||
String[] inlinedProperties) {
|
||||
Assert.notNull(context, "context must not be null");
|
||||
Assert.notNull(inlinedProperties, "inlinedProperties must not be null");
|
||||
addInlinedPropertiesToEnvironment(context.getEnvironment(), inlinedProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given <em>inlined properties</em> (in the form of <em>key-value</em>
|
||||
* pairs) to the supplied {@link ConfigurableEnvironment environment}.
|
||||
* <p>All key-value pairs will be added to the {@code Environment} as a
|
||||
* single {@link MapPropertySource} with the highest precedence.
|
||||
* <p>For details on the parsing of <em>inlined properties</em>, consult the
|
||||
* Javadoc for {@link #convertInlinedPropertiesToMap}.
|
||||
* @param environment the environment to update; never {@code null}
|
||||
* @param inlinedProperties the inlined properties to add to the environment;
|
||||
* potentially empty but never {@code null}
|
||||
* @since 4.1.5
|
||||
* @see MapPropertySource
|
||||
* @see #INLINED_PROPERTIES_PROPERTY_SOURCE_NAME
|
||||
* @see TestPropertySource#properties
|
||||
* @see #convertInlinedPropertiesToMap
|
||||
*/
|
||||
public static void addInlinedPropertiesToEnvironment(ConfigurableEnvironment environment, String[] inlinedProperties) {
|
||||
Assert.notNull(environment, "environment must not be null");
|
||||
Assert.notNull(inlinedProperties, "inlinedProperties must not be null");
|
||||
if (!ObjectUtils.isEmpty(inlinedProperties)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Adding inlined properties to environment: "
|
||||
+ ObjectUtils.nullSafeToString(inlinedProperties));
|
||||
}
|
||||
MapPropertySource ps = new MapPropertySource(INLINED_PROPERTIES_PROPERTY_SOURCE_NAME,
|
||||
convertInlinedPropertiesToMap(inlinedProperties));
|
||||
environment.getPropertySources().addFirst(ps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the supplied <em>inlined properties</em> (in the form of <em>key-value</em>
|
||||
* pairs) into a map keyed by property name, preserving the ordering of property names
|
||||
* in the returned map.
|
||||
* <p>Parsing of the key-value pairs is achieved by converting all pairs
|
||||
* into <em>virtual</em> properties files in memory and delegating to
|
||||
* {@link Properties#load(java.io.Reader)} to parse each virtual file.
|
||||
* <p>For a full discussion of <em>inlined properties</em>, consult the Javadoc
|
||||
* for {@link TestPropertySource#properties}.
|
||||
* @param inlinedProperties the inlined properties to convert; potentially empty
|
||||
* but never {@code null}
|
||||
* @return a new, ordered map containing the converted properties
|
||||
* @since 4.1.5
|
||||
* @throws IllegalStateException if a given key-value pair cannot be parsed, or if
|
||||
* a given inlined property contains multiple key-value pairs
|
||||
* @see #addInlinedPropertiesToEnvironment(ConfigurableEnvironment, String[])
|
||||
*/
|
||||
public static Map<String, Object> convertInlinedPropertiesToMap(String[] inlinedProperties) {
|
||||
Assert.notNull(inlinedProperties, "inlinedProperties must not be null");
|
||||
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
|
||||
Properties props = new Properties();
|
||||
for (String pair : inlinedProperties) {
|
||||
if (!StringUtils.hasText(pair)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
props.load(new StringReader(pair));
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to load test environment property from [" + pair + "].", e);
|
||||
}
|
||||
Assert.state(props.size() == 1, "Failed to load exactly one test environment property from [" + pair + "].");
|
||||
|
||||
for (String name : props.stringPropertyNames()) {
|
||||
map.put(name, props.getProperty(name));
|
||||
}
|
||||
props.clear();
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+30
-47
@@ -63,7 +63,6 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
||||
* supplied parameters.
|
||||
* <p>Delegates to
|
||||
* {@link #WebMergedContextConfiguration(Class, String[], Class[], Set, String[], String[], String[], String, ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)}.
|
||||
*
|
||||
* @param testClass the test class for which the configuration was merged
|
||||
* @param locations the merged resource locations
|
||||
* @param classes the merged annotated classes
|
||||
@@ -76,10 +75,7 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
||||
* {@link #WebMergedContextConfiguration(Class, String[], Class[], Set, String[], String, ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public WebMergedContextConfiguration(
|
||||
Class<?> testClass,
|
||||
String[] locations,
|
||||
Class<?>[] classes,
|
||||
public WebMergedContextConfiguration(Class<?> testClass, String[] locations, Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
|
||||
String[] activeProfiles, String resourceBasePath, ContextLoader contextLoader) {
|
||||
|
||||
@@ -92,7 +88,6 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
||||
* supplied parameters.
|
||||
* <p>Delegates to
|
||||
* {@link #WebMergedContextConfiguration(Class, String[], Class[], Set, String[], String[], String[], String, ContextLoader, CacheAwareContextLoaderDelegate, MergedContextConfiguration)}.
|
||||
*
|
||||
* @param testClass the test class for which the configuration was merged
|
||||
* @param locations the merged resource locations
|
||||
* @param classes the merged annotated classes
|
||||
@@ -109,10 +104,7 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public WebMergedContextConfiguration(
|
||||
Class<?> testClass,
|
||||
String[] locations,
|
||||
Class<?>[] classes,
|
||||
public WebMergedContextConfiguration(Class<?> testClass, String[] locations, Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
|
||||
String[] activeProfiles, String resourceBasePath, ContextLoader contextLoader,
|
||||
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parent) {
|
||||
@@ -137,7 +129,6 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
||||
/**
|
||||
* Create a new {@code WebMergedContextConfiguration} instance for the
|
||||
* supplied parameters.
|
||||
*
|
||||
* <p>If a {@code null} value is supplied for {@code locations},
|
||||
* {@code classes}, {@code activeProfiles}, {@code propertySourceLocations},
|
||||
* or {@code propertySourceProperties} an empty array will be stored instead.
|
||||
@@ -146,7 +137,6 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
||||
* If an <em>empty</em> value is supplied for the {@code resourceBasePath}
|
||||
* an empty string will be used. Furthermore, active profiles will be sorted,
|
||||
* and duplicate profiles will be removed.
|
||||
*
|
||||
* @param testClass the test class for which the configuration was merged
|
||||
* @param locations the merged resource locations
|
||||
* @param classes the merged annotated classes
|
||||
@@ -161,10 +151,7 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
||||
* @param parent the parent configuration or {@code null} if there is no parent
|
||||
* @since 4.1
|
||||
*/
|
||||
public WebMergedContextConfiguration(
|
||||
Class<?> testClass,
|
||||
String[] locations,
|
||||
Class<?>[] classes,
|
||||
public WebMergedContextConfiguration(Class<?> testClass, String[] locations, Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
|
||||
String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties,
|
||||
String resourceBasePath, ContextLoader contextLoader,
|
||||
@@ -176,6 +163,7 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
||||
this.resourceBasePath = !StringUtils.hasText(resourceBasePath) ? "" : resourceBasePath;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the resource path to the root directory of the web application for the
|
||||
* {@linkplain #getTestClass() test class}, configured via {@code @WebAppConfiguration}.
|
||||
@@ -185,18 +173,6 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
||||
return this.resourceBasePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique hash code for all properties of this
|
||||
* {@code WebMergedContextConfiguration} excluding the
|
||||
* {@linkplain #getTestClass() test class}.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + resourceBasePath.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the supplied object is equal to this {@code WebMergedContextConfiguration}
|
||||
@@ -209,18 +185,25 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
||||
* {@link #getContextLoader() ContextLoaders}.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof WebMergedContextConfiguration)) {
|
||||
if (!(other instanceof WebMergedContextConfiguration)) {
|
||||
return false;
|
||||
}
|
||||
WebMergedContextConfiguration otherConfig = (WebMergedContextConfiguration) other;
|
||||
return super.equals(otherConfig) && this.getResourceBasePath().equals(otherConfig.getResourceBasePath());
|
||||
}
|
||||
|
||||
final WebMergedContextConfiguration that = (WebMergedContextConfiguration) obj;
|
||||
|
||||
return super.equals(that) && this.getResourceBasePath().equals(that.getResourceBasePath());
|
||||
/**
|
||||
* Generate a unique hash code for all properties of this
|
||||
* {@code WebMergedContextConfiguration} excluding the
|
||||
* {@linkplain #getTestClass() test class}.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + this.resourceBasePath.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,18 +219,18 @@ public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this)//
|
||||
.append("testClass", getTestClass())//
|
||||
.append("locations", ObjectUtils.nullSafeToString(getLocations()))//
|
||||
.append("classes", ObjectUtils.nullSafeToString(getClasses()))//
|
||||
.append("contextInitializerClasses", ObjectUtils.nullSafeToString(getContextInitializerClasses()))//
|
||||
.append("activeProfiles", ObjectUtils.nullSafeToString(getActiveProfiles()))//
|
||||
.append("propertySourceLocations", ObjectUtils.nullSafeToString(getPropertySourceLocations()))//
|
||||
.append("propertySourceProperties", ObjectUtils.nullSafeToString(getPropertySourceProperties()))//
|
||||
.append("resourceBasePath", getResourceBasePath())//
|
||||
.append("contextLoader", nullSafeToString(getContextLoader()))//
|
||||
.append("parent", getParent())//
|
||||
.toString();
|
||||
return new ToStringCreator(this)
|
||||
.append("testClass", getTestClass())
|
||||
.append("locations", ObjectUtils.nullSafeToString(getLocations()))
|
||||
.append("classes", ObjectUtils.nullSafeToString(getClasses()))
|
||||
.append("contextInitializerClasses", ObjectUtils.nullSafeToString(getContextInitializerClasses()))
|
||||
.append("activeProfiles", ObjectUtils.nullSafeToString(getActiveProfiles()))
|
||||
.append("propertySourceLocations", ObjectUtils.nullSafeToString(getPropertySourceLocations()))
|
||||
.append("propertySourceProperties", ObjectUtils.nullSafeToString(getPropertySourceProperties()))
|
||||
.append("resourceBasePath", getResourceBasePath())
|
||||
.append("contextLoader", nullSafeToString(getContextLoader()))
|
||||
.append("parent", getParent())
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+121
-119
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-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.
|
||||
@@ -41,8 +41,10 @@ import static org.junit.Assert.*;
|
||||
public class MergedContextConfigurationTests {
|
||||
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
|
||||
|
||||
|
||||
private final GenericXmlContextLoader loader = new GenericXmlContextLoader();
|
||||
|
||||
|
||||
@@ -50,7 +52,6 @@ public class MergedContextConfigurationTests {
|
||||
public void hashCodeWithNulls() {
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(null, null, null, null, null);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(null, null, null, null, null);
|
||||
assertTrue(mergedConfig1.hashCode() > 0);
|
||||
assertEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@@ -63,19 +64,19 @@ public class MergedContextConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void hashCodeWithEmptyArrays() {
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
assertEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashCodeWithEmptyArraysAndDifferentLoaders() {
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, new AnnotationConfigContextLoader());
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, new AnnotationConfigContextLoader());
|
||||
assertNotEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@@ -83,9 +84,9 @@ public class MergedContextConfigurationTests {
|
||||
public void hashCodeWithSameLocations() {
|
||||
String[] locations = new String[] { "foo", "bar}" };
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), locations,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), locations,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
assertEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@@ -94,19 +95,19 @@ public class MergedContextConfigurationTests {
|
||||
String[] locations1 = new String[] { "foo", "bar}" };
|
||||
String[] locations2 = new String[] { "baz", "quux}" };
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), locations1,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), locations2,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
assertNotEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashCodeWithSameConfigClasses() {
|
||||
Class<?>[] classes = new Class<?>[] { String.class, Integer.class };
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
classes, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
classes, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, classes, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, classes, EMPTY_STRING_ARRAY, loader);
|
||||
assertEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@@ -114,20 +115,20 @@ public class MergedContextConfigurationTests {
|
||||
public void hashCodeWithDifferentConfigClasses() {
|
||||
Class<?>[] classes1 = new Class<?>[] { String.class, Integer.class };
|
||||
Class<?>[] classes2 = new Class<?>[] { Boolean.class, Number.class };
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
classes1, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
classes2, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, classes1, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, classes2, EMPTY_STRING_ARRAY, loader);
|
||||
assertNotEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashCodeWithSameProfiles() {
|
||||
String[] activeProfiles = new String[] { "catbert", "dogbert" };
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, activeProfiles, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, activeProfiles, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles, loader);
|
||||
assertEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@@ -135,10 +136,10 @@ public class MergedContextConfigurationTests {
|
||||
public void hashCodeWithSameProfilesReversed() {
|
||||
String[] activeProfiles1 = new String[] { "catbert", "dogbert" };
|
||||
String[] activeProfiles2 = new String[] { "dogbert", "catbert" };
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, activeProfiles1, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, activeProfiles2, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles1, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles2, loader);
|
||||
assertEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@@ -146,10 +147,10 @@ public class MergedContextConfigurationTests {
|
||||
public void hashCodeWithSameDuplicateProfiles() {
|
||||
String[] activeProfiles1 = new String[] { "catbert", "dogbert" };
|
||||
String[] activeProfiles2 = new String[] { "catbert", "dogbert", "catbert", "dogbert", "catbert" };
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, activeProfiles1, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, activeProfiles2, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles1, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles2, loader);
|
||||
assertEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@@ -157,46 +158,46 @@ public class MergedContextConfigurationTests {
|
||||
public void hashCodeWithDifferentProfiles() {
|
||||
String[] activeProfiles1 = new String[] { "catbert", "dogbert" };
|
||||
String[] activeProfiles2 = new String[] { "X", "Y" };
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, activeProfiles1, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, activeProfiles2, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles1, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles2, loader);
|
||||
assertNotEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashCodeWithSameInitializers() {
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses1 = //
|
||||
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses1 =
|
||||
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
initializerClasses1.add(FooInitializer.class);
|
||||
initializerClasses1.add(BarInitializer.class);
|
||||
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses2 = //
|
||||
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses2 =
|
||||
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
initializerClasses2.add(BarInitializer.class);
|
||||
initializerClasses2.add(FooInitializer.class);
|
||||
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, initializerClasses1, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, initializerClasses2, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, initializerClasses1, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, initializerClasses2, EMPTY_STRING_ARRAY, loader);
|
||||
assertEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashCodeWithDifferentInitializers() {
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses1 = //
|
||||
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses1 =
|
||||
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
initializerClasses1.add(FooInitializer.class);
|
||||
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses2 = //
|
||||
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses2 =
|
||||
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
initializerClasses2.add(BarInitializer.class);
|
||||
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, initializerClasses1, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, initializerClasses2, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, initializerClasses1, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, initializerClasses2, EMPTY_STRING_ARRAY, loader);
|
||||
assertNotEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@@ -206,12 +207,12 @@ public class MergedContextConfigurationTests {
|
||||
@Test
|
||||
public void hashCodeWithSameParent() {
|
||||
MergedContextConfiguration parent = new MergedContextConfiguration(getClass(), new String[] { "foo", "bar}" },
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent);
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent);
|
||||
assertEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@@ -221,14 +222,14 @@ public class MergedContextConfigurationTests {
|
||||
@Test
|
||||
public void hashCodeWithDifferentParents() {
|
||||
MergedContextConfiguration parent1 = new MergedContextConfiguration(getClass(), new String[] { "foo", "bar}" },
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration parent2 = new MergedContextConfiguration(getClass(), new String[] { "baz", "quux" },
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent1);
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent1);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent2);
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, loader, null, parent2);
|
||||
assertNotEquals(mergedConfig1.hashCode(), mergedConfig2.hashCode());
|
||||
}
|
||||
|
||||
@@ -237,7 +238,7 @@ public class MergedContextConfigurationTests {
|
||||
MergedContextConfiguration mergedConfig = new MergedContextConfiguration(null, null, null, null, null);
|
||||
assertEquals(mergedConfig, mergedConfig);
|
||||
assertNotEquals(mergedConfig, null);
|
||||
assertNotEquals(mergedConfig, new Integer(1));
|
||||
assertNotEquals(mergedConfig, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -256,19 +257,19 @@ public class MergedContextConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void equalsWithEmptyArrays() {
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
assertEquals(mergedConfig1, mergedConfig2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsWithEmptyArraysAndDifferentLoaders() {
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, new AnnotationConfigContextLoader());
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, new AnnotationConfigContextLoader());
|
||||
assertNotEquals(mergedConfig1, mergedConfig2);
|
||||
assertNotEquals(mergedConfig2, mergedConfig1);
|
||||
}
|
||||
@@ -276,10 +277,10 @@ public class MergedContextConfigurationTests {
|
||||
@Test
|
||||
public void equalsWithSameLocations() {
|
||||
String[] locations = new String[] { "foo", "bar}" };
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), locations,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), locations,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
locations, EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
locations, EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
assertEquals(mergedConfig1, mergedConfig2);
|
||||
}
|
||||
|
||||
@@ -287,10 +288,10 @@ public class MergedContextConfigurationTests {
|
||||
public void equalsWithDifferentLocations() {
|
||||
String[] locations1 = new String[] { "foo", "bar}" };
|
||||
String[] locations2 = new String[] { "baz", "quux}" };
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), locations1,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), locations2,
|
||||
EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
locations1, EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
locations2, EMPTY_CLASS_ARRAY, EMPTY_STRING_ARRAY, loader);
|
||||
assertNotEquals(mergedConfig1, mergedConfig2);
|
||||
assertNotEquals(mergedConfig2, mergedConfig1);
|
||||
}
|
||||
@@ -298,10 +299,10 @@ public class MergedContextConfigurationTests {
|
||||
@Test
|
||||
public void equalsWithSameConfigClasses() {
|
||||
Class<?>[] classes = new Class<?>[] { String.class, Integer.class };
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
classes, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
classes, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, classes, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, classes, EMPTY_STRING_ARRAY, loader);
|
||||
assertEquals(mergedConfig1, mergedConfig2);
|
||||
}
|
||||
|
||||
@@ -309,10 +310,10 @@ public class MergedContextConfigurationTests {
|
||||
public void equalsWithDifferentConfigClasses() {
|
||||
Class<?>[] classes1 = new Class<?>[] { String.class, Integer.class };
|
||||
Class<?>[] classes2 = new Class<?>[] { Boolean.class, Number.class };
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
classes1, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
classes2, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, classes1, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, classes2, EMPTY_STRING_ARRAY, loader);
|
||||
assertNotEquals(mergedConfig1, mergedConfig2);
|
||||
assertNotEquals(mergedConfig2, mergedConfig1);
|
||||
}
|
||||
@@ -320,10 +321,10 @@ public class MergedContextConfigurationTests {
|
||||
@Test
|
||||
public void equalsWithSameProfiles() {
|
||||
String[] activeProfiles = new String[] { "catbert", "dogbert" };
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, activeProfiles, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, activeProfiles, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles, loader);
|
||||
assertEquals(mergedConfig1, mergedConfig2);
|
||||
}
|
||||
|
||||
@@ -331,10 +332,10 @@ public class MergedContextConfigurationTests {
|
||||
public void equalsWithSameProfilesReversed() {
|
||||
String[] activeProfiles1 = new String[] { "catbert", "dogbert" };
|
||||
String[] activeProfiles2 = new String[] { "dogbert", "catbert" };
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, activeProfiles1, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, activeProfiles2, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles1, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles2, loader);
|
||||
assertEquals(mergedConfig1, mergedConfig2);
|
||||
}
|
||||
|
||||
@@ -342,10 +343,10 @@ public class MergedContextConfigurationTests {
|
||||
public void equalsWithSameDuplicateProfiles() {
|
||||
String[] activeProfiles1 = new String[] { "catbert", "dogbert" };
|
||||
String[] activeProfiles2 = new String[] { "catbert", "dogbert", "catbert", "dogbert", "catbert" };
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, activeProfiles1, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, activeProfiles2, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles1, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles2, loader);
|
||||
assertEquals(mergedConfig1, mergedConfig2);
|
||||
}
|
||||
|
||||
@@ -353,47 +354,47 @@ public class MergedContextConfigurationTests {
|
||||
public void equalsWithDifferentProfiles() {
|
||||
String[] activeProfiles1 = new String[] { "catbert", "dogbert" };
|
||||
String[] activeProfiles2 = new String[] { "X", "Y" };
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, activeProfiles1, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, activeProfiles2, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles1, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles2, loader);
|
||||
assertNotEquals(mergedConfig1, mergedConfig2);
|
||||
assertNotEquals(mergedConfig2, mergedConfig1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsWithSameInitializers() {
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses1 = //
|
||||
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses1 =
|
||||
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
initializerClasses1.add(FooInitializer.class);
|
||||
initializerClasses1.add(BarInitializer.class);
|
||||
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses2 = //
|
||||
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses2 =
|
||||
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
initializerClasses2.add(BarInitializer.class);
|
||||
initializerClasses2.add(FooInitializer.class);
|
||||
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, initializerClasses1, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, initializerClasses2, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, initializerClasses1, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, initializerClasses2, EMPTY_STRING_ARRAY, loader);
|
||||
assertEquals(mergedConfig1, mergedConfig2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsWithDifferentInitializers() {
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses1 = //
|
||||
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses1 =
|
||||
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
initializerClasses1.add(FooInitializer.class);
|
||||
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses2 = //
|
||||
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses2 =
|
||||
new HashSet<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>>();
|
||||
initializerClasses2.add(BarInitializer.class);
|
||||
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, initializerClasses1, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, initializerClasses2, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, initializerClasses1, EMPTY_STRING_ARRAY, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, initializerClasses2, EMPTY_STRING_ARRAY, loader);
|
||||
assertNotEquals(mergedConfig1, mergedConfig2);
|
||||
assertNotEquals(mergedConfig2, mergedConfig1);
|
||||
}
|
||||
@@ -440,6 +441,7 @@ public class MergedContextConfigurationTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class BarInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
|
||||
|
||||
@Override
|
||||
|
||||
+36
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -21,33 +21,60 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.test.context.support.TestPropertySourceUtils.*;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link TestPropertySource @TestPropertySource}
|
||||
* support with an inlined properties.
|
||||
* Integration tests for {@link TestPropertySource @TestPropertySource} support with
|
||||
* inlined properties.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@TestPropertySource(properties = { "foo = bar", "enigma: 42" })
|
||||
@TestPropertySource(properties = { "", "foo = bar", "baz quux", "enigma: 42", "x.y.z = a=b=c",
|
||||
"server.url = http://example.com", "key.value.1: key=value", "key.value.2 key=value", "key.value.3 key:value" })
|
||||
public class InlinedPropertiesTestPropertySourceTests {
|
||||
|
||||
@Autowired
|
||||
protected Environment env;
|
||||
private ConfigurableEnvironment env;
|
||||
|
||||
|
||||
private String property(String key) {
|
||||
return env.getProperty(key);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyPropertiesAreAvailableInEnvironment() {
|
||||
assertEquals("bar", env.getProperty("foo"));
|
||||
assertEquals(42, env.getProperty("enigma", Integer.class).intValue());
|
||||
public void propertiesAreAvailableInEnvironment() {
|
||||
// Simple key/value pairs
|
||||
assertThat(property("foo"), is("bar"));
|
||||
assertThat(property("baz"), is("quux"));
|
||||
assertThat(property("enigma"), is("42"));
|
||||
|
||||
// Values containing key/value delimiters (":", "=", " ")
|
||||
assertThat(property("x.y.z"), is("a=b=c"));
|
||||
assertThat(property("server.url"), is("http://example.com"));
|
||||
assertThat(property("key.value.1"), is("key=value"));
|
||||
assertThat(property("key.value.2"), is("key=value"));
|
||||
assertThat(property("key.value.3"), is("key:value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void propertyNameOrderingIsPreservedInEnvironment() {
|
||||
final String[] expectedPropertyNames = new String[] { "foo", "baz", "enigma", "x.y.z", "server.url",
|
||||
"key.value.1", "key.value.2", "key.value.3" };
|
||||
EnumerablePropertySource eps = (EnumerablePropertySource) env.getPropertySources().get(
|
||||
INLINED_PROPERTIES_PROPERTY_SOURCE_NAME);
|
||||
assertArrayEquals(expectedPropertyNames, eps.getPropertyNames());
|
||||
}
|
||||
|
||||
|
||||
|
||||
+40
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -20,14 +20,17 @@ import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.ActiveProfilesResolver;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.test.context.support.ActiveProfilesUtils.*;
|
||||
@@ -211,7 +214,6 @@ public class ActiveProfilesUtilsTests extends AbstractContextConfigurationUtilsT
|
||||
/**
|
||||
* This test verifies that the actual test class, not the composed annotation,
|
||||
* is passed to the resolver.
|
||||
*
|
||||
* @since 4.0.3
|
||||
*/
|
||||
@Test
|
||||
@@ -220,6 +222,24 @@ public class ActiveProfilesUtilsTests extends AbstractContextConfigurationUtilsT
|
||||
assertResolvedProfiles(testClass, testClass.getSimpleName());
|
||||
}
|
||||
|
||||
/**
|
||||
* This test verifies that {@link DefaultActiveProfilesResolver} can be declared explicitly.
|
||||
* @since 4.1.5
|
||||
*/
|
||||
@Test
|
||||
public void resolveActiveProfilesWithDefaultActiveProfilesResolver() {
|
||||
assertResolvedProfiles(DefaultActiveProfilesResolverTestCase.class, "default");
|
||||
}
|
||||
|
||||
/**
|
||||
* This test verifies that {@link DefaultActiveProfilesResolver} can be extended.
|
||||
* @since 4.1.5
|
||||
*/
|
||||
@Test
|
||||
public void resolveActiveProfilesWithExtendedDefaultActiveProfilesResolver() {
|
||||
assertResolvedProfiles(ExtendedDefaultActiveProfilesResolverTestCase.class, "default", "foo");
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@@ -294,6 +314,14 @@ public class ActiveProfilesUtilsTests extends AbstractContextConfigurationUtilsT
|
||||
private static class TestClassVerifyingActiveProfilesResolverTestCase {
|
||||
}
|
||||
|
||||
@ActiveProfiles(profiles = "default", resolver = DefaultActiveProfilesResolver.class)
|
||||
private static class DefaultActiveProfilesResolverTestCase {
|
||||
}
|
||||
|
||||
@ActiveProfiles(profiles = "default", resolver = ExtendedDefaultActiveProfilesResolver.class)
|
||||
private static class ExtendedDefaultActiveProfilesResolverTestCase {
|
||||
}
|
||||
|
||||
@ActiveProfiles(profiles = "conflict 1", value = "conflict 2")
|
||||
private static class ConflictingProfilesAndValueTestCase {
|
||||
}
|
||||
@@ -343,4 +371,14 @@ public class ActiveProfilesUtilsTests extends AbstractContextConfigurationUtilsT
|
||||
}
|
||||
}
|
||||
|
||||
private static class ExtendedDefaultActiveProfilesResolver extends DefaultActiveProfilesResolver {
|
||||
|
||||
@Override
|
||||
public String[] resolve(Class<?> testClass) {
|
||||
List<String> profiles = new ArrayList<String>(Arrays.asList(super.resolve(testClass)));
|
||||
profiles.add("foo");
|
||||
return StringUtils.toStringArray(profiles);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.test.context.support;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.test.context.support.AnnotationConfigContextLoaderUtils.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AnnotationConfigContextLoaderUtils}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 4.1.5
|
||||
*/
|
||||
public class AnnotationConfigContextLoaderUtilsTests {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void detectDefaultConfigurationClassesWithNullDeclaringClass() {
|
||||
detectDefaultConfigurationClasses(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void detectDefaultConfigurationClassesWithoutConfigurationClass() {
|
||||
Class<?>[] configClasses = detectDefaultConfigurationClasses(NoConfigTestCase.class);
|
||||
assertNotNull(configClasses);
|
||||
assertEquals(0, configClasses.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void detectDefaultConfigurationClassesWithExplicitConfigurationAnnotation() {
|
||||
Class<?>[] configClasses = detectDefaultConfigurationClasses(ExplicitConfigTestCase.class);
|
||||
assertNotNull(configClasses);
|
||||
assertArrayEquals(new Class<?>[] { ExplicitConfigTestCase.Config.class }, configClasses);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void detectDefaultConfigurationClassesWithConfigurationMetaAnnotation() {
|
||||
Class<?>[] configClasses = detectDefaultConfigurationClasses(MetaAnnotatedConfigTestCase.class);
|
||||
assertNotNull(configClasses);
|
||||
assertArrayEquals(new Class<?>[] { MetaAnnotatedConfigTestCase.Config.class }, configClasses);
|
||||
}
|
||||
|
||||
|
||||
private static class NoConfigTestCase {
|
||||
|
||||
}
|
||||
|
||||
private static class ExplicitConfigTestCase {
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
private static @interface MetaConfig {
|
||||
}
|
||||
|
||||
private static class MetaAnnotatedConfigTestCase {
|
||||
|
||||
@MetaConfig
|
||||
static class Config {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+93
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,14 +16,22 @@
|
||||
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.mock.env.MockPropertySource;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.test.context.support.TestPropertySourceUtils.*;
|
||||
|
||||
/**
|
||||
@@ -35,6 +43,7 @@ import static org.springframework.test.context.support.TestPropertySourceUtils.*
|
||||
public class TestPropertySourceUtilsTests {
|
||||
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
private static final String[] KEY_VALUE_PAIR = new String[] { "key = value" };
|
||||
|
||||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
@@ -104,15 +113,96 @@ public class TestPropertySourceUtilsTests {
|
||||
@Test
|
||||
public void overriddenProperties() {
|
||||
assertMergedTestPropertySources(OverriddenPropertiesPropertySources.class, new String[] {
|
||||
"classpath:/foo1.xml", "classpath:/foo2.xml", "classpath:/baz.properties" }, new String[] { "key = value" });
|
||||
"classpath:/foo1.xml", "classpath:/foo2.xml", "classpath:/baz.properties" }, KEY_VALUE_PAIR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overriddenLocationsAndProperties() {
|
||||
assertMergedTestPropertySources(OverriddenLocationsAndPropertiesPropertySources.class,
|
||||
new String[] { "classpath:/baz.properties" }, new String[] { "key = value" });
|
||||
new String[] { "classpath:/baz.properties" }, KEY_VALUE_PAIR);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.1.5
|
||||
*/
|
||||
@Test
|
||||
public void addInlinedPropertiesToEnvironmentWithNullContext() {
|
||||
expectedException.expect(IllegalArgumentException.class);
|
||||
expectedException.expectMessage("context");
|
||||
addInlinedPropertiesToEnvironment((ConfigurableApplicationContext) null, KEY_VALUE_PAIR);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.1.5
|
||||
*/
|
||||
@Test
|
||||
public void addInlinedPropertiesToEnvironmentWithContextAndNullInlinedProperties() {
|
||||
expectedException.expect(IllegalArgumentException.class);
|
||||
expectedException.expectMessage("inlined");
|
||||
addInlinedPropertiesToEnvironment(mock(ConfigurableApplicationContext.class), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.1.5
|
||||
*/
|
||||
@Test
|
||||
public void addInlinedPropertiesToEnvironmentWithNullEnvironment() {
|
||||
expectedException.expect(IllegalArgumentException.class);
|
||||
expectedException.expectMessage("environment");
|
||||
addInlinedPropertiesToEnvironment((ConfigurableEnvironment) null, KEY_VALUE_PAIR);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.1.5
|
||||
*/
|
||||
@Test
|
||||
public void addInlinedPropertiesToEnvironmentWithEnvironmentAndNullInlinedProperties() {
|
||||
expectedException.expect(IllegalArgumentException.class);
|
||||
expectedException.expectMessage("inlined");
|
||||
addInlinedPropertiesToEnvironment(new MockEnvironment(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.1.5
|
||||
*/
|
||||
@Test
|
||||
public void addInlinedPropertiesToEnvironmentWithMalformedUnicodeInValue() {
|
||||
expectedException.expect(IllegalStateException.class);
|
||||
expectedException.expectMessage("Failed to load test environment property");
|
||||
addInlinedPropertiesToEnvironment(new MockEnvironment(), new String[] { "key = \\uZZZZ" });
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.1.5
|
||||
*/
|
||||
@Test
|
||||
public void addInlinedPropertiesToEnvironmentWithMultipleKeyValuePairsInSingleInlinedProperty() {
|
||||
expectedException.expect(IllegalStateException.class);
|
||||
expectedException.expectMessage("Failed to load exactly one test environment property");
|
||||
addInlinedPropertiesToEnvironment(new MockEnvironment(), new String[] { "a=b\nx=y" });
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.1.5
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void addInlinedPropertiesToEnvironmentWithEmptyProperty() {
|
||||
ConfigurableEnvironment environment = new MockEnvironment();
|
||||
MutablePropertySources propertySources = environment.getPropertySources();
|
||||
propertySources.remove(MockPropertySource.MOCK_PROPERTIES_PROPERTY_SOURCE_NAME);
|
||||
assertEquals(0, propertySources.size());
|
||||
addInlinedPropertiesToEnvironment(environment, new String[] { " " });
|
||||
assertEquals(1, propertySources.size());
|
||||
assertEquals(0, ((Map) propertySources.iterator().next().getSource()).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertInlinedPropertiesToMapWithNullInlinedProperties() {
|
||||
expectedException.expect(IllegalArgumentException.class);
|
||||
expectedException.expectMessage("inlined");
|
||||
convertInlinedPropertiesToMap(null);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -41,7 +41,7 @@ public abstract class AbstractTransactionManagementConfiguration implements Impo
|
||||
protected AnnotationAttributes enableTx;
|
||||
|
||||
/**
|
||||
* Default transaction manager.
|
||||
* Default transaction manager, as configured through a {@link TransactionManagementConfigurer}.
|
||||
*/
|
||||
protected PlatformTransactionManager txManager;
|
||||
|
||||
@@ -54,7 +54,7 @@ public abstract class AbstractTransactionManagementConfiguration implements Impo
|
||||
"@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName());
|
||||
}
|
||||
|
||||
@Autowired(required=false)
|
||||
@Autowired(required = false)
|
||||
void setConfigurers(Collection<TransactionManagementConfigurer> configurers) {
|
||||
if (CollectionUtils.isEmpty(configurers)) {
|
||||
return;
|
||||
|
||||
+16
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -21,18 +21,20 @@ import org.springframework.transaction.PlatformTransactionManager;
|
||||
/**
|
||||
* Interface to be implemented by @{@link org.springframework.context.annotation.Configuration
|
||||
* Configuration} classes annotated with @{@link EnableTransactionManagement} that wish to
|
||||
* or need to explicitly specify the default {@link PlatformTransactionManager} bean to be used
|
||||
* for annotation-driven transaction management, as opposed to the default approach of a
|
||||
* by-type lookup. One reason this might be necessary is if there are two
|
||||
* or need to explicitly specify the default {@link PlatformTransactionManager} bean to be
|
||||
* used for annotation-driven transaction management, as opposed to the default approach
|
||||
* of a by-type lookup. One reason this might be necessary is if there are two
|
||||
* {@code PlatformTransactionManager} beans present in the container.
|
||||
*
|
||||
* <p>See @{@link EnableTransactionManagement} for general examples and context; see
|
||||
* {@link #annotationDrivenTransactionManager()} for detailed instructions.
|
||||
* <p>See @{@link EnableTransactionManagement} for general examples and context;
|
||||
* see {@link #annotationDrivenTransactionManager()} for detailed instructions.
|
||||
*
|
||||
* <p>Note that in by-type lookup disambiguation cases, an alternative approach to
|
||||
* implementing this interface is to simply mark one of the offending {@code
|
||||
* PlatformTransactionManager} {@code @Bean} methods as @{@link
|
||||
* org.springframework.context.annotation.Primary Primary}.
|
||||
* implementing this interface is to simply mark one of the offending
|
||||
* {@code PlatformTransactionManager} {@code @Bean} methods as
|
||||
* @{@link org.springframework.context.annotation.Primary Primary}.
|
||||
* This is even generally preferred since it doesn't lead to early initialization
|
||||
* of the {@code PlatformTransactionManager} bean.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
@@ -44,7 +46,6 @@ public interface TransactionManagementConfigurer {
|
||||
/**
|
||||
* Return the default transaction manager bean to use for annotation-driven database
|
||||
* transaction management, i.e. when processing {@code @Transactional} methods.
|
||||
*
|
||||
* <p>There are two basic approaches to implementing this method:
|
||||
* <h3>1. Implement the method and annotate it with {@code @Bean}</h3>
|
||||
* In this case, the implementing {@code @Configuration} class implements this method,
|
||||
@@ -68,15 +69,13 @@ public interface TransactionManagementConfigurer {
|
||||
* public PlatformTransactionManager annotationDrivenTransactionManager() {
|
||||
* return txManager(); // reference the existing {@code @Bean} method above
|
||||
* }</pre>
|
||||
*
|
||||
* If taking approach #2, be sure that <em>only one</em> of the methods is marked with
|
||||
* {@code @Bean}!
|
||||
*
|
||||
* If taking approach #2, be sure that <em>only one</em> of the methods is marked
|
||||
* with {@code @Bean}!
|
||||
* <p>In either scenario #1 or #2, it is important that the
|
||||
* {@code PlatformTransactionManager} instance is managed as a Spring bean within the
|
||||
* container as all {@code PlatformTransactionManager} implementations take
|
||||
* advantage of Spring lifecycle callbacks such as {@code InitializingBean} and {@code
|
||||
* BeanFactoryAware}.
|
||||
* container as all {@code PlatformTransactionManager} implementations take advantage
|
||||
* of Spring lifecycle callbacks such as {@code InitializingBean} and
|
||||
* {@code BeanFactoryAware}.
|
||||
*/
|
||||
PlatformTransactionManager annotationDrivenTransactionManager();
|
||||
|
||||
|
||||
+15
-9
@@ -353,16 +353,12 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init
|
||||
if (txAttr == null || this.beanFactory == null) {
|
||||
return getTransactionManager();
|
||||
}
|
||||
String qualifier = (txAttr.getQualifier() != null ?
|
||||
txAttr.getQualifier() : this.transactionManagerBeanName);
|
||||
String qualifier = txAttr.getQualifier();
|
||||
if (StringUtils.hasText(qualifier)) {
|
||||
PlatformTransactionManager txManager = this.transactionManagerCache.get(qualifier);
|
||||
if (txManager == null) {
|
||||
txManager = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
|
||||
this.beanFactory, PlatformTransactionManager.class, qualifier);
|
||||
this.transactionManagerCache.putIfAbsent(qualifier, txManager);
|
||||
}
|
||||
return txManager;
|
||||
return determineQualifiedTransactionManager(qualifier);
|
||||
}
|
||||
else if (StringUtils.hasText(this.transactionManagerBeanName)) {
|
||||
return determineQualifiedTransactionManager(this.transactionManagerBeanName);
|
||||
}
|
||||
else {
|
||||
PlatformTransactionManager defaultTransactionManager = getTransactionManager();
|
||||
@@ -375,6 +371,16 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init
|
||||
}
|
||||
}
|
||||
|
||||
private PlatformTransactionManager determineQualifiedTransactionManager(String qualifier) {
|
||||
PlatformTransactionManager txManager = this.transactionManagerCache.get(qualifier);
|
||||
if (txManager == null) {
|
||||
txManager = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
|
||||
this.beanFactory, PlatformTransactionManager.class, qualifier);
|
||||
this.transactionManagerCache.putIfAbsent(qualifier, txManager);
|
||||
}
|
||||
return txManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to return a String representation of this Method
|
||||
* for use in logging. Can be overridden in subclasses to provide a
|
||||
|
||||
+45
-15
@@ -126,7 +126,7 @@ public class TransactionInterceptorTests extends AbstractTransactionAspectTests
|
||||
@Test
|
||||
public void determineTransactionManagerWithNoBeanFactory() {
|
||||
PlatformTransactionManager transactionManager = mock(PlatformTransactionManager.class);
|
||||
TransactionInterceptor ti = createTestTransactionInterceptor(null, transactionManager);
|
||||
TransactionInterceptor ti = transactionInterceptorWithTransactionManager(transactionManager, null);
|
||||
|
||||
assertSame(transactionManager, ti.determineTransactionManager(new DefaultTransactionAttribute()));
|
||||
}
|
||||
@@ -134,7 +134,7 @@ public class TransactionInterceptorTests extends AbstractTransactionAspectTests
|
||||
@Test
|
||||
public void determineTransactionManagerWithNoBeanFactoryAndNoTransactionAttribute() {
|
||||
PlatformTransactionManager transactionManager = mock(PlatformTransactionManager.class);
|
||||
TransactionInterceptor ti = createTestTransactionInterceptor(null, transactionManager);
|
||||
TransactionInterceptor ti = transactionInterceptorWithTransactionManager(transactionManager, null);
|
||||
|
||||
assertSame(transactionManager, ti.determineTransactionManager(null));
|
||||
}
|
||||
@@ -142,7 +142,7 @@ public class TransactionInterceptorTests extends AbstractTransactionAspectTests
|
||||
@Test
|
||||
public void determineTransactionManagerWithNoTransactionAttribute() {
|
||||
BeanFactory beanFactory = mock(BeanFactory.class);
|
||||
TransactionInterceptor ti = createTestTransactionInterceptor(beanFactory, null);
|
||||
TransactionInterceptor ti = simpleTransactionInterceptor(beanFactory);
|
||||
|
||||
assertNull(ti.determineTransactionManager(null));
|
||||
}
|
||||
@@ -150,7 +150,7 @@ public class TransactionInterceptorTests extends AbstractTransactionAspectTests
|
||||
@Test
|
||||
public void determineTransactionManagerWithQualifierUnknown() {
|
||||
BeanFactory beanFactory = mock(BeanFactory.class);
|
||||
TransactionInterceptor ti = createTestTransactionInterceptor(beanFactory);
|
||||
TransactionInterceptor ti = simpleTransactionInterceptor(beanFactory);
|
||||
DefaultTransactionAttribute attribute = new DefaultTransactionAttribute();
|
||||
attribute.setQualifier("fooTransactionManager");
|
||||
|
||||
@@ -163,7 +163,7 @@ public class TransactionInterceptorTests extends AbstractTransactionAspectTests
|
||||
public void determineTransactionManagerWithQualifierAndDefault() {
|
||||
BeanFactory beanFactory = mock(BeanFactory.class);
|
||||
PlatformTransactionManager transactionManager = mock(PlatformTransactionManager.class);
|
||||
TransactionInterceptor ti = createTestTransactionInterceptor(beanFactory, transactionManager);
|
||||
TransactionInterceptor ti = transactionInterceptorWithTransactionManager(transactionManager, beanFactory);
|
||||
PlatformTransactionManager fooTransactionManager =
|
||||
associateTransactionManager(beanFactory, "fooTransactionManager");
|
||||
|
||||
@@ -177,8 +177,8 @@ public class TransactionInterceptorTests extends AbstractTransactionAspectTests
|
||||
public void determineTransactionManagerWithQualifierAndDefaultName() {
|
||||
BeanFactory beanFactory = mock(BeanFactory.class);
|
||||
associateTransactionManager(beanFactory, "defaultTransactionManager");
|
||||
TransactionInterceptor ti = createTestTransactionInterceptor(beanFactory);
|
||||
ti.setTransactionManagerBeanName("defaultTransactionManager");
|
||||
TransactionInterceptor ti = transactionInterceptorWithTransactionManagerName(
|
||||
"defaultTransactionManager", beanFactory);
|
||||
|
||||
PlatformTransactionManager fooTransactionManager =
|
||||
associateTransactionManager(beanFactory, "fooTransactionManager");
|
||||
@@ -188,10 +188,24 @@ public class TransactionInterceptorTests extends AbstractTransactionAspectTests
|
||||
assertSame(fooTransactionManager, ti.determineTransactionManager(attribute));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void determineTransactionManagerWithEmptyQualifierAndDefaultName() {
|
||||
BeanFactory beanFactory = mock(BeanFactory.class);
|
||||
PlatformTransactionManager defaultTransactionManager
|
||||
= associateTransactionManager(beanFactory, "defaultTransactionManager");
|
||||
TransactionInterceptor ti = transactionInterceptorWithTransactionManagerName(
|
||||
"defaultTransactionManager", beanFactory);
|
||||
|
||||
DefaultTransactionAttribute attribute = new DefaultTransactionAttribute();
|
||||
attribute.setQualifier("");
|
||||
|
||||
assertSame(defaultTransactionManager, ti.determineTransactionManager(attribute));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void determineTransactionManagerWithQualifierSeveralTimes() {
|
||||
BeanFactory beanFactory = mock(BeanFactory.class);
|
||||
TransactionInterceptor ti = createTestTransactionInterceptor(beanFactory);
|
||||
TransactionInterceptor ti = simpleTransactionInterceptor(beanFactory);
|
||||
|
||||
PlatformTransactionManager txManager = associateTransactionManager(beanFactory, "fooTransactionManager");
|
||||
|
||||
@@ -210,8 +224,8 @@ public class TransactionInterceptorTests extends AbstractTransactionAspectTests
|
||||
@Test
|
||||
public void determineTransactionManagerWithBeanNameSeveralTimes() {
|
||||
BeanFactory beanFactory = mock(BeanFactory.class);
|
||||
TransactionInterceptor ti = createTestTransactionInterceptor(beanFactory);
|
||||
ti.setTransactionManagerBeanName("fooTransactionManager");
|
||||
TransactionInterceptor ti = transactionInterceptorWithTransactionManagerName(
|
||||
"fooTransactionManager", beanFactory);
|
||||
|
||||
PlatformTransactionManager txManager = associateTransactionManager(beanFactory, "fooTransactionManager");
|
||||
|
||||
@@ -228,7 +242,7 @@ public class TransactionInterceptorTests extends AbstractTransactionAspectTests
|
||||
@Test
|
||||
public void determineTransactionManagerDefaultSeveralTimes() {
|
||||
BeanFactory beanFactory = mock(BeanFactory.class);
|
||||
TransactionInterceptor ti = createTestTransactionInterceptor(beanFactory);
|
||||
TransactionInterceptor ti = simpleTransactionInterceptor(beanFactory);
|
||||
|
||||
PlatformTransactionManager txManager = mock(PlatformTransactionManager.class);
|
||||
given(beanFactory.getBean(PlatformTransactionManager.class)).willReturn(txManager);
|
||||
@@ -243,12 +257,16 @@ public class TransactionInterceptorTests extends AbstractTransactionAspectTests
|
||||
verify(beanFactory, times(1)).getBean(PlatformTransactionManager.class);
|
||||
}
|
||||
|
||||
private TransactionInterceptor createTestTransactionInterceptor(BeanFactory beanFactory,
|
||||
PlatformTransactionManager transactionManager) {
|
||||
private TransactionInterceptor createTransactionInterceptor(BeanFactory beanFactory,
|
||||
String transactionManagerName, PlatformTransactionManager transactionManager) {
|
||||
TransactionInterceptor ti = new TransactionInterceptor();
|
||||
if (beanFactory != null) {
|
||||
ti.setBeanFactory(beanFactory);
|
||||
}
|
||||
if (transactionManagerName != null) {
|
||||
ti.setTransactionManagerBeanName(transactionManagerName);
|
||||
|
||||
}
|
||||
if (transactionManager != null) {
|
||||
ti.setTransactionManager(transactionManager);
|
||||
}
|
||||
@@ -257,8 +275,20 @@ public class TransactionInterceptorTests extends AbstractTransactionAspectTests
|
||||
return ti;
|
||||
}
|
||||
|
||||
private TransactionInterceptor createTestTransactionInterceptor(BeanFactory beanFactory) {
|
||||
return createTestTransactionInterceptor(beanFactory, null);
|
||||
private TransactionInterceptor transactionInterceptorWithTransactionManager(
|
||||
PlatformTransactionManager transactionManager, BeanFactory beanFactory) {
|
||||
|
||||
return createTransactionInterceptor(beanFactory, null, transactionManager);
|
||||
}
|
||||
|
||||
private TransactionInterceptor transactionInterceptorWithTransactionManagerName(
|
||||
String transactionManagerName, BeanFactory beanFactory) {
|
||||
|
||||
return createTransactionInterceptor(beanFactory, transactionManagerName, null);
|
||||
}
|
||||
|
||||
private TransactionInterceptor simpleTransactionInterceptor(BeanFactory beanFactory) {
|
||||
return createTransactionInterceptor(beanFactory, null, null);
|
||||
}
|
||||
|
||||
private PlatformTransactionManager associateTransactionManager(BeanFactory beanFactory, String name) {
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link ClientHttpResponse} that reads the request's body into memory,
|
||||
* Simple implementation of {@link ClientHttpResponse} that reads the response's body into memory,
|
||||
* thus allowing for multiple invocations of {@link #getBody()}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -65,7 +65,7 @@ class Netty4ClientHttpRequest extends AbstractAsyncClientHttpRequest implements
|
||||
this.bootstrap = bootstrap;
|
||||
this.uri = uri;
|
||||
this.method = method;
|
||||
this.body = new ByteBufOutputStream(Unpooled.buffer(maxRequestSize));
|
||||
this.body = new ByteBufOutputStream(Unpooled.buffer(1024, maxRequestSize));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+27
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -43,6 +43,7 @@ import org.springframework.util.Assert;
|
||||
* across multiple clients.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.1.2
|
||||
*/
|
||||
public class Netty4ClientHttpRequestFactory implements ClientHttpRequestFactory,
|
||||
@@ -51,9 +52,17 @@ public class Netty4ClientHttpRequestFactory implements ClientHttpRequestFactory,
|
||||
/**
|
||||
* The default maximum request size.
|
||||
* @see #setMaxRequestSize(int)
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public static final int DEFAULT_MAX_REQUEST_SIZE = 1024 * 1024 * 10;
|
||||
|
||||
/**
|
||||
* The default maximum response size.
|
||||
* @see #setMaxResponseSize(int)
|
||||
*/
|
||||
public static final int DEFAULT_MAX_RESPONSE_SIZE = 1024 * 1024 * 10;
|
||||
|
||||
|
||||
private final EventLoopGroup eventLoopGroup;
|
||||
|
||||
@@ -61,6 +70,8 @@ public class Netty4ClientHttpRequestFactory implements ClientHttpRequestFactory,
|
||||
|
||||
private int maxRequestSize = DEFAULT_MAX_REQUEST_SIZE;
|
||||
|
||||
private int maxResponseSize = DEFAULT_MAX_REQUEST_SIZE;
|
||||
|
||||
private SslContext sslContext;
|
||||
|
||||
private volatile Bootstrap bootstrap;
|
||||
@@ -94,11 +105,25 @@ public class Netty4ClientHttpRequestFactory implements ClientHttpRequestFactory,
|
||||
* Set the default maximum request size.
|
||||
* <p>By default this is set to {@link #DEFAULT_MAX_REQUEST_SIZE}.
|
||||
* @see HttpObjectAggregator#HttpObjectAggregator(int)
|
||||
* @deprecated as of 4.1.5 this property is no longer supported;
|
||||
* effectively renamed to {@link #setMaxResponseSize(int)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setMaxRequestSize(int maxRequestSize) {
|
||||
this.maxRequestSize = maxRequestSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default maximum response size.
|
||||
* <p>By default this is set to {@link #DEFAULT_MAX_RESPONSE_SIZE}.
|
||||
* @see HttpObjectAggregator#HttpObjectAggregator(int)
|
||||
* @since 4.1.5
|
||||
*/
|
||||
public void setMaxResponseSize(int maxResponseSize) {
|
||||
this.maxResponseSize = maxResponseSize;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the SSL context. When configured it is used to create and insert an
|
||||
* {@link io.netty.handler.ssl.SslHandler} in the channel pipeline.
|
||||
@@ -120,7 +145,7 @@ public class Netty4ClientHttpRequestFactory implements ClientHttpRequestFactory,
|
||||
pipeline.addLast(sslContext.newHandler(channel.alloc()));
|
||||
}
|
||||
pipeline.addLast(new HttpClientCodec());
|
||||
pipeline.addLast(new HttpObjectAggregator(maxRequestSize));
|
||||
pipeline.addLast(new HttpObjectAggregator(maxResponseSize));
|
||||
}
|
||||
});
|
||||
this.bootstrap = bootstrap;
|
||||
|
||||
+3
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* 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.
|
||||
@@ -29,7 +29,6 @@ import org.springframework.http.MediaType;
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.2
|
||||
*
|
||||
* @see org.springframework.core.ParameterizedTypeReference
|
||||
*/
|
||||
public interface GenericHttpMessageConverter<T> extends HttpMessageConverter<T> {
|
||||
@@ -38,7 +37,7 @@ public interface GenericHttpMessageConverter<T> extends HttpMessageConverter<T>
|
||||
* Indicates whether the given type can be read by this converter.
|
||||
* @param type the type to test for readability
|
||||
* @param contextClass a context class for the target type, for example a class
|
||||
* in which the target type appears in a method signature, can be {@code null}
|
||||
* in which the target type appears in a method signature (can be {@code null})
|
||||
* @param mediaType the media type to read, can be {@code null} if not specified.
|
||||
* Typically the value of a {@code Content-Type} header.
|
||||
* @return {@code true} if readable; {@code false} otherwise
|
||||
@@ -51,7 +50,7 @@ public interface GenericHttpMessageConverter<T> extends HttpMessageConverter<T>
|
||||
* been passed to the {@link #canRead canRead} method of this interface,
|
||||
* which must have returned {@code true}.
|
||||
* @param contextClass a context class for the target type, for example a class
|
||||
* in which the target type appears in a method signature, can be {@code null}
|
||||
* in which the target type appears in a method signature (can be {@code null})
|
||||
* @param inputMessage the HTTP input message to read from
|
||||
* @return the converted object
|
||||
* @throws IOException in case of I/O errors
|
||||
|
||||
+124
-32
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -18,11 +18,14 @@ package org.springframework.http.converter.json;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
@@ -46,6 +49,7 @@ import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A builder used to create {@link ObjectMapper} instances with a fluent API.
|
||||
@@ -75,6 +79,10 @@ public class Jackson2ObjectMapperBuilder {
|
||||
|
||||
private DateFormat dateFormat;
|
||||
|
||||
private Locale locale;
|
||||
|
||||
private TimeZone timeZone;
|
||||
|
||||
private AnnotationIntrospector annotationIntrospector;
|
||||
|
||||
private PropertyNamingStrategy propertyNamingStrategy;
|
||||
@@ -91,9 +99,11 @@ public class Jackson2ObjectMapperBuilder {
|
||||
|
||||
private List<Module> modules;
|
||||
|
||||
private Class<? extends Module>[] modulesToInstall;
|
||||
private Class<? extends Module>[] moduleClasses;
|
||||
|
||||
private boolean findModulesViaServiceLoader;
|
||||
private boolean findModulesViaServiceLoader = false;
|
||||
|
||||
private boolean findWellKnownModules = true;
|
||||
|
||||
private ClassLoader moduleClassLoader = getClass().getClassLoader();
|
||||
|
||||
@@ -134,6 +144,48 @@ public class Jackson2ObjectMapperBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the default {@link Locale} to use for formatting.
|
||||
* Default value used is {@link Locale#getDefault()}.
|
||||
* @since 4.1.5
|
||||
*/
|
||||
public Jackson2ObjectMapperBuilder locale(Locale locale) {
|
||||
this.locale = locale;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the default {@link Locale} to use for formatting.
|
||||
* Default value used is {@link Locale#getDefault()}.
|
||||
* @param localeString the locale ID as a String representation
|
||||
* @since 4.1.5
|
||||
*/
|
||||
public Jackson2ObjectMapperBuilder locale(String localeString) {
|
||||
this.locale = StringUtils.parseLocaleString(localeString);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the default {@link TimeZone} to use for formatting.
|
||||
* Default value used is UTC (NOT local timezone).
|
||||
* @since 4.1.5
|
||||
*/
|
||||
public Jackson2ObjectMapperBuilder timeZone(TimeZone timeZone) {
|
||||
this.timeZone = timeZone;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the default {@link TimeZone} to use for formatting.
|
||||
* Default value used is UTC (NOT local timezone).
|
||||
* @param timeZoneString the zone ID as a String representation
|
||||
* @since 4.1.5
|
||||
*/
|
||||
public Jackson2ObjectMapperBuilder timeZone(String timeZoneString) {
|
||||
this.timeZone = StringUtils.parseTimeZoneString(timeZoneString);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an {@link AnnotationIntrospector} for both serialization and deserialization.
|
||||
*/
|
||||
@@ -232,7 +284,7 @@ public class Jackson2ObjectMapperBuilder {
|
||||
* @see com.fasterxml.jackson.databind.ObjectMapper#addMixInAnnotations(Class, Class)
|
||||
*/
|
||||
public Jackson2ObjectMapperBuilder mixIn(Class<?> target, Class<?> mixinSource) {
|
||||
if (mixIns != null) {
|
||||
if (mixinSource != null) {
|
||||
this.mixIns.put(target, mixinSource);
|
||||
}
|
||||
return this;
|
||||
@@ -337,6 +389,21 @@ public class Jackson2ObjectMapperBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify one or more modules to be registered with the {@link ObjectMapper}.
|
||||
* <p>Note: If this is set, no finding of modules is going to happen - not by
|
||||
* Jackson, and not by Spring either (see {@link #findModulesViaServiceLoader}).
|
||||
* As a consequence, specifying an empty list here will suppress any kind of
|
||||
* module detection.
|
||||
* <p>Specify either this or {@link #modulesToInstall}, not both.
|
||||
* @since 4.1.5
|
||||
* @see #modules(List)
|
||||
* @see com.fasterxml.jackson.databind.Module
|
||||
*/
|
||||
public Jackson2ObjectMapperBuilder modules(Module... modules) {
|
||||
return modules(Arrays.asList(modules));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a complete list of modules to be registered with the {@link ObjectMapper}.
|
||||
* <p>Note: If this is set, no finding of modules is going to happen - not by
|
||||
@@ -344,24 +411,46 @@ public class Jackson2ObjectMapperBuilder {
|
||||
* As a consequence, specifying an empty list here will suppress any kind of
|
||||
* module detection.
|
||||
* <p>Specify either this or {@link #modulesToInstall}, not both.
|
||||
* @see #modules(Module...)
|
||||
* @see com.fasterxml.jackson.databind.Module
|
||||
*/
|
||||
public Jackson2ObjectMapperBuilder modules(List<Module> modules) {
|
||||
this.modules = new LinkedList<Module>(modules);
|
||||
this.findModulesViaServiceLoader = false;
|
||||
this.findWellKnownModules = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify one or more modules by class (or class name in XML),
|
||||
* to be registered with the {@link ObjectMapper}.
|
||||
* <p>Modules specified here will be registered in combination with
|
||||
* Specify one or more modules to be registered with the {@link ObjectMapper}.
|
||||
* <p>Modules specified here will be registered after
|
||||
* Spring's autodetection of JSR-310 and Joda-Time, or Jackson's
|
||||
* finding of modules (see {@link #findModulesViaServiceLoader}).
|
||||
* finding of modules (see {@link #findModulesViaServiceLoader}),
|
||||
* allowing to eventually override their configuration.
|
||||
* <p>Specify either this or {@link #modules}, not both.
|
||||
* @since 4.1.5
|
||||
* @see com.fasterxml.jackson.databind.Module
|
||||
*/
|
||||
public Jackson2ObjectMapperBuilder modulesToInstall(Module... modules) {
|
||||
this.modules = Arrays.asList(modules);
|
||||
this.findWellKnownModules = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify one or more modules by class to be registered with
|
||||
* the {@link ObjectMapper}.
|
||||
* <p>Modules specified here will be registered after
|
||||
* Spring's autodetection of JSR-310 and Joda-Time, or Jackson's
|
||||
* finding of modules (see {@link #findModulesViaServiceLoader}),
|
||||
* allowing to eventually override their configuration.
|
||||
* <p>Specify either this or {@link #modules}, not both.
|
||||
* @see #modulesToInstall(Module...)
|
||||
* @see com.fasterxml.jackson.databind.Module
|
||||
*/
|
||||
public Jackson2ObjectMapperBuilder modulesToInstall(Class<? extends Module>... modules) {
|
||||
this.modulesToInstall = modules;
|
||||
this.moduleClasses = modules;
|
||||
this.findWellKnownModules = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -444,9 +533,35 @@ public class Jackson2ObjectMapperBuilder {
|
||||
public void configure(ObjectMapper objectMapper) {
|
||||
Assert.notNull(objectMapper, "ObjectMapper must not be null");
|
||||
|
||||
if (this.findModulesViaServiceLoader) {
|
||||
// Jackson 2.2+
|
||||
objectMapper.registerModules(ObjectMapper.findModules(this.moduleClassLoader));
|
||||
}
|
||||
else if (this.findWellKnownModules) {
|
||||
registerWellKnownModulesIfAvailable(objectMapper);
|
||||
}
|
||||
|
||||
if (this.modules != null) {
|
||||
for (Module module : this.modules) {
|
||||
// Using Jackson 2.0+ registerModule method, not Jackson 2.2+ registerModules
|
||||
objectMapper.registerModule(module);
|
||||
}
|
||||
}
|
||||
if (this.moduleClasses != null) {
|
||||
for (Class<? extends Module> module : this.moduleClasses) {
|
||||
objectMapper.registerModule(BeanUtils.instantiate(module));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.dateFormat != null) {
|
||||
objectMapper.setDateFormat(this.dateFormat);
|
||||
}
|
||||
if (this.locale != null) {
|
||||
objectMapper.setLocale(this.locale);
|
||||
}
|
||||
if (this.timeZone != null) {
|
||||
objectMapper.setTimeZone(this.timeZone);
|
||||
}
|
||||
|
||||
if (this.annotationIntrospector != null) {
|
||||
objectMapper.setAnnotationIntrospector(this.annotationIntrospector);
|
||||
@@ -468,29 +583,6 @@ public class Jackson2ObjectMapperBuilder {
|
||||
configureFeature(objectMapper, feature, this.features.get(feature));
|
||||
}
|
||||
|
||||
if (this.modules != null) {
|
||||
// Complete list of modules given
|
||||
for (Module module : this.modules) {
|
||||
// Using Jackson 2.0+ registerModule method, not Jackson 2.2+ registerModules
|
||||
objectMapper.registerModule(module);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Combination of modules by class names specified and class presence in the classpath
|
||||
if (this.modulesToInstall != null) {
|
||||
for (Class<? extends Module> module : this.modulesToInstall) {
|
||||
objectMapper.registerModule(BeanUtils.instantiate(module));
|
||||
}
|
||||
}
|
||||
if (this.findModulesViaServiceLoader) {
|
||||
// Jackson 2.2+
|
||||
objectMapper.registerModules(ObjectMapper.findModules(this.moduleClassLoader));
|
||||
}
|
||||
else {
|
||||
registerWellKnownModulesIfAvailable(objectMapper);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.propertyNamingStrategy != null) {
|
||||
objectMapper.setPropertyNamingStrategy(this.propertyNamingStrategy);
|
||||
}
|
||||
|
||||
+24
-3
@@ -19,7 +19,9 @@ package org.springframework.http.converter.json;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.AnnotationIntrospector;
|
||||
@@ -171,6 +173,24 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
|
||||
this.builder.simpleDateFormat(format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the default {@link Locale} to use for formatting.
|
||||
* Default value used is {@link Locale#getDefault()}.
|
||||
* @since 4.1.5
|
||||
*/
|
||||
public void setLocale(Locale locale) {
|
||||
this.builder.locale(locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the default {@link TimeZone} to use for formatting.
|
||||
* Default value used is UTC (NOT local timezone).
|
||||
* @since 4.1.5
|
||||
*/
|
||||
public void setTimeZone(TimeZone timeZone) {
|
||||
this.builder.timeZone(timeZone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an {@link AnnotationIntrospector} for both serialization and deserialization.
|
||||
*/
|
||||
@@ -316,11 +336,12 @@ public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify one or more modules by class (or class name in XML),
|
||||
* Specify one or more modules by class (or class name in XML)
|
||||
* to be registered with the {@link ObjectMapper}.
|
||||
* <p>Modules specified here will be registered in combination with
|
||||
* <p>Modules specified here will be registered after
|
||||
* Spring's autodetection of JSR-310 and Joda-Time, or Jackson's
|
||||
* finding of modules (see {@link #setFindModulesViaServiceLoader}).
|
||||
* finding of modules (see {@link #setFindModulesViaServiceLoader}),
|
||||
* allowing to eventually override their configuration.
|
||||
* <p>Specify either this or {@link #setModules}, not both.
|
||||
* @since 4.0.1
|
||||
* @see com.fasterxml.jackson.databind.Module
|
||||
|
||||
+6
-40
@@ -23,7 +23,6 @@ import java.util.List;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
@@ -80,10 +79,12 @@ public class HttpMessageConverterExtractor<T> implements ResponseExtractor<T> {
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public T extractData(ClientHttpResponse response) throws IOException {
|
||||
if (!hasMessageBody(response)) {
|
||||
|
||||
MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
|
||||
if(!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
|
||||
return null;
|
||||
}
|
||||
MediaType contentType = getContentType(response);
|
||||
MediaType contentType = getContentType(responseWrapper);
|
||||
|
||||
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
|
||||
if (messageConverter instanceof GenericHttpMessageConverter) {
|
||||
@@ -93,7 +94,7 @@ public class HttpMessageConverterExtractor<T> implements ResponseExtractor<T> {
|
||||
logger.debug("Reading [" + this.responseType + "] as \"" +
|
||||
contentType + "\" using [" + messageConverter + "]");
|
||||
}
|
||||
return (T) genericMessageConverter.read(this.responseType, null, response);
|
||||
return (T) genericMessageConverter.read(this.responseType, null, responseWrapper);
|
||||
}
|
||||
}
|
||||
if (this.responseClass != null) {
|
||||
@@ -102,7 +103,7 @@ public class HttpMessageConverterExtractor<T> implements ResponseExtractor<T> {
|
||||
logger.debug("Reading [" + this.responseClass.getName() + "] as \"" +
|
||||
contentType + "\" using [" + messageConverter + "]");
|
||||
}
|
||||
return (T) messageConverter.read((Class) this.responseClass, response);
|
||||
return (T) messageConverter.read((Class) this.responseClass, responseWrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,39 +123,4 @@ public class HttpMessageConverterExtractor<T> implements ResponseExtractor<T> {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the given response has a message body.
|
||||
* <p>Default implementation returns {@code false} for:
|
||||
* <ul>
|
||||
* <li>a response status of {@code 204} or {@code 304}</li>
|
||||
* <li>a {@code Content-Length} of {@code 0}</li>
|
||||
* <li>no indication of content (no {@code Content-Length} nor {@code Transfer-encoding: chunked}) and
|
||||
* a ({@code Connection: closed}) header. See rfc7230 section 3.4</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param response the response to check for a message body
|
||||
* @return {@code true} if the response has a body, {@code false} otherwise
|
||||
* @throws IOException in case of I/O errors
|
||||
*/
|
||||
protected boolean hasMessageBody(ClientHttpResponse response) throws IOException {
|
||||
HttpStatus responseStatus = response.getStatusCode();
|
||||
if (responseStatus == HttpStatus.NO_CONTENT ||
|
||||
responseStatus == HttpStatus.NOT_MODIFIED) {
|
||||
return false;
|
||||
}
|
||||
HttpHeaders headers = response.getHeaders();
|
||||
long contentLength = headers.getContentLength();
|
||||
if(contentLength == 0) {
|
||||
return false;
|
||||
}
|
||||
boolean chunked = headers.containsKey(HttpHeaders.TRANSFER_ENCODING)
|
||||
&& headers.get(HttpHeaders.TRANSFER_ENCODING).contains("chunked");
|
||||
boolean closed = headers.containsKey(HttpHeaders.CONNECTION)
|
||||
&& headers.getConnection().contains("close");
|
||||
if(!chunked && contentLength == -1 && closed) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PushbackInputStream;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ClientHttpResponse} that can not only check if
|
||||
* the response has a message body, but also if its length is 0 (i.e. empty)
|
||||
* by actually reading the input stream.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 4.1.5
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7230#section-3.3.3">rfc7230 Section 3.3.3</a>
|
||||
*/
|
||||
class MessageBodyClientHttpResponseWrapper implements ClientHttpResponse {
|
||||
|
||||
private final ClientHttpResponse response;
|
||||
|
||||
private PushbackInputStream pushbackInputStream;
|
||||
|
||||
|
||||
public MessageBodyClientHttpResponseWrapper(ClientHttpResponse response) throws IOException {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicates whether the response has a message body.
|
||||
* <p>Implementation returns {@code false} for:
|
||||
* <ul>
|
||||
* <li>a response status of {@code 1XX}, {@code 204} or {@code 304}</li>
|
||||
* <li>a {@code Content-Length} header of {@code 0}</li>
|
||||
* </ul>
|
||||
* @return {@code true} if the response has a message body, {@code false} otherwise
|
||||
* @throws IOException in case of I/O errors
|
||||
*/
|
||||
public boolean hasMessageBody() throws IOException {
|
||||
HttpStatus responseStatus = this.getStatusCode();
|
||||
if (responseStatus.is1xxInformational() || responseStatus == HttpStatus.NO_CONTENT ||
|
||||
responseStatus == HttpStatus.NOT_MODIFIED) {
|
||||
return false;
|
||||
}
|
||||
else if(this.getHeaders().getContentLength() == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the response has an empty message body.
|
||||
* <p>Implementation tries to read the first bytes of the response stream:
|
||||
* <ul>
|
||||
* <li>if no bytes are available, the message body is empty</li>
|
||||
* <li>otherwise it is not empty and the stream is reset to its start for further reading</li>
|
||||
* </ul>
|
||||
* @return {@code true} if the response has a zero-length message body, {@code false} otherwise
|
||||
* @throws IOException in case of I/O errors
|
||||
*/
|
||||
public boolean hasEmptyMessageBody() throws IOException {
|
||||
InputStream body = this.response.getBody();
|
||||
if (body == null) {
|
||||
return true;
|
||||
}
|
||||
else if (body.markSupported()) {
|
||||
body.mark(1);
|
||||
if (body.read() == -1) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
body.reset();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.pushbackInputStream = new PushbackInputStream(body);
|
||||
int b = this.pushbackInputStream.read();
|
||||
if (b == -1) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
this.pushbackInputStream.unread(b);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return this.response.getHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getBody() throws IOException {
|
||||
return (this.pushbackInputStream != null ? this.pushbackInputStream : this.response.getBody());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpStatus getStatusCode() throws IOException {
|
||||
return this.response.getStatusCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawStatusCode() throws IOException {
|
||||
return this.response.getRawStatusCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusText() throws IOException {
|
||||
return this.response.getStatusText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.response.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -596,7 +596,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
|
||||
logger.debug(method.name() + " request for \"" + url + "\" resulted in " +
|
||||
response.getRawStatusCode() + " (" + response.getStatusText() + ")");
|
||||
}
|
||||
catch (IOException e) {
|
||||
catch (IOException ex) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
@@ -608,7 +608,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
|
||||
logger.warn(method.name() + " request for \"" + url + "\" resulted in " +
|
||||
response.getRawStatusCode() + " (" + response.getStatusText() + "); invoking error handler");
|
||||
}
|
||||
catch (IOException e) {
|
||||
catch (IOException ex) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
@@ -668,12 +668,11 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
|
||||
|
||||
@Override
|
||||
public void doWithRequest(ClientHttpRequest request) throws IOException {
|
||||
if (responseType != null) {
|
||||
if (this.responseType != null) {
|
||||
Class<?> responseClass = null;
|
||||
if (responseType instanceof Class) {
|
||||
responseClass = (Class<?>) responseType;
|
||||
if (this.responseType instanceof Class) {
|
||||
responseClass = (Class<?>) this.responseType;
|
||||
}
|
||||
|
||||
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
|
||||
for (HttpMessageConverter<?> converter : getMessageConverters()) {
|
||||
if (responseClass != null) {
|
||||
@@ -682,13 +681,11 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
|
||||
}
|
||||
}
|
||||
else if (converter instanceof GenericHttpMessageConverter) {
|
||||
|
||||
GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
|
||||
if (genericConverter.canRead(responseType, null, null)) {
|
||||
if (genericConverter.canRead(this.responseType, null, null)) {
|
||||
allSupportedMediaTypes.addAll(getSupportedMediaTypes(converter));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (!allSupportedMediaTypes.isEmpty()) {
|
||||
MediaType.sortBySpecificity(allSupportedMediaTypes);
|
||||
@@ -744,9 +741,9 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
|
||||
@SuppressWarnings("unchecked")
|
||||
public void doWithRequest(ClientHttpRequest httpRequest) throws IOException {
|
||||
super.doWithRequest(httpRequest);
|
||||
if (!requestEntity.hasBody()) {
|
||||
if (!this.requestEntity.hasBody()) {
|
||||
HttpHeaders httpHeaders = httpRequest.getHeaders();
|
||||
HttpHeaders requestHeaders = requestEntity.getHeaders();
|
||||
HttpHeaders requestHeaders = this.requestEntity.getHeaders();
|
||||
if (!requestHeaders.isEmpty()) {
|
||||
httpHeaders.putAll(requestHeaders);
|
||||
}
|
||||
@@ -755,9 +752,9 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
|
||||
}
|
||||
}
|
||||
else {
|
||||
Object requestBody = requestEntity.getBody();
|
||||
Object requestBody = this.requestEntity.getBody();
|
||||
Class<?> requestType = requestBody.getClass();
|
||||
HttpHeaders requestHeaders = requestEntity.getHeaders();
|
||||
HttpHeaders requestHeaders = this.requestEntity.getHeaders();
|
||||
MediaType requestContentType = requestHeaders.getContentType();
|
||||
for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
|
||||
if (messageConverter.canWrite(requestType, requestContentType)) {
|
||||
|
||||
+20
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -211,14 +211,15 @@ public abstract class AbstractRequestLoggingFilter extends OncePerRequestFilter
|
||||
requestToUse = new ContentCachingRequestWrapper(request);
|
||||
}
|
||||
|
||||
if (isFirstRequest) {
|
||||
boolean shouldLog = shouldLog(requestToUse);
|
||||
if (shouldLog && isFirstRequest) {
|
||||
beforeRequest(requestToUse, getBeforeMessage(requestToUse));
|
||||
}
|
||||
try {
|
||||
filterChain.doFilter(requestToUse, response);
|
||||
}
|
||||
finally {
|
||||
if (!isAsyncStarted(requestToUse)) {
|
||||
if (shouldLog && !isAsyncStarted(requestToUse)) {
|
||||
afterRequest(requestToUse, getAfterMessage(requestToUse));
|
||||
}
|
||||
}
|
||||
@@ -289,6 +290,22 @@ public abstract class AbstractRequestLoggingFilter extends OncePerRequestFilter
|
||||
return msg.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether to call the {@link #beforeRequest}/{@link #afterRequest}
|
||||
* methods for the current request, i.e. whether logging is currently active
|
||||
* (and the log message is worth building).
|
||||
* <p>The default implementation always returns {@code true}. Subclasses may
|
||||
* override this with a log level check.
|
||||
* @param request current HTTP request
|
||||
* @return {@code true} if the before/after method should get called;
|
||||
* {@code false} otherwise
|
||||
* @since 4.1.5
|
||||
*/
|
||||
protected boolean shouldLog(HttpServletRequest request) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concrete subclasses should implement this method to write a log message
|
||||
* <i>before</i> the request is processed.
|
||||
|
||||
+9
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
* 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.
|
||||
@@ -23,6 +23,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
* (and optionally the query string) to the Commons Log.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2.5
|
||||
* @see #setIncludeQueryString
|
||||
* @see #setBeforeMessagePrefix
|
||||
@@ -33,14 +34,17 @@ import javax.servlet.http.HttpServletRequest;
|
||||
*/
|
||||
public class CommonsRequestLoggingFilter extends AbstractRequestLoggingFilter {
|
||||
|
||||
@Override
|
||||
protected boolean shouldLog(HttpServletRequest request) {
|
||||
return logger.isDebugEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a log message before the request is processed.
|
||||
*/
|
||||
@Override
|
||||
protected void beforeRequest(HttpServletRequest request, String message) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(message);
|
||||
}
|
||||
logger.debug(message);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,9 +52,7 @@ public class CommonsRequestLoggingFilter extends AbstractRequestLoggingFilter {
|
||||
*/
|
||||
@Override
|
||||
protected void afterRequest(HttpServletRequest request, String message) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(message);
|
||||
}
|
||||
logger.debug(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+22
-24
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -56,7 +56,7 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
*/
|
||||
public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
|
||||
|
||||
protected Log logger = LogFactory.getLog(this.getClass());
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final boolean annotationNotRequired;
|
||||
|
||||
@@ -72,8 +72,8 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||
|
||||
|
||||
/**
|
||||
* @return true if the parameter is annotated with {@link ModelAttribute}
|
||||
* or in default resolution mode also if it is not a simple type.
|
||||
* Returns {@code true} if the parameter is annotated with {@link ModelAttribute}
|
||||
* or in default resolution mode, and also if it is not a simple type.
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
@@ -109,15 +109,12 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||
if (binder.getTarget() != null) {
|
||||
bindRequestParameters(binder, webRequest);
|
||||
validateIfApplicable(binder, parameter);
|
||||
if (binder.getBindingResult().hasErrors()) {
|
||||
if (isBindExceptionRequired(binder, parameter)) {
|
||||
throw new BindException(binder.getBindingResult());
|
||||
}
|
||||
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
|
||||
throw new BindException(binder.getBindingResult());
|
||||
}
|
||||
}
|
||||
|
||||
// Add resolved attribute and BindingResult at the end of the model
|
||||
|
||||
Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
|
||||
mavContainer.removeAttributes(bindingResultModel);
|
||||
mavContainer.addAllAttributes(bindingResultModel);
|
||||
@@ -129,15 +126,15 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||
* Extension point to create the model attribute if not found in the model.
|
||||
* The default implementation uses the default constructor.
|
||||
* @param attributeName the name of the attribute (never {@code null})
|
||||
* @param parameter the method parameter
|
||||
* @param methodParam the method parameter
|
||||
* @param binderFactory for creating WebDataBinder instance
|
||||
* @param request the current request
|
||||
* @return the created model attribute (never {@code null})
|
||||
*/
|
||||
protected Object createAttribute(String attributeName, MethodParameter parameter,
|
||||
WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
|
||||
protected Object createAttribute(String attributeName, MethodParameter methodParam,
|
||||
WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
|
||||
|
||||
return BeanUtils.instantiateClass(parameter.getParameterType());
|
||||
return BeanUtils.instantiateClass(methodParam.getParameterType());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,12 +148,14 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||
|
||||
/**
|
||||
* Validate the model attribute if applicable.
|
||||
* <p>The default implementation checks for {@code @javax.validation.Valid}.
|
||||
* <p>The default implementation checks for {@code @javax.validation.Valid},
|
||||
* Spring's {@link org.springframework.validation.annotation.Validated},
|
||||
* and custom annotations whose name starts with "Valid".
|
||||
* @param binder the DataBinder to be used
|
||||
* @param parameter the method parameter
|
||||
* @param methodParam the method parameter
|
||||
*/
|
||||
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
|
||||
Annotation[] annotations = parameter.getParameterAnnotations();
|
||||
protected void validateIfApplicable(WebDataBinder binder, MethodParameter methodParam) {
|
||||
Annotation[] annotations = methodParam.getParameterAnnotations();
|
||||
for (Annotation ann : annotations) {
|
||||
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
|
||||
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
|
||||
@@ -169,16 +168,15 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to raise a {@link BindException} on validation errors.
|
||||
* Whether to raise a fatal bind exception on validation errors.
|
||||
* @param binder the data binder used to perform data binding
|
||||
* @param parameter the method argument
|
||||
* @return {@code true} if the next method argument is not of type {@link Errors}.
|
||||
* @param methodParam the method argument
|
||||
* @return {@code true} if the next method argument is not of type {@link Errors}
|
||||
*/
|
||||
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {
|
||||
int i = parameter.getParameterIndex();
|
||||
Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
|
||||
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter methodParam) {
|
||||
int i = methodParam.getParameterIndex();
|
||||
Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes();
|
||||
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
|
||||
|
||||
return !hasBindingResult;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* 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.
|
||||
@@ -94,6 +94,9 @@ public interface MultipartFile {
|
||||
* <p>If the file has been moved in the filesystem, this operation cannot
|
||||
* be invoked again. Therefore, call this method just once to be able to
|
||||
* work with any storage mechanism.
|
||||
* <p><strong>Note:</strong> when using Servlet 3.0 multipart support you
|
||||
* need to configure the location relative to which files will be copied
|
||||
* as explained in {@link javax.servlet.http.Part#write}.
|
||||
* @param dest the destination file
|
||||
* @throws IOException in case of reading or writing errors
|
||||
* @throws IllegalStateException if the file has already been moved
|
||||
|
||||
+50
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -20,6 +20,14 @@ import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
@@ -36,6 +44,10 @@ import javax.servlet.http.HttpServletRequestWrapper;
|
||||
*/
|
||||
public class ContentCachingRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
private static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";
|
||||
|
||||
private static final String METHOD_POST = "POST";
|
||||
|
||||
private final ByteArrayOutputStream cachedContent;
|
||||
|
||||
private ServletInputStream inputStream;
|
||||
@@ -80,9 +92,46 @@ public class ContentCachingRequestWrapper extends HttpServletRequestWrapper {
|
||||
* Return the cached request content as a byte array.
|
||||
*/
|
||||
public byte[] getContentAsByteArray() {
|
||||
if(this.cachedContent.size() == 0 && isFormPost()) {
|
||||
writeRequestParamsToContent();
|
||||
}
|
||||
return this.cachedContent.toByteArray();
|
||||
}
|
||||
|
||||
private boolean isFormPost() {
|
||||
return (getContentType() != null && getContentType().contains(FORM_CONTENT_TYPE) &&
|
||||
METHOD_POST.equalsIgnoreCase(getMethod()));
|
||||
}
|
||||
|
||||
private void writeRequestParamsToContent() {
|
||||
try {
|
||||
if (this.cachedContent.size() == 0) {
|
||||
String requestEncoding = getCharacterEncoding();
|
||||
Map<String, String[]> form = getParameterMap();
|
||||
for (Iterator<String> nameIterator = form.keySet().iterator(); nameIterator.hasNext(); ) {
|
||||
String name = nameIterator.next();
|
||||
List<String> values = Arrays.asList(form.get(name));
|
||||
for (Iterator<String> valueIterator = values.iterator(); valueIterator.hasNext(); ) {
|
||||
String value = valueIterator.next();
|
||||
cachedContent.write(URLEncoder.encode(name, requestEncoding).getBytes());
|
||||
if (value != null) {
|
||||
cachedContent.write('=');
|
||||
cachedContent.write(URLEncoder.encode(value, requestEncoding).getBytes());
|
||||
if (valueIterator.hasNext()) {
|
||||
cachedContent.write('&');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nameIterator.hasNext()) {
|
||||
cachedContent.write('&');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private class ContentCachingInputStream extends ServletInputStream {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -24,6 +24,7 @@ import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
@@ -263,6 +264,55 @@ public class UriComponentsBuilder implements Cloneable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code UriComponents} object from the URI associated with
|
||||
* the given HttpRequest while also overlaying with values from the headers
|
||||
* "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if present.
|
||||
* @param request the source request
|
||||
* @return the URI components of the URI
|
||||
* @since 4.1.5
|
||||
*/
|
||||
public static UriComponentsBuilder fromHttpRequest(HttpRequest request) {
|
||||
URI uri = request.getURI();
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.fromUri(uri);
|
||||
|
||||
String scheme = uri.getScheme();
|
||||
String host = uri.getHost();
|
||||
int port = uri.getPort();
|
||||
|
||||
String hostHeader = request.getHeaders().getFirst("X-Forwarded-Host");
|
||||
if (StringUtils.hasText(hostHeader)) {
|
||||
String[] hosts = StringUtils.commaDelimitedListToStringArray(hostHeader);
|
||||
String hostToUse = hosts[0];
|
||||
if (hostToUse.contains(":")) {
|
||||
String[] hostAndPort = StringUtils.split(hostToUse, ":");
|
||||
host = hostAndPort[0];
|
||||
port = Integer.parseInt(hostAndPort[1]);
|
||||
}
|
||||
else {
|
||||
host = hostToUse;
|
||||
port = -1;
|
||||
}
|
||||
}
|
||||
|
||||
String portHeader = request.getHeaders().getFirst("X-Forwarded-Port");
|
||||
if (StringUtils.hasText(portHeader)) {
|
||||
port = Integer.parseInt(portHeader);
|
||||
}
|
||||
|
||||
String protocolHeader = request.getHeaders().getFirst("X-Forwarded-Proto");
|
||||
if (StringUtils.hasText(protocolHeader)) {
|
||||
scheme = protocolHeader;
|
||||
}
|
||||
|
||||
builder.scheme(scheme);
|
||||
builder.host(host);
|
||||
if (scheme.equals("http") && port != 80 || scheme.equals("https") && port != 443) {
|
||||
builder.port(port);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
// build methods
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.web.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
@@ -32,6 +33,10 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
@@ -43,6 +48,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
public abstract class WebUtils {
|
||||
|
||||
@@ -109,6 +115,7 @@ public abstract class WebUtils {
|
||||
/**
|
||||
* Use of response encoding for HTML escaping parameter at the servlet context level
|
||||
* (i.e. a context-param in {@code web.xml}): "responseEncodedHtmlEscape".
|
||||
* @since 4.1.2
|
||||
*/
|
||||
public static final String RESPONSE_ENCODED_HTML_ESCAPE_CONTEXT_PARAM = "responseEncodedHtmlEscape";
|
||||
|
||||
@@ -127,6 +134,8 @@ public abstract class WebUtils {
|
||||
/** Key for the mutex session attribute */
|
||||
public static final String SESSION_MUTEX_ATTRIBUTE = WebUtils.class.getName() + ".MUTEX";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(WebUtils.class);
|
||||
|
||||
|
||||
/**
|
||||
* Set a system property to the web application root directory.
|
||||
@@ -207,7 +216,7 @@ public abstract class WebUtils {
|
||||
return null;
|
||||
}
|
||||
String param = servletContext.getInitParameter(HTML_ESCAPE_CONTEXT_PARAM);
|
||||
return (StringUtils.hasText(param)? Boolean.valueOf(param) : null);
|
||||
return (StringUtils.hasText(param) ? Boolean.valueOf(param) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,13 +230,14 @@ public abstract class WebUtils {
|
||||
* default in case of no setting at the global level.
|
||||
* @param servletContext the servlet context of the web application
|
||||
* @return whether response encoding is used for HTML escaping (null = no explicit default)
|
||||
* @since 4.1.2
|
||||
*/
|
||||
public static Boolean getResponseEncodedHtmlEscape(ServletContext servletContext) {
|
||||
if (servletContext == null) {
|
||||
return null;
|
||||
}
|
||||
String param = servletContext.getInitParameter(RESPONSE_ENCODED_HTML_ESCAPE_CONTEXT_PARAM);
|
||||
return (StringUtils.hasText(param)? Boolean.valueOf(param) : null);
|
||||
return (StringUtils.hasText(param) ? Boolean.valueOf(param) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -737,9 +747,9 @@ public abstract class WebUtils {
|
||||
* like this {@code "q1=a;q1=b;q2=a,b,c"}. The resulting map would contain
|
||||
* keys {@code "q1"} and {@code "q2"} with values {@code ["a","b"]} and
|
||||
* {@code ["a","b","c"]} respectively.
|
||||
*
|
||||
* @param matrixVariables the unparsed matrix variables string
|
||||
* @return a map with matrix variable names and values, never {@code null}
|
||||
* @since 3.2
|
||||
*/
|
||||
public static MultiValueMap<String, String> parseMatrixVariables(String matrixVariables) {
|
||||
MultiValueMap<String, String> result = new LinkedMultiValueMap<String, String>();
|
||||
@@ -763,4 +773,55 @@ public abstract class WebUtils {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the given request origin against a list of allowed origins.
|
||||
* A list containing "*" means that all origins are allowed.
|
||||
* An empty list means only same origin is allowed.
|
||||
* @return true if the request origin is valid, false otherwise
|
||||
* @since 4.1.5
|
||||
* @see <a href="https://tools.ietf.org/html/rfc6454">RFC 6454: The Web Origin Concept</a>
|
||||
*/
|
||||
public static boolean isValidOrigin(HttpRequest request, Collection<String> allowedOrigins) {
|
||||
Assert.notNull(request, "Request must not be null");
|
||||
Assert.notNull(allowedOrigins, "Allowed origins must not be null");
|
||||
|
||||
String origin = request.getHeaders().getOrigin();
|
||||
if (origin == null || allowedOrigins.contains("*")) {
|
||||
return true;
|
||||
}
|
||||
else if (allowedOrigins.isEmpty()) {
|
||||
UriComponents originComponents;
|
||||
try {
|
||||
originComponents = UriComponentsBuilder.fromHttpUrl(origin).build();
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Failed to parse Origin header value [" + origin + "]");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
UriComponents requestComponents = UriComponentsBuilder.fromHttpRequest(request).build();
|
||||
int originPort = getPort(originComponents);
|
||||
int requestPort = getPort(requestComponents);
|
||||
return (originComponents.getHost().equals(requestComponents.getHost()) && originPort == requestPort);
|
||||
}
|
||||
else {
|
||||
return allowedOrigins.contains(origin);
|
||||
}
|
||||
}
|
||||
|
||||
private static int getPort(UriComponents component) {
|
||||
int port = component.getPort();
|
||||
if (port == -1) {
|
||||
if ("http".equals(component.getScheme())) {
|
||||
port = 80;
|
||||
}
|
||||
else if ("https".equals(component.getScheme())) {
|
||||
port = 443;
|
||||
}
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,11 +42,13 @@ public class HttpHeadersTests {
|
||||
|
||||
private HttpHeaders headers;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
headers = new HttpHeaders();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void accept() {
|
||||
MediaType mediaType1 = new MediaType("text", "html");
|
||||
@@ -59,9 +61,7 @@ public class HttpHeadersTests {
|
||||
assertEquals("Invalid Accept header", "text/html, text/plain", headers.getFirst("Accept"));
|
||||
}
|
||||
|
||||
// SPR-9655
|
||||
|
||||
@Test
|
||||
@Test // SPR-9655
|
||||
public void acceptiPlanet() {
|
||||
headers.add("Accept", "text/html");
|
||||
headers.add("Accept", "text/plain");
|
||||
@@ -228,7 +228,7 @@ public class HttpHeadersTests {
|
||||
calendar.setTimeZone(TimeZone.getTimeZone("CET"));
|
||||
long date = calendar.getTimeInMillis();
|
||||
headers.setIfModifiedSince(date);
|
||||
assertEquals("Invalid If-Modified-Since header", date, headers.getIfNotModifiedSince());
|
||||
assertEquals("Invalid If-Modified-Since header", date, headers.getIfModifiedSince());
|
||||
assertEquals("Invalid If-Modified-Since header", "Thu, 18 Dec 2008 10:20:00 GMT",
|
||||
headers.getFirst("if-modified-since"));
|
||||
}
|
||||
@@ -260,12 +260,9 @@ public class HttpHeadersTests {
|
||||
headers.getFirst("Content-Disposition"));
|
||||
}
|
||||
|
||||
// SPR-11917
|
||||
|
||||
@Test
|
||||
@Test // SPR-11917
|
||||
public void getAllowEmptySet() {
|
||||
headers.setAllow(Collections.<HttpMethod> emptySet());
|
||||
|
||||
assertThat(headers.getAllow(), Matchers.emptyCollectionOf(HttpMethod.class));
|
||||
}
|
||||
|
||||
|
||||
+129
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,16 +16,22 @@
|
||||
|
||||
package org.springframework.http.converter.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.Version;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
@@ -35,6 +41,7 @@ import com.fasterxml.jackson.databind.Module;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
|
||||
import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
|
||||
import com.fasterxml.jackson.databind.deser.BasicDeserializerFactory;
|
||||
@@ -42,16 +49,20 @@ import com.fasterxml.jackson.databind.deser.Deserializers;
|
||||
import com.fasterxml.jackson.databind.deser.std.DateDeserializers;
|
||||
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.databind.module.SimpleSerializers;
|
||||
import com.fasterxml.jackson.databind.ser.BasicSerializerFactory;
|
||||
import com.fasterxml.jackson.databind.ser.Serializers;
|
||||
import com.fasterxml.jackson.databind.ser.std.ClassSerializer;
|
||||
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;
|
||||
import com.fasterxml.jackson.databind.type.SimpleType;
|
||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
@@ -167,15 +178,94 @@ public class Jackson2ObjectMapperBuilderTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setModules() {
|
||||
public void localeSetter() {
|
||||
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().locale(Locale.FRENCH).build();
|
||||
assertEquals(Locale.FRENCH, objectMapper.getSerializationConfig().getLocale());
|
||||
assertEquals(Locale.FRENCH, objectMapper.getDeserializationConfig().getLocale());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeZoneSetter() {
|
||||
TimeZone timeZone = TimeZone.getTimeZone("Europe/Paris");
|
||||
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().timeZone(timeZone).build();
|
||||
assertEquals(timeZone, objectMapper.getSerializationConfig().getTimeZone());
|
||||
assertEquals(timeZone, objectMapper.getDeserializationConfig().getTimeZone());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeZoneStringSetter() {
|
||||
String zoneId = "Europe/Paris";
|
||||
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().timeZone(zoneId).build();
|
||||
TimeZone timeZone = TimeZone.getTimeZone(zoneId);
|
||||
assertEquals(timeZone, objectMapper.getSerializationConfig().getTimeZone());
|
||||
assertEquals(timeZone, objectMapper.getDeserializationConfig().getTimeZone());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void wrongTimeZoneStringSetter() {
|
||||
String zoneId = "foo";
|
||||
Jackson2ObjectMapperBuilder.json().timeZone(zoneId).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void modules() {
|
||||
NumberSerializer serializer1 = new NumberSerializer();
|
||||
SimpleModule module = new SimpleModule();
|
||||
module.addSerializer(Integer.class, serializer1);
|
||||
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modules(Arrays.asList(new Module[]{module})).build();
|
||||
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modules(module).build();
|
||||
Serializers serializers = getSerializerFactoryConfig(objectMapper).serializers().iterator().next();
|
||||
assertTrue(serializers.findSerializer(null, SimpleType.construct(Integer.class), null) == serializer1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void modulesToInstallByClass() {
|
||||
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modulesToInstall(CustomIntegerModule.class).build();
|
||||
Serializers serializers = getSerializerFactoryConfig(objectMapper).serializers().iterator().next();
|
||||
assertTrue(serializers.findSerializer(null, SimpleType.construct(Integer.class), null).getClass() == CustomIntegerSerializer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void modulesToInstallByInstance() {
|
||||
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modulesToInstall(new CustomIntegerModule()).build();
|
||||
Serializers serializers = getSerializerFactoryConfig(objectMapper).serializers().iterator().next();
|
||||
assertTrue(serializers.findSerializer(null, SimpleType.construct(Integer.class), null).getClass() == CustomIntegerSerializer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultModules() throws JsonProcessingException, UnsupportedEncodingException {
|
||||
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
|
||||
Long timestamp = 1322903730000L;
|
||||
DateTime dateTime = new DateTime(timestamp, DateTimeZone.UTC);
|
||||
assertEquals(timestamp.toString(), new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
|
||||
}
|
||||
|
||||
@Test // SPR-12634
|
||||
public void customizeDefaultModulesWithModule() throws JsonProcessingException, UnsupportedEncodingException {
|
||||
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
|
||||
.modulesToInstall(new CustomIntegerModule()).build();
|
||||
DateTime dateTime = new DateTime(1322903730000L, DateTimeZone.UTC);
|
||||
assertEquals("1322903730000", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
|
||||
assertThat(new String(objectMapper.writeValueAsBytes(new Integer(4)), "UTF-8"), containsString("customid"));
|
||||
}
|
||||
|
||||
@Test // SPR-12634
|
||||
public void customizeDefaultModulesWithModuleClass() throws JsonProcessingException, UnsupportedEncodingException {
|
||||
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modulesToInstall(CustomIntegerModule.class).build();
|
||||
DateTime dateTime = new DateTime(1322903730000L, DateTimeZone.UTC);
|
||||
assertEquals("1322903730000", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
|
||||
assertThat(new String(objectMapper.writeValueAsBytes(new Integer(4)), "UTF-8"), containsString("customid"));
|
||||
}
|
||||
|
||||
@Test // SPR-12634
|
||||
public void customizeDefaultModulesWithSerializer() throws JsonProcessingException, UnsupportedEncodingException {
|
||||
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
|
||||
.serializerByType(Integer.class, new CustomIntegerSerializer()).build();
|
||||
DateTime dateTime = new DateTime(1322903730000L, DateTimeZone.UTC);
|
||||
assertEquals("1322903730000", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
|
||||
assertThat(new String(objectMapper.writeValueAsBytes(new Integer(4)), "UTF-8"), containsString("customid"));
|
||||
}
|
||||
|
||||
|
||||
private static SerializerFactoryConfig getSerializerFactoryConfig(ObjectMapper objectMapper) {
|
||||
return ((BasicSerializerFactory) objectMapper.getSerializerFactory()).getFactoryConfig();
|
||||
}
|
||||
@@ -196,6 +286,7 @@ public class Jackson2ObjectMapperBuilderTests {
|
||||
public void serializerByType() {
|
||||
JsonSerializer<Number> serializer = new NumberSerializer();
|
||||
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
|
||||
.modules(new ArrayList<>()) // Disable well-known modules detection
|
||||
.serializerByType(Boolean.class, serializer).build();
|
||||
assertTrue(getSerializerFactoryConfig(objectMapper).hasSerializers());
|
||||
Serializers serializers = getSerializerFactoryConfig(objectMapper).serializers().iterator().next();
|
||||
@@ -206,6 +297,7 @@ public class Jackson2ObjectMapperBuilderTests {
|
||||
public void deserializerByType() throws JsonMappingException {
|
||||
JsonDeserializer<Date> deserializer = new DateDeserializers.DateDeserializer();
|
||||
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
|
||||
.modules(new ArrayList<>()) // Disable well-known modules detection
|
||||
.deserializerByType(Date.class, deserializer).build();
|
||||
assertTrue(getDeserializerFactoryConfig(objectMapper).hasDeserializers());
|
||||
Deserializers deserializers = getDeserializerFactoryConfig(objectMapper).deserializers().iterator().next();
|
||||
@@ -249,6 +341,7 @@ public class Jackson2ObjectMapperBuilderTests {
|
||||
JsonSerializer<Number> serializer2 = new NumberSerializer();
|
||||
|
||||
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json()
|
||||
.modules(new ArrayList<>()) // Disable well-known modules detection
|
||||
.serializers(serializer1)
|
||||
.serializersByType(Collections.<Class<?>, JsonSerializer<?>>singletonMap(Boolean.class, serializer2))
|
||||
.deserializersByType(deserializerMap)
|
||||
@@ -311,4 +404,36 @@ public class Jackson2ObjectMapperBuilderTests {
|
||||
assertTrue(xmlObjectMapper.getClass().isAssignableFrom(XmlMapper.class));
|
||||
}
|
||||
|
||||
|
||||
public static class CustomIntegerModule extends Module {
|
||||
|
||||
@Override
|
||||
public String getModuleName() {
|
||||
return this.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version version() {
|
||||
return Version.unknownVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
SimpleSerializers serializers = new SimpleSerializers();
|
||||
serializers.addSerializer(Integer.class, new CustomIntegerSerializer());
|
||||
context.addSerializers(serializers);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class CustomIntegerSerializer extends JsonSerializer<Integer> {
|
||||
|
||||
@Override
|
||||
public void serialize(Integer value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
gen.writeStartObject();
|
||||
gen.writeNumberField("customid", value);
|
||||
gen.writeEndObject();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+127
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,16 +16,23 @@
|
||||
|
||||
package org.springframework.http.converter.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.Version;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
@@ -34,23 +41,28 @@ import com.fasterxml.jackson.databind.Module;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
|
||||
import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
|
||||
import com.fasterxml.jackson.databind.deser.BasicDeserializerFactory;
|
||||
import com.fasterxml.jackson.databind.deser.std.DateDeserializers.DateDeserializer;
|
||||
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.databind.module.SimpleSerializers;
|
||||
import com.fasterxml.jackson.databind.ser.BasicSerializerFactory;
|
||||
import com.fasterxml.jackson.databind.ser.Serializers;
|
||||
import com.fasterxml.jackson.databind.ser.std.ClassSerializer;
|
||||
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;
|
||||
import com.fasterxml.jackson.databind.type.SimpleType;
|
||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
@@ -58,6 +70,7 @@ import static org.junit.Assert.*;
|
||||
*
|
||||
* @author <a href="mailto:dmitry.katsubo@gmail.com">Dmitry Katsubo</a>
|
||||
* @author Brian Clozel
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
public class Jackson2ObjectMapperFactoryBeanTests {
|
||||
|
||||
@@ -161,13 +174,57 @@ public class Jackson2ObjectMapperFactoryBeanTests {
|
||||
assertEquals(dateFormat, this.factory.getObject().getDeserializationConfig().getDateFormat());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void localeSetter() {
|
||||
this.factory.setLocale(Locale.FRENCH);
|
||||
this.factory.afterPropertiesSet();
|
||||
|
||||
assertEquals(Locale.FRENCH, this.factory.getObject().getSerializationConfig().getLocale());
|
||||
assertEquals(Locale.FRENCH, this.factory.getObject().getDeserializationConfig().getLocale());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeZoneSetter() {
|
||||
TimeZone timeZone = TimeZone.getTimeZone("Europe/Paris");
|
||||
|
||||
this.factory.setTimeZone(timeZone);
|
||||
this.factory.afterPropertiesSet();
|
||||
|
||||
assertEquals(timeZone, this.factory.getObject().getSerializationConfig().getTimeZone());
|
||||
assertEquals(timeZone, this.factory.getObject().getDeserializationConfig().getTimeZone());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeZoneStringSetter() {
|
||||
String zoneId = "Europe/Paris";
|
||||
|
||||
this.factory.setTimeZone(TimeZone.getTimeZone(zoneId));
|
||||
this.factory.afterPropertiesSet();
|
||||
|
||||
TimeZone timeZone = TimeZone.getTimeZone(zoneId);
|
||||
assertEquals(timeZone, this.factory.getObject().getSerializationConfig().getTimeZone());
|
||||
assertEquals(timeZone, this.factory.getObject().getDeserializationConfig().getTimeZone());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongTimeZoneStringSetter() {
|
||||
String zoneId = "foo";
|
||||
|
||||
this.factory.setTimeZone(TimeZone.getTimeZone(zoneId));
|
||||
this.factory.afterPropertiesSet();
|
||||
|
||||
TimeZone timeZone = TimeZone.getTimeZone("GMT");
|
||||
assertEquals(timeZone, this.factory.getObject().getSerializationConfig().getTimeZone());
|
||||
assertEquals(timeZone, this.factory.getObject().getDeserializationConfig().getTimeZone());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setModules() {
|
||||
NumberSerializer serializer1 = new NumberSerializer();
|
||||
SimpleModule module = new SimpleModule();
|
||||
module.addSerializer(Integer.class, serializer1);
|
||||
|
||||
this.factory.setModules(Arrays.asList(new Module[] {module}));
|
||||
this.factory.setModules(Arrays.asList(new Module[]{module}));
|
||||
this.factory.afterPropertiesSet();
|
||||
ObjectMapper objectMapper = this.factory.getObject();
|
||||
|
||||
@@ -175,6 +232,41 @@ public class Jackson2ObjectMapperFactoryBeanTests {
|
||||
assertTrue(serializers.findSerializer(null, SimpleType.construct(Integer.class), null) == serializer1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultModules() throws JsonProcessingException, UnsupportedEncodingException {
|
||||
this.factory.afterPropertiesSet();
|
||||
ObjectMapper objectMapper = this.factory.getObject();
|
||||
|
||||
Long timestamp = 1322903730000L;
|
||||
DateTime dateTime = new DateTime(timestamp, DateTimeZone.UTC);
|
||||
assertEquals(timestamp.toString(), new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
|
||||
}
|
||||
|
||||
@Test // SPR-12634
|
||||
public void customizeDefaultModulesWithModuleClass() throws JsonProcessingException, UnsupportedEncodingException {
|
||||
this.factory.setModulesToInstall(CustomIntegerModule.class);
|
||||
this.factory.afterPropertiesSet();
|
||||
ObjectMapper objectMapper = this.factory.getObject();
|
||||
|
||||
DateTime dateTime = new DateTime(1322903730000L, DateTimeZone.UTC);
|
||||
assertEquals("1322903730000", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
|
||||
assertThat(new String(objectMapper.writeValueAsBytes(new Integer(4)), "UTF-8"), containsString("customid"));
|
||||
}
|
||||
|
||||
@Test // SPR-12634
|
||||
public void customizeDefaultModulesWithSerializer() throws JsonProcessingException, UnsupportedEncodingException {
|
||||
Map<Class<?>, JsonSerializer<?>> serializers = new HashMap<>();
|
||||
serializers.put(Integer.class, new CustomIntegerSerializer());
|
||||
|
||||
this.factory.setSerializersByType(serializers);
|
||||
this.factory.afterPropertiesSet();
|
||||
ObjectMapper objectMapper = this.factory.getObject();
|
||||
|
||||
DateTime dateTime = new DateTime(1322903730000L, DateTimeZone.UTC);
|
||||
assertEquals("1322903730000", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
|
||||
assertThat(new String(objectMapper.writeValueAsBytes(new Integer(4)), "UTF-8"), containsString("customid"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleSetup() {
|
||||
this.factory.afterPropertiesSet();
|
||||
@@ -237,6 +329,7 @@ public class Jackson2ObjectMapperFactoryBeanTests {
|
||||
JsonSerializer<Class<?>> serializer1 = new ClassSerializer();
|
||||
JsonSerializer<Number> serializer2 = new NumberSerializer();
|
||||
|
||||
factory.setModules(new ArrayList<>()); // Disable well-known modules detection
|
||||
factory.setSerializers(serializer1);
|
||||
factory.setSerializersByType(Collections.<Class<?>, JsonSerializer<?>> singletonMap(Boolean.class, serializer2));
|
||||
factory.setDeserializersByType(deserializers);
|
||||
@@ -304,4 +397,36 @@ public class Jackson2ObjectMapperFactoryBeanTests {
|
||||
assertEquals(XmlMapper.class, this.factory.getObjectType());
|
||||
}
|
||||
|
||||
|
||||
public static class CustomIntegerModule extends Module {
|
||||
|
||||
@Override
|
||||
public String getModuleName() {
|
||||
return this.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version version() {
|
||||
return Version.unknownVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
SimpleSerializers serializers = new SimpleSerializers();
|
||||
serializers.addSerializer(Integer.class, new CustomIntegerSerializer());
|
||||
context.addSerializers(serializers);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class CustomIntegerSerializer extends JsonSerializer<Integer> {
|
||||
|
||||
@Override
|
||||
public void serialize(Integer value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
gen.writeStartObject();
|
||||
gen.writeNumberField("customid", value);
|
||||
gen.writeEndObject();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+34
-18
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.web.client;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
@@ -26,6 +27,7 @@ import org.junit.Test;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
@@ -73,6 +75,17 @@ public class HttpMessageConverterExtractorTests {
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void informational() throws IOException {
|
||||
HttpMessageConverter<?> converter = mock(HttpMessageConverter.class);
|
||||
extractor = new HttpMessageConverterExtractor<String>(String.class, createConverterList(converter));
|
||||
given(response.getStatusCode()).willReturn(HttpStatus.CONTINUE);
|
||||
|
||||
Object result = extractor.extractData(response);
|
||||
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void zeroContentLength() throws IOException {
|
||||
HttpMessageConverter<?> converter = mock(HttpMessageConverter.class);
|
||||
@@ -87,6 +100,22 @@ public class HttpMessageConverterExtractorTests {
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void emptyMessageBody() throws IOException {
|
||||
HttpMessageConverter<String> converter = mock(HttpMessageConverter.class);
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
|
||||
converters.add(converter);
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
extractor = new HttpMessageConverterExtractor<String>(String.class, createConverterList(converter));
|
||||
given(response.getStatusCode()).willReturn(HttpStatus.OK);
|
||||
given(response.getHeaders()).willReturn(responseHeaders);
|
||||
given(response.getBody()).willReturn(new ByteArrayInputStream("".getBytes()));
|
||||
|
||||
Object result = extractor.extractData(response);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void normal() throws IOException {
|
||||
@@ -100,8 +129,9 @@ public class HttpMessageConverterExtractorTests {
|
||||
extractor = new HttpMessageConverterExtractor<String>(String.class, converters);
|
||||
given(response.getStatusCode()).willReturn(HttpStatus.OK);
|
||||
given(response.getHeaders()).willReturn(responseHeaders);
|
||||
given(response.getBody()).willReturn(new ByteArrayInputStream(expected.getBytes()));
|
||||
given(converter.canRead(String.class, contentType)).willReturn(true);
|
||||
given(converter.read(String.class, response)).willReturn(expected);
|
||||
given(converter.read(eq(String.class), any(HttpInputMessage.class))).willReturn(expected);
|
||||
|
||||
Object result = extractor.extractData(response);
|
||||
|
||||
@@ -120,28 +150,13 @@ public class HttpMessageConverterExtractorTests {
|
||||
extractor = new HttpMessageConverterExtractor<String>(String.class, converters);
|
||||
given(response.getStatusCode()).willReturn(HttpStatus.OK);
|
||||
given(response.getHeaders()).willReturn(responseHeaders);
|
||||
given(response.getBody()).willReturn(new ByteArrayInputStream("Foobar".getBytes()));
|
||||
given(converter.canRead(String.class, contentType)).willReturn(false);
|
||||
|
||||
extractor.extractData(response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void connectionClose() throws IOException {
|
||||
HttpMessageConverter<String> converter = mock(HttpMessageConverter.class);
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
|
||||
converters.add(converter);
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
responseHeaders.setConnection("close");
|
||||
extractor = new HttpMessageConverterExtractor<String>(String.class, createConverterList(converter));
|
||||
given(response.getStatusCode()).willReturn(HttpStatus.OK);
|
||||
given(response.getHeaders()).willReturn(responseHeaders);
|
||||
|
||||
Object result = extractor.extractData(response);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void generics() throws IOException {
|
||||
GenericHttpMessageConverter<String> converter = mock(GenericHttpMessageConverter.class);
|
||||
List<HttpMessageConverter<?>> converters = createConverterList(converter);
|
||||
@@ -154,8 +169,9 @@ public class HttpMessageConverterExtractorTests {
|
||||
extractor = new HttpMessageConverterExtractor<List<String>>(type, converters);
|
||||
given(response.getStatusCode()).willReturn(HttpStatus.OK);
|
||||
given(response.getHeaders()).willReturn(responseHeaders);
|
||||
given(response.getBody()).willReturn(new ByteArrayInputStream(expected.getBytes()));
|
||||
given(converter.canRead(type, null, contentType)).willReturn(true);
|
||||
given(converter.read(type, null, response)).willReturn(expected);
|
||||
given(converter.read(eq(type), eq(null), any(HttpInputMessage.class))).willReturn(expected);
|
||||
|
||||
Object result = extractor.extractData(response);
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.web.client;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
@@ -31,6 +32,7 @@ import org.junit.Test;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -170,14 +172,15 @@ public class RestTemplateTests {
|
||||
given(request.getHeaders()).willReturn(requestHeaders);
|
||||
given(request.execute()).willReturn(response);
|
||||
given(errorHandler.hasError(response)).willReturn(false);
|
||||
String expected = "Hello World";
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
responseHeaders.setContentType(textPlain);
|
||||
responseHeaders.setContentLength(10);
|
||||
given(response.getStatusCode()).willReturn(HttpStatus.OK);
|
||||
given(response.getHeaders()).willReturn(responseHeaders);
|
||||
given(response.getBody()).willReturn(new ByteArrayInputStream(expected.getBytes()));
|
||||
given(converter.canRead(String.class, textPlain)).willReturn(true);
|
||||
String expected = "Hello World";
|
||||
given(converter.read(String.class, response)).willReturn(expected);
|
||||
given(converter.read(eq(String.class), any(HttpInputMessage.class))).willReturn(expected);
|
||||
HttpStatus status = HttpStatus.OK;
|
||||
given(response.getStatusCode()).willReturn(status);
|
||||
given(response.getStatusText()).willReturn(status.getReasonPhrase());
|
||||
@@ -205,6 +208,7 @@ public class RestTemplateTests {
|
||||
responseHeaders.setContentLength(10);
|
||||
given(response.getStatusCode()).willReturn(HttpStatus.OK);
|
||||
given(response.getHeaders()).willReturn(responseHeaders);
|
||||
given(response.getBody()).willReturn(new ByteArrayInputStream("Foo".getBytes()));
|
||||
given(converter.canRead(String.class, contentType)).willReturn(false);
|
||||
HttpStatus status = HttpStatus.OK;
|
||||
given(response.getStatusCode()).willReturn(status);
|
||||
@@ -232,14 +236,15 @@ public class RestTemplateTests {
|
||||
given(request.getHeaders()).willReturn(requestHeaders);
|
||||
given(request.execute()).willReturn(response);
|
||||
given(errorHandler.hasError(response)).willReturn(false);
|
||||
String expected = "Hello World";
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
responseHeaders.setContentType(textPlain);
|
||||
responseHeaders.setContentLength(10);
|
||||
given(response.getStatusCode()).willReturn(HttpStatus.OK);
|
||||
given(response.getHeaders()).willReturn(responseHeaders);
|
||||
given(response.getBody()).willReturn(new ByteArrayInputStream(expected.getBytes()));
|
||||
given(converter.canRead(String.class, textPlain)).willReturn(true);
|
||||
String expected = "Hello World";
|
||||
given(converter.read(String.class, response)).willReturn(expected);
|
||||
given(converter.read(eq(String.class), any(HttpInputMessage.class))).willReturn(expected);
|
||||
given(response.getStatusCode()).willReturn(HttpStatus.OK);
|
||||
HttpStatus status = HttpStatus.OK;
|
||||
given(response.getStatusCode()).willReturn(status);
|
||||
@@ -405,14 +410,15 @@ public class RestTemplateTests {
|
||||
converter.write(request, null, this.request);
|
||||
given(this.request.execute()).willReturn(response);
|
||||
given(errorHandler.hasError(response)).willReturn(false);
|
||||
Integer expected = 42;
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
responseHeaders.setContentType(textPlain);
|
||||
responseHeaders.setContentLength(10);
|
||||
given(response.getStatusCode()).willReturn(HttpStatus.OK);
|
||||
given(response.getHeaders()).willReturn(responseHeaders);
|
||||
Integer expected = 42;
|
||||
given(response.getBody()).willReturn(new ByteArrayInputStream(expected.toString().getBytes()));
|
||||
given(converter.canRead(Integer.class, textPlain)).willReturn(true);
|
||||
given(converter.read(Integer.class, response)).willReturn(expected);
|
||||
given(converter.read(eq(Integer.class), any(HttpInputMessage.class))).willReturn(expected);
|
||||
HttpStatus status = HttpStatus.OK;
|
||||
given(response.getStatusCode()).willReturn(status);
|
||||
given(response.getStatusText()).willReturn(status.getReasonPhrase());
|
||||
@@ -437,14 +443,15 @@ public class RestTemplateTests {
|
||||
converter.write(request, null, this.request);
|
||||
given(this.request.execute()).willReturn(response);
|
||||
given(errorHandler.hasError(response)).willReturn(false);
|
||||
Integer expected = 42;
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
responseHeaders.setContentType(textPlain);
|
||||
responseHeaders.setContentLength(10);
|
||||
given(response.getStatusCode()).willReturn(HttpStatus.OK);
|
||||
given(response.getHeaders()).willReturn(responseHeaders);
|
||||
Integer expected = 42;
|
||||
given(response.getBody()).willReturn(new ByteArrayInputStream(expected.toString().getBytes()));
|
||||
given(converter.canRead(Integer.class, textPlain)).willReturn(true);
|
||||
given(converter.read(Integer.class, response)).willReturn(expected);
|
||||
given(converter.read(eq(Integer.class), any(HttpInputMessage.class))).willReturn(expected);
|
||||
given(response.getStatusCode()).willReturn(HttpStatus.OK);
|
||||
HttpStatus status = HttpStatus.OK;
|
||||
given(response.getStatusCode()).willReturn(status);
|
||||
@@ -615,14 +622,16 @@ public class RestTemplateTests {
|
||||
converter.write(body, null, this.request);
|
||||
given(this.request.execute()).willReturn(response);
|
||||
given(errorHandler.hasError(response)).willReturn(false);
|
||||
Integer expected = 42;
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
responseHeaders.setContentType(MediaType.TEXT_PLAIN);
|
||||
responseHeaders.setContentLength(10);
|
||||
given(response.getStatusCode()).willReturn(HttpStatus.OK);
|
||||
given(response.getHeaders()).willReturn(responseHeaders);
|
||||
Integer expected = 42;
|
||||
given(response.getBody()).willReturn(new ByteArrayInputStream(expected.toString().getBytes()));
|
||||
given(converter.canRead(Integer.class, MediaType.TEXT_PLAIN)).willReturn(true);
|
||||
given(converter.read(Integer.class, response)).willReturn(expected);
|
||||
given(converter.read(eq(Integer.class), any(HttpInputMessage.class))).willReturn(expected);
|
||||
given(response.getStatusCode()).willReturn(HttpStatus.OK);
|
||||
HttpStatus status = HttpStatus.OK;
|
||||
given(response.getStatusCode()).willReturn(status);
|
||||
@@ -658,14 +667,15 @@ public class RestTemplateTests {
|
||||
converter.write(requestBody, null, this.request);
|
||||
given(this.request.execute()).willReturn(response);
|
||||
given(errorHandler.hasError(response)).willReturn(false);
|
||||
List<Integer> expected = Collections.singletonList(42);
|
||||
HttpHeaders responseHeaders = new HttpHeaders();
|
||||
responseHeaders.setContentType(MediaType.TEXT_PLAIN);
|
||||
responseHeaders.setContentLength(10);
|
||||
given(response.getStatusCode()).willReturn(HttpStatus.OK);
|
||||
given(response.getHeaders()).willReturn(responseHeaders);
|
||||
List<Integer> expected = Collections.singletonList(42);
|
||||
given(response.getBody()).willReturn(new ByteArrayInputStream(new Integer(42).toString().getBytes()));
|
||||
given(converter.canRead(intList.getType(), null, MediaType.TEXT_PLAIN)).willReturn(true);
|
||||
given(converter.read(intList.getType(), null, response)).willReturn(expected);
|
||||
given(converter.read(eq(intList.getType()), eq(null), any(HttpInputMessage.class))).willReturn(expected);
|
||||
given(response.getStatusCode()).willReturn(HttpStatus.OK);
|
||||
HttpStatus status = HttpStatus.OK;
|
||||
given(response.getStatusCode()).willReturn(status);
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.mock.web.test.MockHttpServletRequest;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
/**
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class ContentCachingRequestWrapperTests {
|
||||
|
||||
protected static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";
|
||||
|
||||
protected static final String CHARSET = "UTF-8";
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
this.request = new MockHttpServletRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cachedContent() throws Exception {
|
||||
this.request.setMethod("GET");
|
||||
this.request.setCharacterEncoding(CHARSET);
|
||||
this.request.setContent("Hello World".getBytes(CHARSET));
|
||||
|
||||
ContentCachingRequestWrapper wrapper = new ContentCachingRequestWrapper(this.request);
|
||||
byte[] response = FileCopyUtils.copyToByteArray(wrapper.getInputStream());
|
||||
Assert.assertArrayEquals(response, wrapper.getContentAsByteArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestParams() throws Exception {
|
||||
this.request.setMethod("POST");
|
||||
this.request.setContentType(FORM_CONTENT_TYPE);
|
||||
this.request.setCharacterEncoding(CHARSET);
|
||||
this.request.setParameter("first", "value");
|
||||
this.request.setParameter("second", new String[] {"foo", "bar"});
|
||||
|
||||
ContentCachingRequestWrapper wrapper = new ContentCachingRequestWrapper(this.request);
|
||||
// getting request parameters will consume the request body
|
||||
Assert.assertFalse(wrapper.getParameterMap().isEmpty());
|
||||
Assert.assertEquals("first=value&second=foo&second=bar", new String(wrapper.getContentAsByteArray()));
|
||||
}
|
||||
|
||||
}
|
||||
+25
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -31,11 +31,16 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.mock.web.test.MockHttpServletRequest;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link org.springframework.web.util.UriComponentsBuilder}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Phillip Webb
|
||||
* @author Oliver Gierke
|
||||
@@ -232,6 +237,25 @@ public class UriComponentsBuilderTests {
|
||||
assertEquals("bar@baz", result.getQueryParams().getFirst("foo"));
|
||||
}
|
||||
|
||||
// Also see X-Forwarded-* related tests in ServletUriComponentsBuilderTests
|
||||
|
||||
@Test
|
||||
public void fromHttpRequest() throws URISyntaxException {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setScheme("http");
|
||||
request.setServerName("localhost");
|
||||
request.setServerPort(-1);
|
||||
request.setRequestURI("/path");
|
||||
request.setQueryString("a=1");
|
||||
|
||||
UriComponents result = UriComponentsBuilder.fromHttpRequest(new ServletServerHttpRequest(request)).build();
|
||||
assertEquals("http", result.getScheme());
|
||||
assertEquals("localhost", result.getHost());
|
||||
assertEquals(-1, result.getPort());
|
||||
assertEquals("/path", result.getPath());
|
||||
assertEquals("a=1", result.getQuery());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void path() throws URISyntaxException {
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.fromPath("/foo/bar");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-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.
|
||||
@@ -30,7 +30,6 @@ public class UriUtilsTests {
|
||||
private static final String ENC = "UTF-8";
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void encodeScheme() throws UnsupportedEncodingException {
|
||||
assertEquals("Invalid encoded result", "foobar+-.", UriUtils.encodeScheme("foobar+-.", ENC));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,12 +16,18 @@
|
||||
|
||||
package org.springframework.web.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.mock.web.test.MockHttpServletRequest;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
@@ -30,6 +36,7 @@ import static org.junit.Assert.*;
|
||||
* @author Juergen Hoeller
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
public class WebUtilsTests {
|
||||
|
||||
@@ -98,4 +105,61 @@ public class WebUtilsTests {
|
||||
assertEquals(Arrays.asList("red", "blue", "green"), variables.get("colors"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isValidOrigin() {
|
||||
List<String> allowedOrigins = new ArrayList<>();
|
||||
MockHttpServletRequest servletRequest = new MockHttpServletRequest();
|
||||
ServerHttpRequest request = new ServletServerHttpRequest(servletRequest);
|
||||
|
||||
servletRequest.setServerName("mydomain1.com");
|
||||
request.getHeaders().set(HttpHeaders.ORIGIN, "http://mydomain1.com");
|
||||
assertTrue(WebUtils.isValidOrigin(request, allowedOrigins));
|
||||
|
||||
servletRequest.setServerName("mydomain1.com");
|
||||
request.getHeaders().set(HttpHeaders.ORIGIN, "http://mydomain1.com:80");
|
||||
assertTrue(WebUtils.isValidOrigin(request, allowedOrigins));
|
||||
|
||||
servletRequest.setServerName("mydomain1.com");
|
||||
servletRequest.setServerPort(443);
|
||||
request.getHeaders().set(HttpHeaders.ORIGIN, "https://mydomain1.com");
|
||||
assertTrue(WebUtils.isValidOrigin(request, allowedOrigins));
|
||||
|
||||
servletRequest.setServerName("mydomain1.com");
|
||||
servletRequest.setServerPort(443);
|
||||
request.getHeaders().set(HttpHeaders.ORIGIN, "https://mydomain1.com:443");
|
||||
assertTrue(WebUtils.isValidOrigin(request, allowedOrigins));
|
||||
|
||||
servletRequest.setServerName("mydomain1.com");
|
||||
servletRequest.setServerPort(123);
|
||||
request.getHeaders().set(HttpHeaders.ORIGIN, "http://mydomain1.com:123");
|
||||
assertTrue(WebUtils.isValidOrigin(request, allowedOrigins));
|
||||
|
||||
servletRequest.setServerName("mydomain1.com");
|
||||
request.getHeaders().set(HttpHeaders.ORIGIN, "http://mydomain2.com");
|
||||
assertFalse(WebUtils.isValidOrigin(request, allowedOrigins));
|
||||
|
||||
servletRequest.setServerName("mydomain1.com");
|
||||
request.getHeaders().set(HttpHeaders.ORIGIN, "https://mydomain1.com");
|
||||
assertFalse(WebUtils.isValidOrigin(request, allowedOrigins));
|
||||
|
||||
servletRequest.setServerName("invalid-origin");
|
||||
request.getHeaders().set(HttpHeaders.ORIGIN, "invalid-origin");
|
||||
assertFalse(WebUtils.isValidOrigin(request, allowedOrigins));
|
||||
|
||||
allowedOrigins = Arrays.asList("*");
|
||||
servletRequest.setServerName("mydomain1.com");
|
||||
request.getHeaders().set(HttpHeaders.ORIGIN, "http://mydomain2.com");
|
||||
assertTrue(WebUtils.isValidOrigin(request, allowedOrigins));
|
||||
|
||||
allowedOrigins = Arrays.asList("http://mydomain1.com");
|
||||
servletRequest.setServerName("mydomain2.com");
|
||||
request.getHeaders().set(HttpHeaders.ORIGIN, "http://mydomain1.com");
|
||||
assertTrue(WebUtils.isValidOrigin(request, allowedOrigins));
|
||||
|
||||
allowedOrigins = Arrays.asList("http://mydomain1.com");
|
||||
servletRequest.setServerName("mydomain2.com");
|
||||
request.getHeaders().set(HttpHeaders.ORIGIN, "http://mydomain3.com");
|
||||
assertFalse(WebUtils.isValidOrigin(request, allowedOrigins));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
@@ -140,6 +140,8 @@ class ViewControllerBeanDefinitionParser implements BeanDefinitionParser {
|
||||
}
|
||||
if (element.hasAttribute("context-relative")) {
|
||||
redirectView.getPropertyValues().add("contextRelative", element.getAttribute("context-relative"));
|
||||
} else {
|
||||
redirectView.getPropertyValues().add("contextRelative", true);
|
||||
}
|
||||
if (element.hasAttribute("keep-query-params")) {
|
||||
redirectView.getPropertyValues().add("propagateQueryParams", element.getAttribute("keep-query-params"));
|
||||
|
||||
+47
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* 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.
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -30,6 +31,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.InvalidMediaTypeException;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -37,7 +39,10 @@ import org.springframework.http.converter.GenericHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
|
||||
@@ -79,6 +84,7 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
||||
return Collections.unmodifiableList(result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create the method argument value of the expected parameter type by
|
||||
* reading from the given request.
|
||||
@@ -102,7 +108,7 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
||||
* from the given HttpInputMessage.
|
||||
* @param <T> the expected type of the argument value to be created
|
||||
* @param inputMessage the HTTP input message representing the current request
|
||||
* @param methodParam the method argument
|
||||
* @param methodParam the method parameter descriptor
|
||||
* @param targetType the type of object to create, not necessarily the same as
|
||||
* the method parameter type (e.g. for {@code HttpEntity<String>} method
|
||||
* parameter the target type is String)
|
||||
@@ -126,6 +132,8 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
||||
}
|
||||
|
||||
Class<?> contextClass = methodParam.getContainingClass();
|
||||
Class<T> targetClass = (Class<T>)
|
||||
ResolvableType.forMethodParameter(methodParam, targetType).resolve(Object.class);
|
||||
|
||||
for (HttpMessageConverter<?> converter : this.messageConverters) {
|
||||
if (converter instanceof GenericHttpMessageConverter) {
|
||||
@@ -138,8 +146,6 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
||||
return genericConverter.read(targetType, contextClass, inputMessage);
|
||||
}
|
||||
}
|
||||
Class<T> targetClass = (Class<T>)
|
||||
ResolvableType.forMethodParameter(methodParam, targetType).resolve(Object.class);
|
||||
if (converter.canRead(targetClass, contentType)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Reading [" + targetClass.getName() + "] as \"" +
|
||||
@@ -162,4 +168,41 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
||||
return new ServletServerHttpRequest(servletRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the request part if applicable.
|
||||
* <p>The default implementation checks for {@code @javax.validation.Valid},
|
||||
* Spring's {@link org.springframework.validation.annotation.Validated},
|
||||
* and custom annotations whose name starts with "Valid".
|
||||
* @param binder the DataBinder to be used
|
||||
* @param methodParam the method parameter
|
||||
* @see #isBindExceptionRequired
|
||||
* @since 4.1.5
|
||||
*/
|
||||
protected void validateIfApplicable(WebDataBinder binder, MethodParameter methodParam) {
|
||||
Annotation[] annotations = methodParam.getParameterAnnotations();
|
||||
for (Annotation ann : annotations) {
|
||||
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
|
||||
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
|
||||
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
|
||||
Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
|
||||
binder.validate(validationHints);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to raise a fatal bind exception on validation errors.
|
||||
* @param binder the data binder used to perform data binding
|
||||
* @param methodParam the method argument
|
||||
* @return {@code true} if the next method argument is not of type {@link Errors}
|
||||
* @since 4.1.5
|
||||
*/
|
||||
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter methodParam) {
|
||||
int i = methodParam.getParameterIndex();
|
||||
Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes();
|
||||
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
|
||||
return !hasBindingResult;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+34
-61
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -25,13 +24,10 @@ import javax.servlet.http.Part;
|
||||
|
||||
import org.springframework.core.GenericCollectionTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@@ -80,12 +76,13 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
|
||||
super(messageConverters);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Supports the following:
|
||||
* <ul>
|
||||
* <li>Annotated with {@code @RequestPart}
|
||||
* <li>Of type {@link MultipartFile} unless annotated with {@code @RequestParam}.
|
||||
* <li>Of type {@code javax.servlet.http.Part} unless annotated with {@code @RequestParam}.
|
||||
* <li>annotated with {@code @RequestPart}
|
||||
* <li>of type {@link MultipartFile} unless annotated with {@code @RequestParam}
|
||||
* <li>of type {@code javax.servlet.http.Part} unless annotated with {@code @RequestParam}
|
||||
* </ul>
|
||||
*/
|
||||
@Override
|
||||
@@ -117,7 +114,7 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
|
||||
assertIsMultipartRequest(servletRequest);
|
||||
|
||||
MultipartHttpServletRequest multipartRequest =
|
||||
WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);
|
||||
WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);
|
||||
|
||||
String partName = getPartName(parameter);
|
||||
Object arg;
|
||||
@@ -153,7 +150,10 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
|
||||
arg = readWithMessageConverters(inputMessage, parameter, parameter.getParameterType());
|
||||
WebDataBinder binder = binderFactory.createBinder(request, arg, partName);
|
||||
if (arg != null) {
|
||||
validate(binder, parameter);
|
||||
validateIfApplicable(binder, parameter);
|
||||
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
|
||||
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
|
||||
}
|
||||
}
|
||||
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + partName, binder.getBindingResult());
|
||||
}
|
||||
@@ -163,8 +163,8 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
|
||||
}
|
||||
}
|
||||
|
||||
RequestPart annot = parameter.getParameterAnnotation(RequestPart.class);
|
||||
boolean isRequired = (annot == null || annot.required());
|
||||
RequestPart ann = parameter.getParameterAnnotation(RequestPart.class);
|
||||
boolean isRequired = (ann == null || ann.required());
|
||||
|
||||
if (arg == null && isRequired) {
|
||||
throw new MissingServletRequestPartException(partName);
|
||||
@@ -180,41 +180,44 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
|
||||
}
|
||||
}
|
||||
|
||||
private String getPartName(MethodParameter parameter) {
|
||||
RequestPart annot = parameter.getParameterAnnotation(RequestPart.class);
|
||||
String partName = (annot != null ? annot.value() : "");
|
||||
private String getPartName(MethodParameter methodParam) {
|
||||
RequestPart ann = methodParam.getParameterAnnotation(RequestPart.class);
|
||||
String partName = (ann != null ? ann.value() : "");
|
||||
if (partName.length() == 0) {
|
||||
partName = parameter.getParameterName();
|
||||
Assert.notNull(partName, "Request part name for argument type [" + parameter.getParameterType().getName() +
|
||||
"] not specified, and parameter name information not found in class file either.");
|
||||
partName = methodParam.getParameterName();
|
||||
if (partName == null) {
|
||||
throw new IllegalArgumentException("Request part name for argument type [" +
|
||||
methodParam.getNestedParameterType().getName() +
|
||||
"] not specified, and parameter name information not found in class file either.");
|
||||
}
|
||||
}
|
||||
return partName;
|
||||
}
|
||||
|
||||
private boolean isMultipartFileCollection(MethodParameter parameter) {
|
||||
Class<?> collectionType = getCollectionParameterType(parameter);
|
||||
return (collectionType != null && collectionType.equals(MultipartFile.class));
|
||||
private boolean isMultipartFileCollection(MethodParameter methodParam) {
|
||||
Class<?> collectionType = getCollectionParameterType(methodParam);
|
||||
return MultipartFile.class.equals(collectionType);
|
||||
}
|
||||
|
||||
private boolean isMultipartFileArray(MethodParameter parameter) {
|
||||
Class<?> paramType = parameter.getParameterType().getComponentType();
|
||||
return (paramType != null && MultipartFile.class.equals(paramType));
|
||||
private boolean isMultipartFileArray(MethodParameter methodParam) {
|
||||
Class<?> paramType = methodParam.getNestedParameterType().getComponentType();
|
||||
return MultipartFile.class.equals(paramType);
|
||||
}
|
||||
|
||||
private boolean isPartCollection(MethodParameter parameter) {
|
||||
Class<?> collectionType = getCollectionParameterType(parameter);
|
||||
private boolean isPartCollection(MethodParameter methodParam) {
|
||||
Class<?> collectionType = getCollectionParameterType(methodParam);
|
||||
return (collectionType != null && "javax.servlet.http.Part".equals(collectionType.getName()));
|
||||
}
|
||||
|
||||
private boolean isPartArray(MethodParameter parameter) {
|
||||
Class<?> paramType = parameter.getParameterType().getComponentType();
|
||||
private boolean isPartArray(MethodParameter methodParam) {
|
||||
Class<?> paramType = methodParam.getNestedParameterType().getComponentType();
|
||||
return (paramType != null && "javax.servlet.http.Part".equals(paramType.getName()));
|
||||
}
|
||||
|
||||
private Class<?> getCollectionParameterType(MethodParameter parameter) {
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
private Class<?> getCollectionParameterType(MethodParameter methodParam) {
|
||||
Class<?> paramType = methodParam.getNestedParameterType();
|
||||
if (Collection.class.equals(paramType) || List.class.isAssignableFrom(paramType)){
|
||||
Class<?> valueType = GenericCollectionTypeResolver.getCollectionParameterType(parameter);
|
||||
Class<?> valueType = GenericCollectionTypeResolver.getCollectionParameterType(methodParam);
|
||||
if (valueType != null) {
|
||||
return valueType;
|
||||
}
|
||||
@@ -222,36 +225,6 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
|
||||
return null;
|
||||
}
|
||||
|
||||
private void validate(WebDataBinder binder, MethodParameter parameter) throws MethodArgumentNotValidException {
|
||||
Annotation[] annotations = parameter.getParameterAnnotations();
|
||||
for (Annotation ann : annotations) {
|
||||
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
|
||||
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
|
||||
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
|
||||
Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
|
||||
binder.validate(validationHints);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
if (bindingResult.hasErrors()) {
|
||||
if (isBindingErrorFatal(parameter)) {
|
||||
throw new MethodArgumentNotValidException(parameter, bindingResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to raise a {@link MethodArgumentNotValidException} on validation errors.
|
||||
* @param parameter the method argument
|
||||
* @return {@code true} if the next method argument is not of type {@link Errors}
|
||||
*/
|
||||
private boolean isBindingErrorFatal(MethodParameter parameter) {
|
||||
int i = parameter.getParameterIndex();
|
||||
Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
|
||||
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
|
||||
return !hasBindingResult;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to avoid hard-coded dependency on Servlet 3.0 Part type...
|
||||
|
||||
+19
-54
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -19,7 +19,6 @@ package org.springframework.web.servlet.mvc.method.annotation;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PushbackInputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -32,8 +31,6 @@ import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.accept.ContentNegotiationManager;
|
||||
@@ -47,16 +44,14 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
||||
|
||||
/**
|
||||
* Resolves method arguments annotated with {@code @RequestBody} and handles
|
||||
* return values from methods annotated with {@code @ResponseBody} by reading
|
||||
* and writing to the body of the request or response with an
|
||||
* {@link HttpMessageConverter}.
|
||||
* Resolves method arguments annotated with {@code @RequestBody} and handles return
|
||||
* values from methods annotated with {@code @ResponseBody} by reading and writing
|
||||
* to the body of the request or response with an {@link HttpMessageConverter}.
|
||||
*
|
||||
* <p>An {@code @RequestBody} method argument is also validated if it is
|
||||
* annotated with {@code @javax.validation.Valid}. In case of validation
|
||||
* failure, {@link MethodArgumentNotValidException} is raised and results
|
||||
* in a 400 response status code if {@link DefaultHandlerExceptionResolver}
|
||||
* is configured.
|
||||
* <p>An {@code @RequestBody} method argument is also validated if it is annotated
|
||||
* with {@code @javax.validation.Valid}. In case of validation failure,
|
||||
* {@link MethodArgumentNotValidException} is raised and results in a 400 response
|
||||
* status code if {@link DefaultHandlerExceptionResolver} is configured.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
@@ -93,7 +88,7 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws MethodArgumentNotValidException if validation fails
|
||||
* Throws MethodArgumentNotValidException if validation fails.
|
||||
* @throws HttpMessageNotReadableException if {@link RequestBody#required()}
|
||||
* is {@code true} and there is no body content or if there is no suitable
|
||||
* converter to read the content with.
|
||||
@@ -102,52 +97,22 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
|
||||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||
|
||||
Object argument = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType());
|
||||
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType());
|
||||
String name = Conventions.getVariableNameForParameter(parameter);
|
||||
WebDataBinder binder = binderFactory.createBinder(webRequest, argument, name);
|
||||
if (argument != null) {
|
||||
validate(binder, parameter);
|
||||
}
|
||||
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
|
||||
return argument;
|
||||
}
|
||||
|
||||
private void validate(WebDataBinder binder, MethodParameter parameter) throws Exception {
|
||||
Annotation[] annotations = parameter.getParameterAnnotations();
|
||||
for (Annotation ann : annotations) {
|
||||
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
|
||||
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
|
||||
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
|
||||
Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
|
||||
binder.validate(validationHints);
|
||||
BindingResult bindingResult = binder.getBindingResult();
|
||||
if (bindingResult.hasErrors()) {
|
||||
if (isBindExceptionRequired(binder, parameter)) {
|
||||
throw new MethodArgumentNotValidException(parameter, bindingResult);
|
||||
}
|
||||
}
|
||||
break;
|
||||
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
|
||||
if (arg != null) {
|
||||
validateIfApplicable(binder, parameter);
|
||||
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
|
||||
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to raise a {@link MethodArgumentNotValidException} on validation errors.
|
||||
* @param binder the data binder used to perform data binding
|
||||
* @param parameter the method argument
|
||||
* @return {@code true} if the next method argument is not of type {@link Errors}.
|
||||
*/
|
||||
private boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {
|
||||
int i = parameter.getParameterIndex();
|
||||
Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
|
||||
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
|
||||
|
||||
return !hasBindingResult;
|
||||
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
|
||||
return arg;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest,
|
||||
MethodParameter methodParam, Type paramType) throws IOException, HttpMediaTypeNotSupportedException {
|
||||
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter methodParam,
|
||||
Type paramType) throws IOException, HttpMediaTypeNotSupportedException {
|
||||
|
||||
final HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
HttpInputMessage inputMessage = new ServletServerHttpRequest(servletRequest);
|
||||
|
||||
+1
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -18,9 +18,6 @@ package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
@@ -37,8 +34,6 @@ import org.springframework.web.method.ControllerAdviceBean;
|
||||
*/
|
||||
class ResponseBodyAdviceChain {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ResponseBodyAdviceChain.class);
|
||||
|
||||
private final List<Object> advice;
|
||||
|
||||
|
||||
@@ -57,9 +52,6 @@ class ResponseBodyAdviceChain {
|
||||
ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
if (this.advice != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Invoking ResponseBodyAdvice chain for body=" + body);
|
||||
}
|
||||
for (Object advice : this.advice) {
|
||||
if (advice instanceof ControllerAdviceBean) {
|
||||
ControllerAdviceBean adviceBean = (ControllerAdviceBean) advice;
|
||||
@@ -79,9 +71,6 @@ class ResponseBodyAdviceChain {
|
||||
throw new IllegalStateException("Expected ResponseBodyAdvice: " + advice);
|
||||
}
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("After ResponseBodyAdvice chain body=" + body);
|
||||
}
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
+13
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -50,7 +50,7 @@ public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodPr
|
||||
/**
|
||||
* @param annotationNotRequired if "true", non-simple method arguments and
|
||||
* return values are considered model attributes with or without a
|
||||
* {@code @ModelAttribute} annotation.
|
||||
* {@code @ModelAttribute} annotation
|
||||
*/
|
||||
public ServletModelAttributeMethodProcessor(boolean annotationNotRequired) {
|
||||
super(annotationNotRequired);
|
||||
@@ -62,21 +62,22 @@ public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodPr
|
||||
* request parameter if the name matches to the model attribute name and
|
||||
* if there is an appropriate type conversion strategy. If none of these
|
||||
* are true delegate back to the base class.
|
||||
* @see #createAttributeFromRequestValue(String, String, MethodParameter, WebDataBinderFactory, NativeWebRequest)
|
||||
* @see #createAttributeFromRequestValue
|
||||
*/
|
||||
@Override
|
||||
protected final Object createAttribute(String attributeName, MethodParameter parameter,
|
||||
protected final Object createAttribute(String attributeName, MethodParameter methodParam,
|
||||
WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
|
||||
|
||||
String value = getRequestValueForAttribute(attributeName, request);
|
||||
if (value != null) {
|
||||
Object attribute = createAttributeFromRequestValue(value, attributeName, parameter, binderFactory, request);
|
||||
Object attribute = createAttributeFromRequestValue(
|
||||
value, attributeName, methodParam, binderFactory, request);
|
||||
if (attribute != null) {
|
||||
return attribute;
|
||||
}
|
||||
}
|
||||
|
||||
return super.createAttribute(attributeName, parameter, binderFactory, request);
|
||||
return super.createAttribute(attributeName, methodParam, binderFactory, request);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,9 +104,8 @@ public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodPr
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected final Map<String, String> getUriTemplateVariables(NativeWebRequest request) {
|
||||
Map<String, String> variables =
|
||||
(Map<String, String>) request.getAttribute(
|
||||
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
|
||||
Map<String, String> variables = (Map<String, String>) request.getAttribute(
|
||||
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
|
||||
return (variables != null ? variables : Collections.<String, String>emptyMap());
|
||||
}
|
||||
|
||||
@@ -116,23 +116,23 @@ public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodPr
|
||||
* {@link Converter} that can perform the conversion.
|
||||
* @param sourceValue the source value to create the model attribute from
|
||||
* @param attributeName the name of the attribute, never {@code null}
|
||||
* @param parameter the method parameter
|
||||
* @param methodParam the method parameter
|
||||
* @param binderFactory for creating WebDataBinder instance
|
||||
* @param request the current request
|
||||
* @return the created model attribute, or {@code null}
|
||||
* @throws Exception
|
||||
*/
|
||||
protected Object createAttributeFromRequestValue(String sourceValue, String attributeName,
|
||||
MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request)
|
||||
MethodParameter methodParam, WebDataBinderFactory binderFactory, NativeWebRequest request)
|
||||
throws Exception {
|
||||
|
||||
DataBinder binder = binderFactory.createBinder(request, null, attributeName);
|
||||
ConversionService conversionService = binder.getConversionService();
|
||||
if (conversionService != null) {
|
||||
TypeDescriptor source = TypeDescriptor.valueOf(String.class);
|
||||
TypeDescriptor target = new TypeDescriptor(parameter);
|
||||
TypeDescriptor target = new TypeDescriptor(methodParam);
|
||||
if (conversionService.canConvert(source, target)) {
|
||||
return binder.convertIfNecessary(sourceValue, parameter.getParameterType(), parameter);
|
||||
return binder.convertIfNecessary(sourceValue, methodParam.getParameterType(), methodParam);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
+2
-1
@@ -123,7 +123,8 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
|
||||
|
||||
private boolean hasScheme(String link) {
|
||||
int schemeIndex = link.indexOf(":");
|
||||
return schemeIndex > 0 && !link.substring(0, schemeIndex).contains("/");
|
||||
return (schemeIndex > 0 && !link.substring(0, schemeIndex).contains("/"))
|
||||
|| link.indexOf("//") == 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+6
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -25,6 +25,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.support.ServletContextResource;
|
||||
|
||||
/**
|
||||
@@ -164,19 +165,19 @@ public class PathResourceResolver extends AbstractResourceResolver {
|
||||
String locationPath;
|
||||
if (resource instanceof UrlResource) {
|
||||
resourcePath = resource.getURL().toExternalForm();
|
||||
locationPath = location.getURL().toExternalForm();
|
||||
locationPath = StringUtils.cleanPath(location.getURL().toString());
|
||||
}
|
||||
else if (resource instanceof ClassPathResource) {
|
||||
resourcePath = ((ClassPathResource) resource).getPath();
|
||||
locationPath = ((ClassPathResource) location).getPath();
|
||||
locationPath = StringUtils.cleanPath(((ClassPathResource) location).getPath());
|
||||
}
|
||||
else if (resource instanceof ServletContextResource) {
|
||||
resourcePath = ((ServletContextResource) resource).getPath();
|
||||
locationPath = ((ServletContextResource) location).getPath();
|
||||
locationPath = StringUtils.cleanPath(((ServletContextResource) location).getPath());
|
||||
}
|
||||
else {
|
||||
resourcePath = resource.getURL().getPath();
|
||||
locationPath = location.getURL().getPath();
|
||||
locationPath = StringUtils.cleanPath(location.getURL().getPath());
|
||||
}
|
||||
locationPath = (locationPath.endsWith("/") || locationPath.isEmpty() ? locationPath : locationPath + "/");
|
||||
if (!resourcePath.startsWith(locationPath)) {
|
||||
|
||||
+19
-18
@@ -21,7 +21,6 @@ import java.io.InputStream;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.activation.MimetypesFileTypeMap;
|
||||
import javax.servlet.ServletException;
|
||||
@@ -30,6 +29,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -51,29 +51,30 @@ import org.springframework.web.servlet.support.WebContentGenerator;
|
||||
* (according to the guidelines of Page Speed, YSlow, etc.) by allowing for flexible cache settings
|
||||
* ({@linkplain #setCacheSeconds "cacheSeconds" property}, last-modified support).
|
||||
*
|
||||
* <p>The {@linkplain #setLocations "locations" property} takes a list of Spring {@link Resource} locations
|
||||
* from which static resources are allowed to be served by this handler. For a given request, the
|
||||
* list of locations will be consulted in order for the presence of the requested resource, and the
|
||||
* <p>The {@linkplain #setLocations "locations" property} takes a list of Spring {@link Resource}
|
||||
* locations from which static resources are allowed to be served by this handler. For a given request,
|
||||
* the list of locations will be consulted in order for the presence of the requested resource, and the
|
||||
* first found match will be written to the response, with {@code Expires} and {@code Cache-Control}
|
||||
* headers set as configured. The handler also properly evaluates the {@code Last-Modified} header
|
||||
* (if present) so that a {@code 304} status code will be returned as appropriate, avoiding unnecessary
|
||||
* overhead for resources that are already cached by the client. The use of {@code Resource} locations
|
||||
* allows resource requests to easily be mapped to locations other than the web application root. For
|
||||
* example, resources could be served from a classpath location such as "classpath:/META-INF/public-web-resources/",
|
||||
* allowing convenient packaging and serving of resources such as a JavaScript library from within jar files.
|
||||
* allows resource requests to easily be mapped to locations other than the web application root.
|
||||
* For example, resources could be served from a classpath location such as
|
||||
* "classpath:/META-INF/public-web-resources/", allowing convenient packaging and serving of resources
|
||||
* such as a JavaScript library from within jar files.
|
||||
*
|
||||
* <p>To ensure that users with a primed browser cache get the latest changes to application-specific
|
||||
* resources upon deployment of new versions of the application, it is recommended that a version string
|
||||
* is used in the URL mapping pattern that selects this handler. Such patterns can be easily parameterized
|
||||
* using Spring EL. See the reference manual for further examples of this approach.
|
||||
* resources upon deployment of new versions of the application, it is recommended that a version
|
||||
* string is used in the URL mapping pattern that selects this handler. Such patterns can be easily
|
||||
* parameterized using Spring EL. See the reference manual for further examples of this approach.
|
||||
*
|
||||
* <p>For various front-end needs — such as ensuring that users with a primed browser cache get the
|
||||
* latest changes, or serving variations of resources (e.g., minified versions) —
|
||||
* <p>For various front-end needs — such as ensuring that users with a primed browser cache
|
||||
* get the latest changes, or serving variations of resources (e.g., minified versions) —
|
||||
* {@link org.springframework.web.servlet.resource.ResourceResolver}s can be configured.
|
||||
*
|
||||
* <p>This handler can be configured through use of a
|
||||
* {@link org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry} or the {@code <mvc:resources/>}
|
||||
* XML configuration element.
|
||||
* {@link org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry}
|
||||
* or the {@code <mvc:resources/>} XML configuration element.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Jeremy Grelle
|
||||
@@ -118,9 +119,8 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
|
||||
|
||||
/**
|
||||
* Configure the list of {@link ResourceResolver}s to use.
|
||||
*
|
||||
* <p>By default {@link PathResourceResolver} is configured. If using this property, it
|
||||
* is recommended to add {@link PathResourceResolver} as the last resolver.
|
||||
* <p>By default {@link PathResourceResolver} is configured. If using this property,
|
||||
* it is recommended to add {@link PathResourceResolver} as the last resolver.
|
||||
*/
|
||||
public void setResourceResolvers(List<ResourceResolver> resourceResolvers) {
|
||||
this.resourceResolvers.clear();
|
||||
@@ -272,7 +272,8 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
|
||||
if (resource == null || getResourceTransformers().isEmpty()) {
|
||||
return resource;
|
||||
}
|
||||
ResourceTransformerChain transformChain = new DefaultResourceTransformerChain(resolveChain, getResourceTransformers());
|
||||
ResourceTransformerChain transformChain =
|
||||
new DefaultResourceTransformerChain(resolveChain, getResourceTransformers());
|
||||
resource = transformChain.transform(request, resource);
|
||||
return resource;
|
||||
}
|
||||
|
||||
+30
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.web.servlet.resource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -129,6 +130,9 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
|
||||
if (this.handlerMap.isEmpty() && logger.isDebugEnabled()) {
|
||||
logger.debug("No resource handling mappings found");
|
||||
}
|
||||
if(!this.handlerMap.isEmpty()) {
|
||||
this.autodetect = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +192,8 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
|
||||
* public use.
|
||||
* <p>It is expected that the given path is what Spring MVC would use for
|
||||
* request mapping purposes, i.e. excluding context and servlet path portions.
|
||||
* <p>If several handler mappings match, the handler used will be the one
|
||||
* configured with the most specific pattern.
|
||||
* @param lookupPath the lookup path to check
|
||||
* @return the resolved public URL path, or {@code null} if unresolved
|
||||
*/
|
||||
@@ -195,25 +201,32 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Getting resource URL for lookupPath=" + lookupPath);
|
||||
}
|
||||
List<String> matchingPatterns = new ArrayList<String>();
|
||||
for (String pattern : this.handlerMap.keySet()) {
|
||||
if (!getPathMatcher().match(pattern, lookupPath)) {
|
||||
continue;
|
||||
if (getPathMatcher().match(pattern, lookupPath)) {
|
||||
matchingPatterns.add(pattern);
|
||||
}
|
||||
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(pattern, lookupPath);
|
||||
String pathMapping = lookupPath.substring(0, lookupPath.indexOf(pathWithinMapping));
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Invoking ResourceResolverChain for URL pattern=\"" + pattern + "\"");
|
||||
}
|
||||
if (!matchingPatterns.isEmpty()) {
|
||||
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(lookupPath);
|
||||
Collections.sort(matchingPatterns, patternComparator);
|
||||
for(String pattern : matchingPatterns) {
|
||||
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(pattern, lookupPath);
|
||||
String pathMapping = lookupPath.substring(0, lookupPath.indexOf(pathWithinMapping));
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Invoking ResourceResolverChain for URL pattern=\"" + pattern + "\"");
|
||||
}
|
||||
ResourceHttpRequestHandler handler = this.handlerMap.get(pattern);
|
||||
ResourceResolverChain chain = new DefaultResourceResolverChain(handler.getResourceResolvers());
|
||||
String resolved = chain.resolveUrlPath(pathWithinMapping, handler.getLocations());
|
||||
if (resolved == null) {
|
||||
continue;
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Resolved public resource URL path=\"" + resolved + "\"");
|
||||
}
|
||||
return pathMapping + resolved;
|
||||
}
|
||||
ResourceHttpRequestHandler handler = this.handlerMap.get(pattern);
|
||||
ResourceResolverChain chain = new DefaultResourceResolverChain(handler.getResourceResolvers());
|
||||
String resolved = chain.resolveUrlPath(pathWithinMapping, handler.getLocations());
|
||||
if (resolved == null) {
|
||||
throw new IllegalStateException("Failed to get public resource URL path for " + pathWithinMapping);
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Resolved public resource URL path=\"" + resolved + "\"");
|
||||
}
|
||||
return pathMapping + resolved;
|
||||
}
|
||||
logger.debug("No matching resource mapping");
|
||||
return null;
|
||||
|
||||
+2
@@ -265,6 +265,8 @@ public class RequestContext {
|
||||
// context-param in web.xml, if any.
|
||||
this.defaultHtmlEscape = WebUtils.getDefaultHtmlEscape(this.webApplicationContext.getServletContext());
|
||||
|
||||
// Determine response-encoded HTML escape setting from the "responseEncodedHtmlEscape"
|
||||
// context-param in web.xml, if any.
|
||||
this.responseEncodedHtmlEscape = WebUtils.getResponseEncodedHtmlEscape(this.webApplicationContext.getServletContext());
|
||||
|
||||
this.urlPathHelper = new UrlPathHelper();
|
||||
|
||||
+20
-39
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -18,11 +18,14 @@ package org.springframework.web.servlet.support;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
@@ -60,6 +63,9 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder {
|
||||
this.originalPath = other.originalPath;
|
||||
}
|
||||
|
||||
|
||||
// Factory methods based on a HttpServletRequest
|
||||
|
||||
/**
|
||||
* Prepare a builder from the host, port, scheme, and context path of the
|
||||
* given HttpServletRequest.
|
||||
@@ -73,7 +79,6 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder {
|
||||
/**
|
||||
* Prepare a builder from the host, port, scheme, context path, and
|
||||
* servlet mapping of the given HttpServletRequest.
|
||||
*
|
||||
* <p>If the servlet is mapped by name, e.g. {@code "/main/*"}, the path
|
||||
* will end with "/main". If the servlet is mapped otherwise, e.g.
|
||||
* {@code "/"} or {@code "*.do"}, the result will be the same as
|
||||
@@ -112,49 +117,21 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder {
|
||||
* Initialize a builder with a scheme, host,and port (but not path and query).
|
||||
*/
|
||||
private static ServletUriComponentsBuilder initFromRequest(HttpServletRequest request) {
|
||||
String scheme = request.getScheme();
|
||||
String host = request.getServerName();
|
||||
int port = request.getServerPort();
|
||||
|
||||
String hostHeader = request.getHeader("X-Forwarded-Host");
|
||||
if (StringUtils.hasText(hostHeader)) {
|
||||
String[] hosts = StringUtils.commaDelimitedListToStringArray(hostHeader);
|
||||
String hostToUse = hosts[0];
|
||||
if (hostToUse.contains(":")) {
|
||||
String[] hostAndPort = StringUtils.split(hostToUse, ":");
|
||||
host = hostAndPort[0];
|
||||
port = Integer.parseInt(hostAndPort[1]);
|
||||
}
|
||||
else {
|
||||
host = hostToUse;
|
||||
port = -1;
|
||||
}
|
||||
}
|
||||
|
||||
String portHeader = request.getHeader("X-Forwarded-Port");
|
||||
if (StringUtils.hasText(portHeader)) {
|
||||
port = Integer.parseInt(portHeader);
|
||||
}
|
||||
|
||||
String protocolHeader = request.getHeader("X-Forwarded-Proto");
|
||||
if (StringUtils.hasText(protocolHeader)) {
|
||||
scheme = protocolHeader;
|
||||
}
|
||||
HttpRequest httpRequest = new ServletServerHttpRequest(request);
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromHttpRequest(httpRequest).build();
|
||||
String scheme = uriComponents.getScheme();
|
||||
String host = uriComponents.getHost();
|
||||
int port = uriComponents.getPort();
|
||||
|
||||
ServletUriComponentsBuilder builder = new ServletUriComponentsBuilder();
|
||||
builder.scheme(scheme);
|
||||
builder.host(host);
|
||||
if (scheme.equals("http") && port != 80 || scheme.equals("https") && port != 443) {
|
||||
if (("http".equals(scheme) && port != 80) || ("https".equals(scheme) && port != 443)) {
|
||||
builder.port(port);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
private void initPath(String path) {
|
||||
this.originalPath = path;
|
||||
replacePath(path);
|
||||
}
|
||||
|
||||
private static String prependForwardedPrefix(HttpServletRequest request, String path) {
|
||||
String prefix = request.getHeader("X-Forwarded-Prefix");
|
||||
if (StringUtils.hasText(prefix)) {
|
||||
@@ -198,9 +175,8 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder {
|
||||
return fromRequest(getCurrentRequest());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the request through {@link RequestContextHolder}.
|
||||
* Obtain current request through {@link RequestContextHolder}.
|
||||
*/
|
||||
protected static HttpServletRequest getCurrentRequest() {
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
@@ -211,12 +187,17 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder {
|
||||
return servletRequest;
|
||||
}
|
||||
|
||||
|
||||
private void initPath(String path) {
|
||||
this.originalPath = path;
|
||||
replacePath(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any path extension from the {@link HttpServletRequest#getRequestURI()
|
||||
* requestURI}. This method must be invoked before any calls to {@link #path(String)}
|
||||
* or {@link #pathSegment(String...)}.
|
||||
* <pre>
|
||||
*
|
||||
* GET http://foo.com/rest/books/6.json
|
||||
*
|
||||
* ServletUriComponentsBuilder builder = ServletUriComponentsBuilder.fromRequestUri(this.request);
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* 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.
|
||||
@@ -186,6 +186,7 @@ public class FreeMarkerView extends AbstractTemplateView {
|
||||
* {@link ObjectWrapper#DEFAULT_WRAPPER default wrapper} if none specified.
|
||||
* @see freemarker.template.Configuration#getObjectWrapper()
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
protected ObjectWrapper getObjectWrapper() {
|
||||
ObjectWrapper ow = getConfiguration().getObjectWrapper();
|
||||
return (ow != null ? ow : ObjectWrapper.DEFAULT_WRAPPER);
|
||||
|
||||
+12
-3
@@ -95,6 +95,7 @@ import org.springframework.web.servlet.handler.UserRoleAuthorizationInterceptor;
|
||||
import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter;
|
||||
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
||||
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
@@ -648,9 +649,17 @@ public class MvcNamespaceTests {
|
||||
|
||||
SimpleUrlHandlerMapping hm = this.appContext.getBean(SimpleUrlHandlerMapping.class);
|
||||
assertNotNull(hm);
|
||||
assertNotNull(hm.getUrlMap().get("/path"));
|
||||
assertNotNull(hm.getUrlMap().get("/old"));
|
||||
assertNotNull(hm.getUrlMap().get("/bad"));
|
||||
ParameterizableViewController viewController = (ParameterizableViewController) hm.getUrlMap().get("/path");
|
||||
assertNotNull(viewController);
|
||||
assertEquals("home", viewController.getViewName());
|
||||
|
||||
ParameterizableViewController redirectViewController = (ParameterizableViewController) hm.getUrlMap().get("/old");
|
||||
assertNotNull(redirectViewController);
|
||||
assertThat(redirectViewController.getView(), Matchers.instanceOf(RedirectView.class));
|
||||
|
||||
ParameterizableViewController statusViewController = (ParameterizableViewController) hm.getUrlMap().get("/bad");
|
||||
assertNotNull(statusViewController);
|
||||
assertEquals(404, statusViewController.getStatusCode().value());
|
||||
|
||||
BeanNameUrlHandlerMapping beanNameMapping = this.appContext.getBean(BeanNameUrlHandlerMapping.class);
|
||||
assertNotNull(beanNameMapping);
|
||||
|
||||
+4
-1
@@ -99,7 +99,8 @@ public class CssLinkResourceTransformerTests {
|
||||
TransformedResource transformedResource = (TransformedResource) resource;
|
||||
|
||||
String expected = "@import url(\"http://example.org/fonts/css\");\n" +
|
||||
"body { background: url(\"file:///home/spring/image.png\") }";
|
||||
"body { background: url(\"file:///home/spring/image.png\") }\n" +
|
||||
"figure { background: url(\"//example.org/style.css\")}";
|
||||
String result = new String(transformedResource.getByteArray(), "UTF-8");
|
||||
result = StringUtils.deleteAny(result, "\r");
|
||||
assertEquals(expected, result);
|
||||
@@ -108,6 +109,8 @@ public class CssLinkResourceTransformerTests {
|
||||
.resolveUrlPath("http://example.org/fonts/css", Arrays.asList(externalCss));
|
||||
Mockito.verify(resolverChain, Mockito.never())
|
||||
.resolveUrlPath("file:///home/spring/image.png", Arrays.asList(externalCss));
|
||||
Mockito.verify(resolverChain, Mockito.never())
|
||||
.resolveUrlPath("//example.org/style.css", Arrays.asList(externalCss));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+10
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -108,4 +108,13 @@ public class PathResourceResolverTests {
|
||||
assertTrue(this.resolver.checkResource(resource, servletContextLocation));
|
||||
}
|
||||
|
||||
// SPR-12624
|
||||
@Test
|
||||
public void checkRelativeLocation() throws Exception {
|
||||
String locationUrl= new UrlResource(getClass().getResource("./test/")).getURL().toExternalForm();
|
||||
Resource location = new UrlResource(locationUrl.replace("/springframework","/../org/springframework"));
|
||||
|
||||
assertNotNull(this.resolver.resolveResource(null, "main.css", Arrays.asList(location), null));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+64
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -21,11 +21,17 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.mock.web.test.MockServletContext;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@@ -38,6 +44,8 @@ import static org.junit.Assert.*;
|
||||
*/
|
||||
public class ResourceUrlProviderTests {
|
||||
|
||||
private List<Resource> locations;
|
||||
|
||||
private ResourceUrlProvider translator;
|
||||
|
||||
private ResourceHttpRequestHandler handler;
|
||||
@@ -47,9 +55,9 @@ public class ResourceUrlProviderTests {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
List<Resource> locations = new ArrayList<Resource>();
|
||||
locations.add(new ClassPathResource("test/", getClass()));
|
||||
locations.add(new ClassPathResource("testalternatepath/", getClass()));
|
||||
this.locations = new ArrayList<Resource>();
|
||||
this.locations.add(new ClassPathResource("test/", getClass()));
|
||||
this.locations.add(new ClassPathResource("testalternatepath/", getClass()));
|
||||
|
||||
this.handler = new ResourceHttpRequestHandler();
|
||||
this.handler.setLocations(locations);
|
||||
@@ -88,4 +96,56 @@ public class ResourceUrlProviderTests {
|
||||
this.translator.setHandlerMap(this.handlerMap);
|
||||
}
|
||||
|
||||
// SPR-12647
|
||||
@Test
|
||||
public void bestPatternMatch() throws Exception {
|
||||
ResourceHttpRequestHandler otherHandler = new ResourceHttpRequestHandler();
|
||||
otherHandler.setLocations(this.locations);
|
||||
Map<String, VersionStrategy> versionStrategyMap = new HashMap<>();
|
||||
versionStrategyMap.put("/**", new ContentVersionStrategy());
|
||||
VersionResourceResolver versionResolver = new VersionResourceResolver();
|
||||
versionResolver.setStrategyMap(versionStrategyMap);
|
||||
|
||||
List<ResourceResolver> resolvers = new ArrayList<ResourceResolver>();
|
||||
resolvers.add(versionResolver);
|
||||
resolvers.add(new PathResourceResolver());
|
||||
otherHandler.setResourceResolvers(resolvers);
|
||||
|
||||
this.handlerMap.put("/resources/*.css", otherHandler);
|
||||
initTranslator();
|
||||
|
||||
String url = this.translator.getForLookupPath("/resources/foo.css");
|
||||
assertEquals("/resources/foo-e36d2e05253c6c7085a91522ce43a0b4.css", url);
|
||||
}
|
||||
|
||||
// SPR-12592
|
||||
@Test
|
||||
public void initializeOnce() throws Exception {
|
||||
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
context.setServletContext(new MockServletContext());
|
||||
context.register(HandlerMappingConfiguration.class);
|
||||
context.refresh();
|
||||
ResourceUrlProvider translator = context.getBean(ResourceUrlProvider.class);
|
||||
assertThat(translator.getHandlerMap(), Matchers.hasKey("/resources/**"));
|
||||
assertFalse(translator.isAutodetect());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class HandlerMappingConfiguration {
|
||||
@Bean
|
||||
public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
|
||||
ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler();
|
||||
HashMap<String, ResourceHttpRequestHandler> handlerMap = new HashMap<String, ResourceHttpRequestHandler>();
|
||||
handlerMap.put("/resources/**", handler);
|
||||
SimpleUrlHandlerMapping hm = new SimpleUrlHandlerMapping();
|
||||
hm.setUrlMap(handlerMap);
|
||||
return hm;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResourceUrlProvider resourceUrlProvider() {
|
||||
return new ResourceUrlProvider();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user