From 42cdb200edf37af50d4bab8bbf0514434916ba69 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 3 Jan 2013 11:15:36 -0500 Subject: [PATCH 001/143] Fix null parameterName issue in content negotiation After this change ParameterContentNegotiationStrategy no longer allows a null parameter name, ContentNegotiationManagerFactoryBean also requires it. Issue: SPR-10139 --- .../web/accept/ContentNegotiationManagerFactoryBean.java | 4 +++- .../web/accept/ParameterContentNegotiationStrategy.java | 2 +- .../web/accept/ContentNegotiationManagerFactoryBeanTests.java | 3 +-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java index 8b805802d20..b05cbd7f97b 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java +++ b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java @@ -27,6 +27,7 @@ import javax.servlet.ServletContext; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.http.MediaType; +import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.web.context.ServletContextAware; @@ -55,7 +56,7 @@ public class ContentNegotiationManagerFactoryBean private Boolean useJaf; - private String parameterName; + private String parameterName = "format"; private MediaType defaultContentType; @@ -126,6 +127,7 @@ public class ContentNegotiationManagerFactoryBean *

The default parameter name is {@code "format"}. */ public void setParameterName(String parameterName) { + Assert.notNull(parameterName, "parameterName is required"); this.parameterName = parameterName; } diff --git a/spring-web/src/main/java/org/springframework/web/accept/ParameterContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/ParameterContentNegotiationStrategy.java index 0adaeaab523..c5fe9f1b4f7 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/ParameterContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/ParameterContentNegotiationStrategy.java @@ -44,7 +44,6 @@ public class ParameterContentNegotiationStrategy extends AbstractMappingContentN */ public ParameterContentNegotiationStrategy(Map mediaTypes) { super(mediaTypes); - Assert.notEmpty(mediaTypes, "Cannot look up media types without any mappings"); } /** @@ -52,6 +51,7 @@ public class ParameterContentNegotiationStrategy extends AbstractMappingContentN *

The default parameter name is {@code format}. */ public void setParameterName(String parameterName) { + Assert.notNull(parameterName, "parameterName is required"); this.parameterName = parameterName; } diff --git a/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java b/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java index 1851879b194..4d82e3f4459 100644 --- a/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java +++ b/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java @@ -88,7 +88,6 @@ public class ContentNegotiationManagerFactoryBeanTests { @Test public void favorParameter() throws Exception { this.factoryBean.setFavorParameter(true); - this.factoryBean.setParameterName("f"); Properties mediaTypes = new Properties(); mediaTypes.put("json", MediaType.APPLICATION_JSON_VALUE); @@ -98,7 +97,7 @@ public class ContentNegotiationManagerFactoryBeanTests { ContentNegotiationManager manager = this.factoryBean.getObject(); this.servletRequest.setRequestURI("/flower"); - this.servletRequest.addParameter("f", "json"); + this.servletRequest.addParameter("format", "json"); assertEquals(Arrays.asList(MediaType.APPLICATION_JSON), manager.resolveMediaTypes(this.webRequest)); } From 9f9f1ed2533110081c4a62b20d2b5658c8a68496 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 3 Jan 2013 15:13:19 -0500 Subject: [PATCH 002/143] Fix ClassCastException when setting media types Issue: SPR-10019 --- .../ContentNegotiationManagerFactoryBean.java | 58 +++++++++++++------ ...entNegotiationManagerFactoryBeanTests.java | 15 ++--- .../ContentNegotiationConfigurer.java | 14 +++-- .../view/ContentNegotiatingViewResolver.java | 5 +- 4 files changed, 61 insertions(+), 31 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java index b05cbd7f97b..8cf79f927d4 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java +++ b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Map.Entry; import java.util.Properties; import javax.servlet.ServletContext; @@ -52,7 +53,7 @@ public class ContentNegotiationManagerFactoryBean private boolean ignoreAcceptHeader = false; - private Properties mediaTypes = new Properties(); + private Map mediaTypes = new HashMap(); private Boolean useJaf; @@ -64,7 +65,6 @@ public class ContentNegotiationManagerFactoryBean private ServletContext servletContext; - /** * Indicate whether the extension of the request path should be used to determine * the requested media type with the highest priority. @@ -77,21 +77,43 @@ public class ContentNegotiationManagerFactoryBean } /** - * Add mappings from file extensions to media types. - *

If this property is not set, the Java Action Framework, if available, may - * still be used in conjunction with {@link #setFavorPathExtension(boolean)}. + * Add mappings from file extensions to media types represented as strings. + *

When this mapping is not set or when an extension is not found, the Java + * Action Framework, if available, may be used if enabled via + * {@link #setFavorPathExtension(boolean)}. + * + * @see #addMediaType(String, MediaType) + * @see #addMediaTypes(Map) */ public void setMediaTypes(Properties mediaTypes) { if (!CollectionUtils.isEmpty(mediaTypes)) { - for (Map.Entry entry : mediaTypes.entrySet()) { - String extension = ((String) entry.getKey()).toLowerCase(Locale.ENGLISH); + for (Entry entry : mediaTypes.entrySet()) { + String extension = ((String)entry.getKey()).toLowerCase(Locale.ENGLISH); this.mediaTypes.put(extension, MediaType.valueOf((String) entry.getValue())); } } } - public Properties getMediaTypes() { - return this.mediaTypes; + /** + * Add a mapping from a file extension to a media type. + *

If no mapping is added or when an extension is not found, the Java + * Action Framework, if available, may be used if enabled via + * {@link #setFavorPathExtension(boolean)}. + */ + public void addMediaType(String fileExtension, MediaType mediaType) { + this.mediaTypes.put(fileExtension, mediaType); + } + + /** + * Add mappings from file extensions to media types. + *

If no mappings are added or when an extension is not found, the Java + * Action Framework, if available, may be used if enabled via + * {@link #setFavorPathExtension(boolean)}. + */ + public void addMediaTypes(Map mediaTypes) { + if (mediaTypes != null) { + this.mediaTypes.putAll(mediaTypes); + } } /** @@ -99,6 +121,7 @@ public class ContentNegotiationManagerFactoryBean * to map from file extensions to media types. This is used only when * {@link #setFavorPathExtension(boolean)} is set to {@code true}. *

The default value is {@code true}. + * * @see #parameterName * @see #setMediaTypes(Properties) */ @@ -115,6 +138,7 @@ public class ContentNegotiationManagerFactoryBean * {@code "application/pdf"} regardless of the {@code Accept} header. *

To use this option effectively you must also configure the MediaType * type mappings via {@link #setMediaTypes(Properties)}. + * * @see #setParameterName(String) */ public void setFavorParameter(boolean favorParameter) { @@ -145,8 +169,8 @@ public class ContentNegotiationManagerFactoryBean /** * Set the default content type. *

This content type will be used when neither the request path extension, - * nor a request parameter, nor the {@code Accept} header could help determine - * the requested content type. + * nor a request parameter, nor the {@code Accept} header could help + * determine the requested content type. */ public void setDefaultContentType(MediaType defaultContentType) { this.defaultContentType = defaultContentType; @@ -159,16 +183,12 @@ public class ContentNegotiationManagerFactoryBean public void afterPropertiesSet() throws Exception { List strategies = new ArrayList(); - Map mediaTypesMap = new HashMap(); - CollectionUtils.mergePropertiesIntoMap(this.mediaTypes, mediaTypesMap); - if (this.favorPathExtension) { PathExtensionContentNegotiationStrategy strategy; if (this.servletContext != null) { - strategy = new ServletPathExtensionContentNegotiationStrategy(this.servletContext, mediaTypesMap); - } - else { - strategy = new PathExtensionContentNegotiationStrategy(mediaTypesMap); + strategy = new ServletPathExtensionContentNegotiationStrategy(this.servletContext, this.mediaTypes); + } else { + strategy = new PathExtensionContentNegotiationStrategy(this.mediaTypes); } if (this.useJaf != null) { strategy.setUseJaf(this.useJaf); @@ -177,7 +197,7 @@ public class ContentNegotiationManagerFactoryBean } if (this.favorParameter) { - ParameterContentNegotiationStrategy strategy = new ParameterContentNegotiationStrategy(mediaTypesMap); + ParameterContentNegotiationStrategy strategy = new ParameterContentNegotiationStrategy(this.mediaTypes); strategy.setParameterName(this.parameterName); strategies.add(strategy); } diff --git a/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java b/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java index 4d82e3f4459..d086093402c 100644 --- a/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java +++ b/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java @@ -19,7 +19,8 @@ import static org.junit.Assert.assertEquals; import java.util.Arrays; import java.util.Collections; -import java.util.Properties; +import java.util.HashMap; +import java.util.Map; import org.junit.Before; import org.junit.Test; @@ -74,9 +75,9 @@ public class ContentNegotiationManagerFactoryBeanTests { @Test public void addMediaTypes() throws Exception { - Properties mediaTypes = new Properties(); - mediaTypes.put("json", MediaType.APPLICATION_JSON_VALUE); - this.factoryBean.setMediaTypes(mediaTypes); + Map mediaTypes = new HashMap(); + mediaTypes.put("json", MediaType.APPLICATION_JSON); + this.factoryBean.addMediaTypes(mediaTypes); this.factoryBean.afterPropertiesSet(); ContentNegotiationManager manager = this.factoryBean.getObject(); @@ -89,9 +90,9 @@ public class ContentNegotiationManagerFactoryBeanTests { public void favorParameter() throws Exception { this.factoryBean.setFavorParameter(true); - Properties mediaTypes = new Properties(); - mediaTypes.put("json", MediaType.APPLICATION_JSON_VALUE); - this.factoryBean.setMediaTypes(mediaTypes); + Map mediaTypes = new HashMap(); + mediaTypes.put("json", MediaType.APPLICATION_JSON); + this.factoryBean.addMediaTypes(mediaTypes); this.factoryBean.afterPropertiesSet(); ContentNegotiationManager manager = this.factoryBean.getObject(); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.java index 9900bc32a79..7073e7122a3 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.java @@ -15,6 +15,7 @@ */ package org.springframework.web.servlet.config.annotation; +import java.util.HashMap; import java.util.Map; import javax.servlet.ServletContext; @@ -36,7 +37,9 @@ import org.springframework.web.accept.ContentNegotiationManagerFactoryBean; */ public class ContentNegotiationConfigurer { - private ContentNegotiationManagerFactoryBean factoryBean = new ContentNegotiationManagerFactoryBean(); + private final ContentNegotiationManagerFactoryBean factoryBean = new ContentNegotiationManagerFactoryBean(); + + private final Map mediaTypes = new HashMap(); /** @@ -64,7 +67,7 @@ public class ContentNegotiationConfigurer { * still be used in conjunction with {@link #favorPathExtension(boolean)}. */ public ContentNegotiationConfigurer mediaType(String extension, MediaType mediaType) { - this.factoryBean.getMediaTypes().put(extension, mediaType); + this.mediaTypes.put(extension, mediaType); return this; } @@ -75,7 +78,7 @@ public class ContentNegotiationConfigurer { */ public ContentNegotiationConfigurer mediaTypes(Map mediaTypes) { if (mediaTypes != null) { - this.factoryBean.getMediaTypes().putAll(mediaTypes); + this.mediaTypes.putAll(mediaTypes); } return this; } @@ -86,7 +89,7 @@ public class ContentNegotiationConfigurer { * still be used in conjunction with {@link #favorPathExtension(boolean)}. */ public ContentNegotiationConfigurer replaceMediaTypes(Map mediaTypes) { - this.factoryBean.getMediaTypes().clear(); + this.mediaTypes.clear(); mediaTypes(mediaTypes); return this; } @@ -157,6 +160,9 @@ public class ContentNegotiationConfigurer { * Return the configured {@link ContentNegotiationManager} instance */ protected ContentNegotiationManager getContentNegotiationManager() throws Exception { + if (!this.mediaTypes.isEmpty()) { + this.factoryBean.addMediaTypes(mediaTypes); + } this.factoryBean.afterPropertiesSet(); return this.factoryBean.getObject(); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/ContentNegotiatingViewResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/ContentNegotiatingViewResolver.java index cc497262972..2d543941af6 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/ContentNegotiatingViewResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/ContentNegotiatingViewResolver.java @@ -23,6 +23,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Properties; import java.util.Set; import javax.activation.FileTypeMap; @@ -196,7 +197,9 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport @Deprecated public void setMediaTypes(Map mediaTypes) { if (mediaTypes != null) { - this.cnManagerFactoryBean.getMediaTypes().putAll(mediaTypes); + Properties props = new Properties(); + props.putAll(mediaTypes); + this.cnManagerFactoryBean.setMediaTypes(props); } } From 00a86c37222d92b1477f2480b7bdfaebd12c2d1a Mon Sep 17 00:00:00 2001 From: Glyn Normington Date: Thu, 3 Jan 2013 14:50:45 +0000 Subject: [PATCH 003/143] Detect split packages at build time Split packages are a well-known anti-pattern for OSGi and a blocker for Eclipse Virgo (which prevents split packages being accessed via its Import-Library construct). Split packages are also unhelpful with a traditional linear classpath as a split package name does not uniquely identify the Spring framework JAR from which it came, thus complicating problem diagnosis and maintenance. Juergen Hoeller supports this position in the following comment in SPR-9990: >FWIW, I generally find split packages a bad practice, even without >OSGi in the mix. For the Spring Framework codebase, I consider a >split-package arrangement a design accident that we want to detect >in any case - and that we're willing to fix if it happened. > >I'm actually equally concerned about the source perspective: After >all, we want a package to be comprehensible from a single glance >at the project, not requiring the developer to jump into several >source modules to understand the overall layout of a package. Split packages have crept into Spring framework twice in recent months - see SPR-9811 and SPR-9988. Currently, they are only detected once the Spring framework has been converted to OSGi bundles and these bundles have been tested with Eclipse Virgo. This commit adds a build-time check for split packages to the Spring framework build. Issue: SPR-9990 Conflicts: build.gradle --- build.gradle | 6 ++ .../build/gradle/SplitPackageDetector.groovy | 85 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetector.groovy diff --git a/build.gradle b/build.gradle index 8c0a1b42452..b9a5e2e8385 100644 --- a/build.gradle +++ b/build.gradle @@ -8,6 +8,11 @@ buildscript { } } +configure(rootProject) { + def splitFound = new org.springframework.build.gradle.SplitPackageDetector('.', logger).diagnoseSplitPackages(); + assert !splitFound // see error log messages for details of split packages +} + configure(allprojects) { project -> group = "org.springframework" version = qualifyVersionIfNecessary(version) @@ -954,6 +959,7 @@ configure(rootProject) { "set GRADLE_OPTS=$gradleBatOpts %GRADLE_OPTS%\nset DEFAULT_JVM_OPTS=") } } + } /* diff --git a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetector.groovy b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetector.groovy new file mode 100644 index 00000000000..b17f859de1c --- /dev/null +++ b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetector.groovy @@ -0,0 +1,85 @@ +/* + * Copyright 2013 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.build.gradle + +class SplitPackageDetector { + + private static final String HIDDEN_DIRECTORY_PREFIX = "." + + private static final String JAVA_FILE_SUFFIX = ".java" + + private static final String SRC_MAIN_JAVA = "src" + File.separator + "main" + File.separator + "java" + + private final Map> pkgMap = [:] + + private final logger + + SplitPackageDetector(baseDir, logger) { + this.logger = logger + dirList(baseDir).each { File dir -> + def packages = getPackagesInDirectory(dir) + if (!packages.isEmpty()) { + pkgMap.put(dir, packages) + } + } + } + + private File[] dirList(String dir) { + dirList(new File(dir)) + } + + private File[] dirList(File dir) { + dir.listFiles({ file -> file.isDirectory() && !file.getName().startsWith(HIDDEN_DIRECTORY_PREFIX) } as FileFilter) + } + + private Set getPackagesInDirectory(File dir) { + def pkgs = new HashSet() + addPackagesInDirectory(pkgs, new File(dir, SRC_MAIN_JAVA), "") + return pkgs; + } + + boolean diagnoseSplitPackages() { + def splitFound = false; + def dirs = pkgMap.keySet().toArray() + def numDirs = dirs.length + for (int i = 0; i < numDirs - 1; i++) { + for (int j = i + 1; j < numDirs - 1; j++) { + def di = dirs[i] + def pi = new HashSet(pkgMap.get(di)) + def dj = dirs[j] + def pj = pkgMap.get(dj) + pi.retainAll(pj) + if (!pi.isEmpty()) { + logger.error("Packages $pi are split between directories '$di' and '$dj'") + splitFound = true + } + } + } + return splitFound + } + + private void addPackagesInDirectory(HashSet packages, File dir, String pkg) { + def scanDir = new File(dir, pkg) + def File[] javaFiles = scanDir.listFiles({ file -> !file.isDirectory() && file.getName().endsWith(JAVA_FILE_SUFFIX) } as FileFilter) + if (javaFiles != null && javaFiles.length != 0) { + packages.add(pkg) + } + dirList(scanDir).each { File subDir -> + addPackagesInDirectory(packages, dir, pkg + File.separator + subDir.getName()) + } + } +} \ No newline at end of file From a27a3be76c5812a97f7439187838c7bbaedaff97 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Thu, 3 Jan 2013 13:04:51 -0600 Subject: [PATCH 004/143] Example SplitPackageDetectorPlugin --- build.gradle | 7 +- .../build/gradle/SplitPackageDetector.groovy | 85 ------------ .../gradle/SplitPackageDetectorPlugin.groovy | 125 ++++++++++++++++++ 3 files changed, 130 insertions(+), 87 deletions(-) delete mode 100644 buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetector.groovy create mode 100644 buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy diff --git a/build.gradle b/build.gradle index b9a5e2e8385..6ab6f444de9 100644 --- a/build.gradle +++ b/build.gradle @@ -9,8 +9,11 @@ buildscript { } configure(rootProject) { - def splitFound = new org.springframework.build.gradle.SplitPackageDetector('.', logger).diagnoseSplitPackages(); - assert !splitFound // see error log messages for details of split packages + apply plugin: org.springframework.build.gradle.SplitPackageDetectorPlugin + + diagnoseSplitPackages { + inputDir = project.projectDir + } } configure(allprojects) { project -> diff --git a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetector.groovy b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetector.groovy deleted file mode 100644 index b17f859de1c..00000000000 --- a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetector.groovy +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2013 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.build.gradle - -class SplitPackageDetector { - - private static final String HIDDEN_DIRECTORY_PREFIX = "." - - private static final String JAVA_FILE_SUFFIX = ".java" - - private static final String SRC_MAIN_JAVA = "src" + File.separator + "main" + File.separator + "java" - - private final Map> pkgMap = [:] - - private final logger - - SplitPackageDetector(baseDir, logger) { - this.logger = logger - dirList(baseDir).each { File dir -> - def packages = getPackagesInDirectory(dir) - if (!packages.isEmpty()) { - pkgMap.put(dir, packages) - } - } - } - - private File[] dirList(String dir) { - dirList(new File(dir)) - } - - private File[] dirList(File dir) { - dir.listFiles({ file -> file.isDirectory() && !file.getName().startsWith(HIDDEN_DIRECTORY_PREFIX) } as FileFilter) - } - - private Set getPackagesInDirectory(File dir) { - def pkgs = new HashSet() - addPackagesInDirectory(pkgs, new File(dir, SRC_MAIN_JAVA), "") - return pkgs; - } - - boolean diagnoseSplitPackages() { - def splitFound = false; - def dirs = pkgMap.keySet().toArray() - def numDirs = dirs.length - for (int i = 0; i < numDirs - 1; i++) { - for (int j = i + 1; j < numDirs - 1; j++) { - def di = dirs[i] - def pi = new HashSet(pkgMap.get(di)) - def dj = dirs[j] - def pj = pkgMap.get(dj) - pi.retainAll(pj) - if (!pi.isEmpty()) { - logger.error("Packages $pi are split between directories '$di' and '$dj'") - splitFound = true - } - } - } - return splitFound - } - - private void addPackagesInDirectory(HashSet packages, File dir, String pkg) { - def scanDir = new File(dir, pkg) - def File[] javaFiles = scanDir.listFiles({ file -> !file.isDirectory() && file.getName().endsWith(JAVA_FILE_SUFFIX) } as FileFilter) - if (javaFiles != null && javaFiles.length != 0) { - packages.add(pkg) - } - dirList(scanDir).each { File subDir -> - addPackagesInDirectory(packages, dir, pkg + File.separator + subDir.getName()) - } - } -} \ No newline at end of file diff --git a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy new file mode 100644 index 00000000000..ed7e4212237 --- /dev/null +++ b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy @@ -0,0 +1,125 @@ +/* + * Copyright 2002-2012 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.build.gradle + +import java.io.File; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.gradle.api.* +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.ProjectDependency; +import org.gradle.api.artifacts.maven.Conf2ScopeMapping +import org.gradle.api.plugins.MavenPlugin +import org.gradle.api.tasks.* +import org.gradle.plugins.ide.eclipse.EclipsePlugin +import org.gradle.plugins.ide.eclipse.model.EclipseClasspath; +import org.gradle.plugins.ide.idea.IdeaPlugin +import org.gradle.api.invocation.* + + +class SplitPackageDetectorPlugin implements Plugin { + public void apply(Project project) { + Task diagnoseSplitPackages = project.tasks.add('diagnoseSplitPackages', SplitPackageDetectorTask.class) + diagnoseSplitPackages.setDescription('Detects split packages') + //project.tasks.findByName('build').dependsOn(diagnoseSplitPackages) + } +} + +public class SplitPackageDetectorTask extends DefaultTask { + @InputDirectory + File inputDir + + @TaskAction + public final void diagnoseSplitPackages() { + def projects = project.subprojects.findAll { it.plugins.findPlugin(org.springframework.build.gradle.MergePlugin) }.findAll { it.merge.into } + projects.each { p -> + println ' > The project directory '+ p.projectDir + ' will merge into ' + p.merge.into.projectDir + } + def splitFound = new org.springframework.build.gradle.SplitPackageDetector(inputDir.absolutePath, project.logger).diagnoseSplitPackages(); + assert !splitFound + } +} + +class SplitPackageDetector { + + private static final String HIDDEN_DIRECTORY_PREFIX = "." + + private static final String JAVA_FILE_SUFFIX = ".java" + + private static final String SRC_MAIN_JAVA = "src" + File.separator + "main" + File.separator + "java" + + private final Map> pkgMap = [:] + + private final logger + + SplitPackageDetector(baseDir, logger) { + this.logger = logger + dirList(baseDir).each { File dir -> + def packages = getPackagesInDirectory(dir) + if (!packages.isEmpty()) { + pkgMap.put(dir, packages) + } + } + } + + private File[] dirList(String dir) { + dirList(new File(dir)) + } + + private File[] dirList(File dir) { + dir.listFiles({ file -> file.isDirectory() && !file.getName().startsWith(HIDDEN_DIRECTORY_PREFIX) } as FileFilter) + } + + private Set getPackagesInDirectory(File dir) { + def pkgs = new HashSet() + addPackagesInDirectory(pkgs, new File(dir, SRC_MAIN_JAVA), "") + return pkgs; + } + + boolean diagnoseSplitPackages() { + def splitFound = false; + def dirs = pkgMap.keySet().toArray() + def numDirs = dirs.length + for (int i = 0; i < numDirs - 1; i++) { + for (int j = i + 1; j < numDirs - 1; j++) { + def di = dirs[i] + def pi = new HashSet(pkgMap.get(di)) + def dj = dirs[j] + def pj = pkgMap.get(dj) + pi.retainAll(pj) + if (!pi.isEmpty()) { + logger.error("Packages $pi are split between directories '$di' and '$dj'") + splitFound = true + } + } + } + return splitFound + } + + private void addPackagesInDirectory(HashSet packages, File dir, String pkg) { + def scanDir = new File(dir, pkg) + def File[] javaFiles = scanDir.listFiles({ file -> !file.isDirectory() && file.getName().endsWith(JAVA_FILE_SUFFIX) } as FileFilter) + if (javaFiles != null && javaFiles.length != 0) { + packages.add(pkg) + } + dirList(scanDir).each { File subDir -> + addPackagesInDirectory(packages, dir, pkg + File.separator + subDir.getName()) + } + } +} \ No newline at end of file From 2df08bdfbdb4efc9da46866eb1a3f6411a045e9e Mon Sep 17 00:00:00 2001 From: Glyn Normington Date: Fri, 4 Jan 2013 11:34:16 +0000 Subject: [PATCH 005/143] Rework split package detection code Allow packages to be split across projects which will be merged into a single JAR file. Make split package detection a dependency of the 'check' task. This is idiomatic gradle as well as allowing the 'test' task (another dependency of 'check') to be executed without split packages being detected. Omit the project spring-instructment-tomcat from the check on the basis of SPR-10150. Issues: SPR-9990, SPR-10150 Conflicts: build.gradle --- build.gradle | 19 +- .../gradle/SplitPackageDetectorPlugin.groovy | 170 +++++++++--------- 2 files changed, 97 insertions(+), 92 deletions(-) diff --git a/build.gradle b/build.gradle index 6ab6f444de9..697cdbea53d 100644 --- a/build.gradle +++ b/build.gradle @@ -8,14 +8,6 @@ buildscript { } } -configure(rootProject) { - apply plugin: org.springframework.build.gradle.SplitPackageDetectorPlugin - - diagnoseSplitPackages { - inputDir = project.projectDir - } -} - configure(allprojects) { project -> group = "org.springframework" version = qualifyVersionIfNecessary(version) @@ -761,13 +753,18 @@ configure(rootProject) { apply plugin: "docbook-reference" apply plugin: "groovy" apply from: "${gradleScriptDir}/jdiff.gradle" + apply plugin: org.springframework.build.gradle.SplitPackageDetectorPlugin - reference { + reference { sourceDir = file("src/reference/docbook") pdfFilename = "spring-framework-reference.pdf" } - // don"t publish the default jar for the root project + diagnoseSplitPackages { + projectsToScan = project.subprojects - project(":spring-instrument-tomcat") // SPR-10150 + } + + // don"t publish the default jar for the root project configurations.archives.artifacts.clear() dependencies { // for integration tests @@ -791,6 +788,8 @@ configure(rootProject) { testCompile("hsqldb:hsqldb:${hsqldbVersion}") } + check.dependsOn diagnoseSplitPackages + task api(type: Javadoc) { group = "Documentation" description = "Generates aggregated Javadoc API documentation." diff --git a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy index ed7e4212237..01c36fad7a2 100644 --- a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy +++ b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2013 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,110 +16,116 @@ package org.springframework.build.gradle -import java.io.File; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.gradle.api.* +import org.gradle.api.DefaultTask +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.ProjectDependency; +import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.artifacts.maven.Conf2ScopeMapping import org.gradle.api.plugins.MavenPlugin -import org.gradle.api.tasks.* +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.TaskAction import org.gradle.plugins.ide.eclipse.EclipsePlugin -import org.gradle.plugins.ide.eclipse.model.EclipseClasspath; +import org.gradle.plugins.ide.eclipse.model.EclipseClasspath import org.gradle.plugins.ide.idea.IdeaPlugin -import org.gradle.api.invocation.* - class SplitPackageDetectorPlugin implements Plugin { - public void apply(Project project) { - Task diagnoseSplitPackages = project.tasks.add('diagnoseSplitPackages', SplitPackageDetectorTask.class) - diagnoseSplitPackages.setDescription('Detects split packages') - //project.tasks.findByName('build').dependsOn(diagnoseSplitPackages) - } + public void apply(Project project) { + Task diagnoseSplitPackages = project.tasks.add('diagnoseSplitPackages', SplitPackageDetectorTask.class) + diagnoseSplitPackages.setDescription('Detects packages which will be split across JARs') + } } public class SplitPackageDetectorTask extends DefaultTask { - @InputDirectory - File inputDir + @Input + Set projectsToScan - @TaskAction - public final void diagnoseSplitPackages() { - def projects = project.subprojects.findAll { it.plugins.findPlugin(org.springframework.build.gradle.MergePlugin) }.findAll { it.merge.into } - projects.each { p -> - println ' > The project directory '+ p.projectDir + ' will merge into ' + p.merge.into.projectDir - } - def splitFound = new org.springframework.build.gradle.SplitPackageDetector(inputDir.absolutePath, project.logger).diagnoseSplitPackages(); - assert !splitFound - } + @TaskAction + public final void diagnoseSplitPackages() { + def Map mergeMap = [:] + def projects = projectsToScan.findAll { it.plugins.findPlugin(org.springframework.build.gradle.MergePlugin) }.findAll { it.merge.into } + projects.each { p -> + mergeMap.put(p, p.merge.into) + } + def splitFound = new org.springframework.build.gradle.SplitPackageDetector(projectsToScan, mergeMap, project.logger).diagnoseSplitPackages(); + assert !splitFound // see error log messages for details of split packages + } } class SplitPackageDetector { - private static final String HIDDEN_DIRECTORY_PREFIX = "." + private static final String HIDDEN_DIRECTORY_PREFIX = "." - private static final String JAVA_FILE_SUFFIX = ".java" + private static final String JAVA_FILE_SUFFIX = ".java" - private static final String SRC_MAIN_JAVA = "src" + File.separator + "main" + File.separator + "java" + private static final String SRC_MAIN_JAVA = "src" + File.separator + "main" + File.separator + "java" - private final Map> pkgMap = [:] + private static final String PACKAGE_SEPARATOR = "." - private final logger + private final Map mergeMap - SplitPackageDetector(baseDir, logger) { - this.logger = logger - dirList(baseDir).each { File dir -> - def packages = getPackagesInDirectory(dir) - if (!packages.isEmpty()) { - pkgMap.put(dir, packages) - } - } - } + private final Map> pkgMap = [:] - private File[] dirList(String dir) { - dirList(new File(dir)) - } + private final logger - private File[] dirList(File dir) { - dir.listFiles({ file -> file.isDirectory() && !file.getName().startsWith(HIDDEN_DIRECTORY_PREFIX) } as FileFilter) - } + SplitPackageDetector(projectsToScan, mergeMap, logger) { + this.mergeMap = mergeMap + this.logger = logger + projectsToScan.each { Project p -> + def dir = p.projectDir + def packages = getPackagesInDirectory(dir) + if (!packages.isEmpty()) { + pkgMap.put(p, packages) + } + } + } - private Set getPackagesInDirectory(File dir) { - def pkgs = new HashSet() - addPackagesInDirectory(pkgs, new File(dir, SRC_MAIN_JAVA), "") - return pkgs; - } + private File[] dirList(String dir) { + dirList(new File(dir)) + } - boolean diagnoseSplitPackages() { - def splitFound = false; - def dirs = pkgMap.keySet().toArray() - def numDirs = dirs.length - for (int i = 0; i < numDirs - 1; i++) { - for (int j = i + 1; j < numDirs - 1; j++) { - def di = dirs[i] - def pi = new HashSet(pkgMap.get(di)) - def dj = dirs[j] - def pj = pkgMap.get(dj) - pi.retainAll(pj) - if (!pi.isEmpty()) { - logger.error("Packages $pi are split between directories '$di' and '$dj'") - splitFound = true - } - } - } - return splitFound - } + private File[] dirList(File dir) { + dir.listFiles({ file -> file.isDirectory() && !file.getName().startsWith(HIDDEN_DIRECTORY_PREFIX) } as FileFilter) + } - private void addPackagesInDirectory(HashSet packages, File dir, String pkg) { - def scanDir = new File(dir, pkg) - def File[] javaFiles = scanDir.listFiles({ file -> !file.isDirectory() && file.getName().endsWith(JAVA_FILE_SUFFIX) } as FileFilter) - if (javaFiles != null && javaFiles.length != 0) { - packages.add(pkg) - } - dirList(scanDir).each { File subDir -> - addPackagesInDirectory(packages, dir, pkg + File.separator + subDir.getName()) - } - } + private Set getPackagesInDirectory(File dir) { + def pkgs = new HashSet() + addPackagesInDirectory(pkgs, new File(dir, SRC_MAIN_JAVA), "") + return pkgs; + } + + boolean diagnoseSplitPackages() { + def splitFound = false; + def projs = pkgMap.keySet().toArray() + def numProjects = projs.length + for (int i = 0; i < numProjects - 1; i++) { + for (int j = i + 1; j < numProjects - 1; j++) { + def pi = projs[i] + def pkgi = new HashSet(pkgMap.get(pi)) + def pj = projs[j] + def pkgj = pkgMap.get(pj) + pkgi.retainAll(pkgj) + if (!pkgi.isEmpty() && mergeMap.get(pi) != pj && mergeMap.get(pj) != pi) { + pkgi.each { pkg -> + def readablePkg = pkg.substring(1).replaceAll(File.separator, PACKAGE_SEPARATOR) + logger.error("Package '$readablePkg' is split between $pi and $pj") + } + splitFound = true + } + } + } + return splitFound + } + + private void addPackagesInDirectory(HashSet packages, File dir, String pkg) { + def scanDir = new File(dir, pkg) + def File[] javaFiles = scanDir.listFiles({ file -> !file.isDirectory() && file.getName().endsWith(JAVA_FILE_SUFFIX) } as FileFilter) + if (javaFiles != null && javaFiles.length != 0) { + packages.add(pkg) + } + dirList(scanDir).each { File subDir -> + addPackagesInDirectory(packages, dir, pkg + File.separator + subDir.getName()) + } + } } \ No newline at end of file From 153508a300772db262c6123f57fbc4bcbf89b4f4 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 7 Jan 2013 16:55:09 -0500 Subject: [PATCH 006/143] Support "X-Forwarded-Host" in UriComponentsBuilder ServletUriComponentsBuilder now supports setting the host to the value held in the X-Forwarded-Host [0] header used in reverse proxy scenarios. [0] http://tools.ietf.org/html/draft-ietf-appsawg-http-forwarded-10 Issue: SPR-10110 --- .../servlet/support/ServletUriComponentsBuilder.java | 12 +++++++++--- .../support/ServletUriComponentsBuilderTests.java | 12 +++++++++++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java index 8aa58643900..7ad0d49b78d 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2013 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. @@ -95,9 +95,12 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder { String scheme = request.getScheme(); int port = request.getServerPort(); + String header = request.getHeader("X-Forwarded-Host"); + String host = StringUtils.hasText(header) ? header: request.getServerName(); + ServletUriComponentsBuilder builder = new ServletUriComponentsBuilder(); builder.scheme(scheme); - builder.host(request.getServerName()); + builder.host(host); if ((scheme.equals("http") && port != 80) || (scheme.equals("https") && port != 443)) { builder.port(port); } @@ -138,7 +141,10 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder { return fromRequest(getCurrentRequest()); } - private static HttpServletRequest getCurrentRequest() { + /** + * Obtain the request through {@link RequestContextHolder}. + */ + protected static HttpServletRequest getCurrentRequest() { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); Assert.state(requestAttributes != null, "Could not find current request via RequestContextHolder"); Assert.isInstanceOf(ServletRequestAttributes.class, requestAttributes); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/support/ServletUriComponentsBuilderTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/support/ServletUriComponentsBuilderTests.java index d87212008c9..02ee04662d1 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/support/ServletUriComponentsBuilderTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/support/ServletUriComponentsBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -83,6 +83,16 @@ public class ServletUriComponentsBuilderTests { assertEquals("http://localhost/mvc-showcase/data/param", result); } + @Test + public void fromRequestWithForwardedHostHeader() { + request.addHeader("X-Forwarded-Host", "anotherHost"); + request.setRequestURI("/mvc-showcase/data/param"); + request.setQueryString("foo=123"); + String result = ServletUriComponentsBuilder.fromRequest(request).build().toUriString(); + + assertEquals("http://anotherHost/mvc-showcase/data/param?foo=123", result); + } + @Test public void fromContextPath() { request.setRequestURI("/mvc-showcase/data/param"); From 7bc9667913571d17c38f2c1854af9eebadffa832 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 7 Jan 2013 18:03:40 -0500 Subject: [PATCH 007/143] Add support for placeholders in @RequestMapping @RequestMapping annotations now support ${...} placeholders. Issue: SPR-9935 --- .../web/bind/annotation/RequestMapping.java | 1 + .../RequestMappingHandlerMapping.java | 36 ++++++++++++++++--- .../RequestMappingHandlerMappingTests.java | 17 ++++++++- src/reference/docbook/mvc.xml | 12 +++++++ 4 files changed, 61 insertions(+), 5 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java b/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java index ff4849e35e2..320bb95d133 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java +++ b/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMapping.java @@ -266,6 +266,7 @@ public @interface RequestMapping { * Ant-style path patterns are also supported (e.g. "/myPath/*.do"). * At the method level, relative paths (e.g. "edit.do") are supported * within the primary mapping expressed at the type level. + * Path mapping URIs may contain placeholders (e.g. "/${connect}") *

In a Portlet environment: the mapped portlet modes * (i.e. "EDIT", "VIEW", "HELP" or any custom modes). *

Supported at the type level as well as at the method level! diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index 315cf8f68e8..5700b8def72 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,9 +20,11 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; +import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.stereotype.Controller; import org.springframework.util.Assert; +import org.springframework.util.StringValueResolver; import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.mvc.condition.AbstractRequestCondition; @@ -46,7 +48,8 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMappi * @author Rossen Stoyanchev * @since 3.1 */ -public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping { +public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping + implements EmbeddedValueResolverAware { private boolean useSuffixPatternMatch = true; @@ -58,6 +61,8 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi private final List fileExtensions = new ArrayList(); + private StringValueResolver embeddedValueResolver; + /** * Whether to use suffix pattern match (".*") when matching patterns to @@ -101,6 +106,11 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi this.useTrailingSlashMatch = useTrailingSlashMatch; } + @Override + public void setEmbeddedValueResolver(StringValueResolver resolver) { + this.embeddedValueResolver = resolver; + } + /** * Set the {@link ContentNegotiationManager} to use to determine requested media types. * If not set, the default constructor is used. @@ -142,7 +152,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi * Return the file extensions to use for suffix pattern matching. */ public List getFileExtensions() { - return fileExtensions; + return this.fileExtensions; } @Override @@ -227,8 +237,9 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi * Created a RequestMappingInfo from a RequestMapping annotation. */ private RequestMappingInfo createRequestMappingInfo(RequestMapping annotation, RequestCondition customCondition) { + String[] patterns = resolveEmbeddedValuesInPatterns(annotation.value()); return new RequestMappingInfo( - new PatternsRequestCondition(annotation.value(), getUrlPathHelper(), getPathMatcher(), + new PatternsRequestCondition(patterns, getUrlPathHelper(), getPathMatcher(), this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions), new RequestMethodsRequestCondition(annotation.method()), new ParamsRequestCondition(annotation.params()), @@ -238,4 +249,21 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi customCondition); } + /** + * Resolve placeholder values in the given array of patterns. + * @return a new array with updated patterns + */ + protected String[] resolveEmbeddedValuesInPatterns(String[] patterns) { + if (this.embeddedValueResolver == null) { + return patterns; + } + else { + String[] resolvedPatterns = new String[patterns.length]; + for (int i=0; i < patterns.length; i++) { + resolvedPatterns[i] = this.embeddedValueResolver.resolveStringValue(patterns[i]); + } + return resolvedPatterns; + } + } + } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java index b5a6bd139e5..5c4d0776a49 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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 org.junit.Before; import org.junit.Test; import org.springframework.http.MediaType; +import org.springframework.util.StringValueResolver; import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.accept.PathExtensionContentNegotiationStrategy; import org.springframework.web.context.support.StaticWebApplicationContext; @@ -78,4 +79,18 @@ public class RequestMappingHandlerMappingTests { this.handlerMapping.useSuffixPatternMatch()); } + @Test + public void resolveEmbeddedValuesInPatterns() { + this.handlerMapping.setEmbeddedValueResolver(new StringValueResolver() { + public String resolveStringValue(String value) { + return "/${pattern}/bar".equals(value) ? "/foo/bar" : value; + } + }); + + String[] patterns = new String[] { "/foo", "/${pattern}/bar" }; + String[] result = this.handlerMapping.resolveEmbeddedValuesInPatterns(patterns); + + assertArrayEquals(new String[] { "/foo", "/foo/bar" }, result); + } + } diff --git a/src/reference/docbook/mvc.xml b/src/reference/docbook/mvc.xml index ffe081c1e09..279a76ec159 100644 --- a/src/reference/docbook/mvc.xml +++ b/src/reference/docbook/mvc.xml @@ -1073,6 +1073,18 @@ public class RelativePathUriTemplateController { /owners/*/pets/{petId}). +

+ Patterns with Placeholders + + Patterns in @RequestMapping annotations + support ${...} placeholders against local properties and/or system properties + and environment variables. This may be useful in cases where the path a + controller is mapped to may need to be customized through configuration. + For more information on placeholders see the Javadoc for + PropertyPlaceholderConfigurer. + +
+
Matrix Variables From b8f223c404db419927d4f9763d71e10ca38d37dd Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Tue, 8 Jan 2013 11:30:22 +0100 Subject: [PATCH 008/143] Add 3.1 migration section to reference docs Address error raised when using component-scan against the unqualified "org" base package. Issue: SPR-9843 --- src/reference/docbook/index.xml | 3 ++ src/reference/docbook/migration-3.1.xml | 37 +++++++++++++++++++++++++ src/reference/docbook/new-in-3.1.xml | 3 +- 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/reference/docbook/migration-3.1.xml diff --git a/src/reference/docbook/index.xml b/src/reference/docbook/index.xml index 393858cecda..de4af30072f 100644 --- a/src/reference/docbook/index.xml +++ b/src/reference/docbook/index.xml @@ -471,6 +471,9 @@ + + diff --git a/src/reference/docbook/migration-3.1.xml b/src/reference/docbook/migration-3.1.xml new file mode 100644 index 00000000000..15ca8d49798 --- /dev/null +++ b/src/reference/docbook/migration-3.1.xml @@ -0,0 +1,37 @@ + + + Migrating to Spring Framework 3.1 + + In this appendix we discuss what users will want to know when upgrading to + Spring Framework 3.1. For a general overview of features, please see + + +
+ Component scanning against the "org" base package + Spring Framework 3.1 introduces a number of @Configuration + classes such as org.springframework.cache.annotation.ProxyCachingConfiguration + and + org.springframework.scheduling.annotation.ProxyAsyncConfiguration. + Because @Configuration is ultimately meta-annotated with Spring's + @Component annotation, these classes will inadvertently be scanned + and processed by the container for any component-scanning directive against the + unqualified "org" package, e.g.: + + <context:component-scan base-package="org"/> + Therefore, in order to avoid errors like the one reported in SPR-9843, + any such directives should be updated to at least one more level of qualification e.g.: + + <context:component-scan base-package="org.xyz"/> + Alternatively, an exclude-filter may be used. See + context:component-scan + documentation for details. +
+
diff --git a/src/reference/docbook/new-in-3.1.xml b/src/reference/docbook/new-in-3.1.xml index ef69b647d06..8a56bfd6472 100644 --- a/src/reference/docbook/new-in-3.1.xml +++ b/src/reference/docbook/new-in-3.1.xml @@ -11,7 +11,8 @@ This is a list of new features for Spring Framework 3.1. A number of features do not have dedicated reference documentation but do have complete Javadoc. In such - cases, fully-qualified class names are given. + cases, fully-qualified class names are given. See also +
Cache Abstraction From 23912771154c54f63fdc0034c181edee07f9bb89 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 8 Jan 2013 06:57:58 -0500 Subject: [PATCH 009/143] Fix issue in BufferedImageHttpMessageConverter This change ensures BufferedImageHttpMessageConverter writes when "*/*" is requested. Issue: SPR-7763 --- .../http/converter/BufferedImageHttpMessageConverter.java | 4 ++-- .../converter/BufferedImageHttpMessageConverterTests.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/converter/BufferedImageHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/BufferedImageHttpMessageConverter.java index b9392d0d95d..bd9f32d1467 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/BufferedImageHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/BufferedImageHttpMessageConverter.java @@ -132,7 +132,7 @@ public class BufferedImageHttpMessageConverter implements HttpMessageConverter imageWriters = ImageIO.getImageWritersByMIMEType(mediaType.toString()); @@ -191,7 +191,7 @@ public class BufferedImageHttpMessageConverter implements HttpMessageConverter 0); BufferedImage result = ImageIO.read(new ByteArrayInputStream(outputMessage.getBodyAsBytes())); From 87109b348cf1fcae2d4aefe64f9ce6b8fcf389a0 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 8 Jan 2013 09:21:17 -0500 Subject: [PATCH 010/143] Add decoding matrix variable values Issue: SPR-10140 --- .../web/util/UrlPathHelper.java | 29 ++++++++++++ .../RequestMappingInfoHandlerMapping.java | 9 ++-- ...RequestMappingInfoHandlerMappingTests.java | 47 ++++++++++++------- 3 files changed, 66 insertions(+), 19 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java b/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java index 67b7a4c024f..72b592108ad 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java +++ b/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java @@ -27,6 +27,8 @@ import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; /** @@ -491,6 +493,33 @@ public class UrlPathHelper { } } + /** + * Decode the given matrix variables via + * {@link #decodeRequestString(HttpServletRequest, String)} unless + * {@link #setUrlDecode(boolean)} is set to {@code true} in which case it is + * assumed the URL path from which the variables were extracted is already + * decoded through a call to + * {@link #getLookupPathForRequest(HttpServletRequest)}. + * + * @param request current HTTP request + * @param vars URI variables extracted from the URL path + * @return the same Map or a new Map instance + */ + public MultiValueMap decodeMatrixVariables(HttpServletRequest request, MultiValueMap vars) { + if (this.urlDecode) { + return vars; + } + else { + MultiValueMap decodedVars = new LinkedMultiValueMap (vars.size()); + for (String key : vars.keySet()) { + for (String value : vars.get(key)) { + decodedVars.add(key, decodeInternal(request, value)); + } + } + return decodedVars; + } + } + private boolean shouldRemoveTrailingServletPathSlash(HttpServletRequest request) { if (request.getAttribute(WEBSPHERE_URI_ATTRIBUTE) == null) { // Regular servlet container: behaves as expected in any case, diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java index dfdfc05ed25..7654d314986 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java @@ -100,7 +100,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables); if (isMatrixVariableContentAvailable()) { - request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, extractMatrixVariables(uriVariables)); + request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, extractMatrixVariables(request, uriVariables)); } if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) { @@ -113,7 +113,9 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe return !getUrlPathHelper().shouldRemoveSemicolonContent(); } - private Map> extractMatrixVariables(Map uriVariables) { + private Map> extractMatrixVariables( + HttpServletRequest request, Map uriVariables) { + Map> result = new LinkedHashMap>(); for (Entry uriVar : uriVariables.entrySet()) { String uriVarValue = uriVar.getValue(); @@ -134,7 +136,8 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe uriVariables.put(uriVar.getKey(), uriVarValue.substring(0, semicolonIndex)); } - result.put(uriVar.getKey(), WebUtils.parseMatrixVariables(matrixVariables)); + MultiValueMap vars = WebUtils.parseMatrixVariables(matrixVariables); + result.put(uriVar.getKey(), getUrlPathHelper().decodeMatrixVariables(request, vars)); } return result; } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java index e0764f7e7bc..841318af71a 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java @@ -310,48 +310,63 @@ public class RequestMappingInfoHandlerMappingTests { MultiValueMap matrixVariables; Map uriVariables; - String lookupPath = "/cars;colors=red,blue,green;year=2012"; - - // Pattern "/{cars}" : matrix variables stripped from "cars" variable - request = new MockHttpServletRequest(); - testHandleMatch(request, "/{cars}", lookupPath); + testHandleMatch(request, "/{cars}", "/cars;colors=red,blue,green;year=2012"); matrixVariables = getMatrixVariables(request, "cars"); + uriVariables = getUriTemplateVariables(request); + assertNotNull(matrixVariables); assertEquals(Arrays.asList("red", "blue", "green"), matrixVariables.get("colors")); assertEquals("2012", matrixVariables.getFirst("year")); - - uriVariables = getUriTemplateVariables(request); assertEquals("cars", uriVariables.get("cars")); - // Pattern "/{cars:[^;]+}{params}" : "cars" and "params" variables unchanged - request = new MockHttpServletRequest(); - testHandleMatch(request, "/{cars:[^;]+}{params}", lookupPath); + testHandleMatch(request, "/{cars:[^;]+}{params}", "/cars;colors=red,blue,green;year=2012"); matrixVariables = getMatrixVariables(request, "params"); + uriVariables = getUriTemplateVariables(request); + assertNotNull(matrixVariables); assertEquals(Arrays.asList("red", "blue", "green"), matrixVariables.get("colors")); assertEquals("2012", matrixVariables.getFirst("year")); - - uriVariables = getUriTemplateVariables(request); assertEquals("cars", uriVariables.get("cars")); assertEquals(";colors=red,blue,green;year=2012", uriVariables.get("params")); - // matrix variables not present : "params" variable is empty - request = new MockHttpServletRequest(); testHandleMatch(request, "/{cars:[^;]+}{params}", "/cars"); matrixVariables = getMatrixVariables(request, "params"); - assertNull(matrixVariables); - uriVariables = getUriTemplateVariables(request); + + assertNull(matrixVariables); assertEquals("cars", uriVariables.get("cars")); assertEquals("", uriVariables.get("params")); } + @Test + public void matrixVariablesDecoding() { + + MockHttpServletRequest request; + + UrlPathHelper urlPathHelper = new UrlPathHelper(); + urlPathHelper.setUrlDecode(false); + urlPathHelper.setRemoveSemicolonContent(false); + + this.handlerMapping.setUrlPathHelper(urlPathHelper ); + + request = new MockHttpServletRequest(); + testHandleMatch(request, "/path{filter}", "/path;mvar=a%2fb"); + + MultiValueMap matrixVariables = getMatrixVariables(request, "filter"); + Map uriVariables = getUriTemplateVariables(request); + + assertNotNull(matrixVariables); + assertEquals(Arrays.asList("a/b"), matrixVariables.get("mvar")); + assertEquals(";mvar=a/b", uriVariables.get("filter")); + } + + private void testHandleMatch(MockHttpServletRequest request, String pattern, String lookupPath) { PatternsRequestCondition patterns = new PatternsRequestCondition(pattern); RequestMappingInfo info = new RequestMappingInfo(patterns, null, null, null, null, null, null); From 2ac4a8c5411f8642ac2c40503678e5d7a4b56dbf Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 8 Jan 2013 09:32:48 -0500 Subject: [PATCH 011/143] Update mvc chapter on annotations and proxies Issue: SPR-10132 --- src/reference/docbook/mvc.xml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/reference/docbook/mvc.xml b/src/reference/docbook/mvc.xml index 279a76ec159..f4d3e3e802e 100644 --- a/src/reference/docbook/mvc.xml +++ b/src/reference/docbook/mvc.xml @@ -867,7 +867,10 @@ public class ClinicController { @Transactional methods). Usually you will introduce an interface for the controller in order to use JDK dynamic proxies. To make this work you must move the - @RequestMapping annotations to the + @RequestMapping annotations, as well as + any other type and method-level annotations (e.g. + @ModelAttribute, + @InitBinder) to the interface as well as the mapping mechanism can only "see" the interface exposed by the proxy. Alternatively, you could activate proxy-target-class="true" in the configuration for the @@ -876,6 +879,10 @@ public class ClinicController { that CGLIB-based subclass proxies should be used instead of interface-based JDK proxies. For more information on various proxying mechanisms see . + + Note however that method argument annotations, e.g. + @RequestParam, must be present in + the method signatures of the controller class.
From c7d0054a9e60b89a1cfbe184e9bbcb983f9231cb Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 8 Jan 2013 09:36:19 -0500 Subject: [PATCH 012/143] Remove closing tag in form tag's extra hidden fields The fields returned from a RequestDataValueProcessor are now written as hidden fields without a closing tag. Before: After: Issue: SPR-10103 --- .../org/springframework/web/servlet/tags/form/FormTag.java | 4 ++-- .../springframework/web/servlet/tags/form/FormTagTests.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java index f827631d62e..17e30d88d32 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java @@ -469,8 +469,8 @@ public class FormTag extends AbstractHtmlElementTag { if (hiddenFields != null) { for (String name : hiddenFields.keySet()) { this.tagWriter.appendValue(""); - this.tagWriter.appendValue("\n"); + this.tagWriter.appendValue("name=\"" + name + "\" value=\"" + hiddenFields.get(name) + "\" "); + this.tagWriter.appendValue("/>\n"); } } } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java index 3bd879a1eac..b22930686de 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java @@ -293,7 +293,7 @@ public class FormTagTests extends AbstractHtmlElementTagTests { String output = getOutput(); - assertEquals("", getInputTag(output)); + assertEquals("", getInputTag(output)); assertFormTagOpened(output); assertFormTagClosed(output); From a56d8f28fe3c434d04d92ec6cd7ff60848cf8a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnaud=20Cogolu=C3=A8gnes?= Date: Fri, 14 Dec 2012 10:05:25 +0100 Subject: [PATCH 013/143] Add support for HTTP OPTIONS in Spring MVC Test Issue: SPR-10093 --- .../web/servlet/MockMvcBuilderSupport.java | 3 +- .../request/MockMvcRequestBuilders.java | 11 +++ .../servlet/setup/DefaultMockMvcBuilder.java | 16 +++- .../test/web/servlet/Spr10093Tests.java | 94 +++++++++++++++++++ 4 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 spring-test-mvc/src/test/java/org/springframework/test/web/servlet/Spr10093Tests.java diff --git a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/MockMvcBuilderSupport.java b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/MockMvcBuilderSupport.java index 75a50db9df7..c39e3dbee0b 100644 --- a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/MockMvcBuilderSupport.java +++ b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/MockMvcBuilderSupport.java @@ -43,11 +43,12 @@ public abstract class MockMvcBuilderSupport { protected final MockMvc createMockMvc(Filter[] filters, MockServletConfig servletConfig, WebApplicationContext webAppContext, RequestBuilder defaultRequestBuilder, - List globalResultMatchers, List globalResultHandlers) { + List globalResultMatchers, List globalResultHandlers, Boolean dispatchOptions) { ServletContext servletContext = webAppContext.getServletContext(); TestDispatcherServlet dispatcherServlet = new TestDispatcherServlet(webAppContext); + dispatcherServlet.setDispatchOptionsRequest(dispatchOptions); try { dispatcherServlet.init(servletConfig); } diff --git a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java index da96fd9cd22..fbbc9b026d7 100644 --- a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java +++ b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java @@ -80,6 +80,17 @@ public abstract class MockMvcRequestBuilders { return new MockHttpServletRequestBuilder(HttpMethod.DELETE, urlTemplate, urlVariables); } + /** + * Create a {@link MockHttpServletRequestBuilder} for an OPTIONS request. + * + * @param urlTemplate a URL template; the resulting URL will be encoded + * @param urlVariables zero or more URL variables + */ + public static MockHttpServletRequestBuilder options(String urlTemplate, Object... urlVariables) { + return new MockHttpServletRequestBuilder(HttpMethod.OPTIONS, urlTemplate, urlVariables); + } + + /** * Create a {@link MockHttpServletRequestBuilder} for a request with the given HTTP method. * diff --git a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilder.java b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilder.java index a97b38aeee2..984be0c8542 100644 --- a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilder.java +++ b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilder.java @@ -31,6 +31,7 @@ import org.springframework.test.web.servlet.ResultHandler; import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.util.Assert; import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.DispatcherServlet; /** * An concrete implementation of {@link MockMvcBuilder} with methods for @@ -53,6 +54,8 @@ public class DefaultMockMvcBuilder extends MockMvcB private final List globalResultMatchers = new ArrayList(); private final List globalResultHandlers = new ArrayList(); + + private Boolean dispatchOptions = Boolean.FALSE; /** @@ -177,6 +180,17 @@ public class DefaultMockMvcBuilder extends MockMvcB this.globalResultHandlers.add(resultHandler); return (T) this; } + + /** + * Should the {@link DispatcherServlet} dispatch OPTIONS request to controllers. + * @param dispatchOptions + * @see {@link DispatcherServlet#setDispatchOptionsRequest(boolean)} + */ + @SuppressWarnings("unchecked") + public final T dispatchOptions(boolean dispatchOptions) { + this.dispatchOptions = dispatchOptions; + return (T) this; + } /** * Build a {@link MockMvc} instance. @@ -191,7 +205,7 @@ public class DefaultMockMvcBuilder extends MockMvcB Filter[] filterArray = this.filters.toArray(new Filter[this.filters.size()]); return super.createMockMvc(filterArray, mockServletConfig, this.webAppContext, - this.defaultRequestBuilder, this.globalResultMatchers, this.globalResultHandlers); + this.defaultRequestBuilder, this.globalResultMatchers, this.globalResultHandlers,this.dispatchOptions); } /** diff --git a/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/Spr10093Tests.java b/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/Spr10093Tests.java new file mode 100644 index 00000000000..32713e951c7 --- /dev/null +++ b/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/Spr10093Tests.java @@ -0,0 +1,94 @@ +/* + * Copyright 2002-2013 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.web.servlet; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Controller; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +/** + * Tests for SPR-10093 (support for OPTIONS requests). + * @author Arnaud Cogoluègnes + */ +@RunWith(SpringJUnit4ClassRunner.class) +@WebAppConfiguration +@ContextConfiguration +public class Spr10093Tests { + + @Autowired + private WebApplicationContext wac; + + private MockMvc mockMvc; + + @Before + public void setup() { + this.mockMvc = webAppContextSetup(this.wac).dispatchOptions(true).build(); + } + + @Test + public void test() throws Exception { + MyController controller = this.wac.getBean(MyController.class); + int initialCount = controller.counter.get(); + this.mockMvc.perform(options("/myUrl")).andExpect(status().isOk()); + + Assert.assertEquals(initialCount+1,controller.counter.get()); + } + + + @Configuration + @EnableWebMvc + static class WebConfig extends WebMvcConfigurerAdapter { + + @Bean + public MyController myController() { + return new MyController(); + } + } + + @Controller + private static class MyController { + + private AtomicInteger counter = new AtomicInteger(0); + + @RequestMapping(value="/myUrl",method=RequestMethod.OPTIONS) + @ResponseBody + public void handle() { + counter.incrementAndGet(); + } + } + + +} From 0829cbfdd3adc4459e47b6c4e1411dba796f964a Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Tue, 8 Jan 2013 16:23:46 +0100 Subject: [PATCH 014/143] Update license header for recently modified files Issue: SPR-7763, SPR-10140, SPR-10132, SPR-10093, SPR-10103 --- .../springframework/test/web/servlet/MockMvcBuilderSupport.java | 2 +- .../test/web/servlet/request/MockMvcRequestBuilders.java | 2 +- .../test/web/servlet/setup/DefaultMockMvcBuilder.java | 2 +- .../http/converter/BufferedImageHttpMessageConverter.java | 2 +- .../main/java/org/springframework/web/util/UrlPathHelper.java | 2 +- .../http/converter/BufferedImageHttpMessageConverterTests.java | 2 +- .../servlet/mvc/method/RequestMappingInfoHandlerMapping.java | 2 +- .../java/org/springframework/web/servlet/tags/form/FormTag.java | 2 +- .../mvc/method/RequestMappingInfoHandlerMappingTests.java | 2 +- .../org/springframework/web/servlet/tags/form/FormTagTests.java | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/MockMvcBuilderSupport.java b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/MockMvcBuilderSupport.java index c39e3dbee0b..d2e1054a636 100644 --- a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/MockMvcBuilderSupport.java +++ b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/MockMvcBuilderSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. diff --git a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java index fbbc9b026d7..bb0c109a099 100644 --- a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java +++ b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. diff --git a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilder.java b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilder.java index 984be0c8542..19c50002de1 100644 --- a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilder.java +++ b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. diff --git a/spring-web/src/main/java/org/springframework/http/converter/BufferedImageHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/BufferedImageHttpMessageConverter.java index bd9f32d1467..74c1772558b 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/BufferedImageHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/BufferedImageHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2013 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. diff --git a/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java b/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java index 72b592108ad..7f98ee23696 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java +++ b/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. diff --git a/spring-web/src/test/java/org/springframework/http/converter/BufferedImageHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/BufferedImageHttpMessageConverterTests.java index 6bfca8ae210..0dcb8703bf9 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/BufferedImageHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/BufferedImageHttpMessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2013 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. diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java index 7654d314986..e55ec1a3cad 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java index 17e30d88d32..d3b6752adef 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java index 841318af71a..1d9168926b8 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java index b22930686de..23bacd5b945 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. From 24ffa5a706d1b8538ff1359c635905f0f9af9fb5 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 8 Jan 2013 10:47:25 -0500 Subject: [PATCH 015/143] Update info on RestTemplate related to 401 status code Issue: SPR-9367 --- src/reference/docbook/remoting.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/reference/docbook/remoting.xml b/src/reference/docbook/remoting.xml index 4bd5acc920d..f642f9d653b 100644 --- a/src/reference/docbook/remoting.xml +++ b/src/reference/docbook/remoting.xml @@ -1401,6 +1401,12 @@ String result = can in turn be configured with credentials information or connection pooling functionality. + Note that the java.net implementation for + HTTP requests may raise an exception when accessing the status of a response + that represents an error (e.g. 401). If this is an issue, switch to + HttpComponentsClientHttpRequestFactory instead. + + The previous example using Apache HttpComponents HttpClient directly rewritten to use the RestTemplate is shown below From 68d4a70f8e7315d227b7727b087a65e64f4b0f9e Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 8 Jan 2013 11:44:24 -0500 Subject: [PATCH 016/143] Add option to always append 'must-revalidate' Issue: SPR-9248 --- .../servlet/support/WebContentGenerator.java | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java index 7f759e8c97f..ec1cf27cc30 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -26,7 +26,9 @@ import javax.servlet.http.HttpServletResponse; import org.springframework.util.StringUtils; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.HttpSessionRequiredException; +import org.springframework.web.context.request.WebRequest; import org.springframework.web.context.support.WebApplicationObjectSupport; +import org.springframework.web.servlet.mvc.LastModified; /** * Convenient superclass for any kind of web content generator, @@ -79,6 +81,8 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport { private int cacheSeconds = -1; + private boolean alwaysMustRevalidate = false; + /** * Create a new WebContentGenerator which supports @@ -194,6 +198,25 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport { return this.useCacheControlNoStore; } + /** + * An option to add 'must-revalidate' to every Cache-Control header. This + * may be useful with annotated controller methods, which can + * programmatically do a lastModified calculation as described in + * {@link WebRequest#checkNotModified(long)}. Default is "false", + * effectively relying on whether the handler implements + * {@link LastModified} or not. + */ + public void setAlwaysMustRevalidate(boolean mustRevalidate) { + this.alwaysMustRevalidate = mustRevalidate; + } + + /** + * Return whether 'must-revaliate' is added to every Cache-Control header. + */ + public boolean isAlwaysMustRevalidate() { + return alwaysMustRevalidate; + } + /** * Cache content for the given number of seconds. Default is -1, * indicating no generation of cache-related headers. @@ -313,7 +336,7 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport { if (this.useCacheControlHeader) { // HTTP 1.1 header String headerValue = "max-age=" + seconds; - if (mustRevalidate) { + if (mustRevalidate || this.alwaysMustRevalidate) { headerValue += ", must-revalidate"; } response.setHeader(HEADER_CACHE_CONTROL, headerValue); From f185c3a3c18f11770494060182780af61a8c9e55 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 8 Jan 2013 12:03:51 -0500 Subject: [PATCH 017/143] Add option to not trim path segments in AntPathMatch Issue: SPR-8687 --- .../org/springframework/util/AntPathMatcher.java | 16 +++++++++++----- .../util/AntPathMatcherTests.java | 10 +++++++++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java b/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java index bbceda14dc5..0ae86e08792 100644 --- a/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java +++ b/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -60,12 +60,18 @@ public class AntPathMatcher implements PathMatcher { private final Map stringMatcherCache = new ConcurrentHashMap(256); + private boolean trimTokens = true; + /** Set the path separator to use for pattern parsing. Default is "/", as in Ant. */ public void setPathSeparator(String pathSeparator) { this.pathSeparator = (pathSeparator != null ? pathSeparator : DEFAULT_PATH_SEPARATOR); } + /** Whether to trim tokenized paths and patterns. */ + public void setTrimTokens(boolean trimTokens) { + this.trimTokens = trimTokens; + } public boolean isPattern(String path) { return (path.indexOf('*') != -1 || path.indexOf('?') != -1); @@ -95,8 +101,8 @@ public class AntPathMatcher implements PathMatcher { return false; } - String[] pattDirs = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator); - String[] pathDirs = StringUtils.tokenizeToStringArray(path, this.pathSeparator); + String[] pattDirs = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator, this.trimTokens, true); + String[] pathDirs = StringUtils.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true); int pattIdxStart = 0; int pattIdxEnd = pattDirs.length - 1; @@ -246,8 +252,8 @@ public class AntPathMatcher implements PathMatcher { * does not enforce this. */ public String extractPathWithinPattern(String pattern, String path) { - String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator); - String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator); + String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator, this.trimTokens, true); + String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true); StringBuilder builder = new StringBuilder(); diff --git a/spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java b/spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java index e960a62ea65..0a34e1c470c 100644 --- a/spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java +++ b/spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2013 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. @@ -542,6 +542,14 @@ public class AntPathMatcherTests { paths.clear(); } + // SPR-8687 + @Test + public void trimTokensOff() { + pathMatcher.setTrimTokens(false); + + assertTrue(pathMatcher.match("/group/{groupName}/members", "/group/sales/members")); + assertTrue(pathMatcher.match("/group/{groupName}/members", "/group/ sales/members")); + } } From 2f6e45ff0615ec98114025eb3b78db8ab963d029 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 9 Jan 2013 05:44:23 -0500 Subject: [PATCH 018/143] Add protected method to AbstractHandlerMethodMapping --- .../handler/AbstractHandlerMethodMapping.java | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java index 28fbe1efbc7..c8f11c8175f 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java @@ -165,25 +165,17 @@ public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMap * under the same mapping */ protected void registerHandlerMethod(Object handler, Method method, T mapping) { - HandlerMethod handlerMethod; - if (handler instanceof String) { - String beanName = (String) handler; - handlerMethod = new HandlerMethod(beanName, getApplicationContext(), method); - } - else { - handlerMethod = new HandlerMethod(handler, method); - } - + HandlerMethod newHandlerMethod = createHandlerMethod(handler, method); HandlerMethod oldHandlerMethod = handlerMethods.get(mapping); - if (oldHandlerMethod != null && !oldHandlerMethod.equals(handlerMethod)) { - throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + handlerMethod.getBean() - + "' bean method \n" + handlerMethod + "\nto " + mapping + ": There is already '" + if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) { + throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() + + "' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '" + oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped."); } - this.handlerMethods.put(mapping, handlerMethod); + this.handlerMethods.put(mapping, newHandlerMethod); if (logger.isInfoEnabled()) { - logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod); + logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod); } Set patterns = getMappingPathPatterns(mapping); @@ -194,6 +186,24 @@ public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMap } } + /** + * Create the HandlerMethod instance. + * @param handler either a bean name or an actual handler instance + * @param method the target method + * @return the created HandlerMethod + */ + protected HandlerMethod createHandlerMethod(Object handler, Method method) { + HandlerMethod handlerMethod; + if (handler instanceof String) { + String beanName = (String) handler; + handlerMethod = new HandlerMethod(beanName, getApplicationContext(), method); + } + else { + handlerMethod = new HandlerMethod(handler, method); + } + return handlerMethod; + } + /** * Extract and return the URL paths contained in a mapping. */ From 4ae9cf7cf11af1626cb5dd127c020c98507f8e7f Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Thu, 10 Jan 2013 16:42:44 +0100 Subject: [PATCH 019/143] Ensure JUnit & TestNG tests run in spring-test Prior to this commit, the Gradle build configuration only executed TestNG-based tests and effectively disabled all JUnit-based tests in the spring-test module. Furthermore, TestNG-based tests were not properly reported in Bamboo CI builds. This commit ensures that both JUnit and TestNG tests are executed in the Gradle build by defining a new testNG task within the spring-test configuration. The test task now depends on the new testNG task. Furthermore, the testNG task makes use of Gradle 1.3's support for generating test reports for TestNG tests alongside reports for JUnit tests. The net effect is that all tests are executed and reportedly properly in Bamboo builds on the CI server. - Enabled both JUnit and TestNG tests for the spring-test module. - Corrected bugs in FailingBeforeAndAfterMethodsTests introduced in commit 3d1b3868fe3b38f5552883f54111c53f6ee75572. - Deleted the now obsolete SPR-9398.txt file. Issue: SPR-9398 --- build.gradle | 16 ++++++++++++++-- .../FailingBeforeAndAfterMethodsTests.java | 8 +++----- .../test/context/testng/SPR-9398.txt | 3 --- 3 files changed, 17 insertions(+), 10 deletions(-) delete mode 100644 spring-test/src/test/java/org/springframework/test/context/testng/SPR-9398.txt diff --git a/build.gradle b/build.gradle index 724927507fb..3ddef1b2398 100644 --- a/build.gradle +++ b/build.gradle @@ -640,10 +640,22 @@ project("spring-webmvc-portlet") { project("spring-test") { description = "Spring TestContext Framework" - test { - useJUnit() + + task testNG(type: Test) { useTestNG() + // "TestCase" classes are run by other test classes, not the build. + exclude "**/*TestCase.class" + // Generate TestNG reports alongside JUnit reports. + testReport true } + + test { + dependsOn testNG + useJUnit() + // "TestCase" classes are run by other test classes, not the build. + exclude "**/*TestCase.class" + } + dependencies { compile(project(":spring-core")) optional(project(":spring-beans")) diff --git a/spring-test/src/test/java/org/springframework/test/context/testng/FailingBeforeAndAfterMethodsTests.java b/spring-test/src/test/java/org/springframework/test/context/testng/FailingBeforeAndAfterMethodsTests.java index 05ba435f9b3..74e8f2513ad 100644 --- a/spring-test/src/test/java/org/springframework/test/context/testng/FailingBeforeAndAfterMethodsTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/testng/FailingBeforeAndAfterMethodsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -237,8 +237,7 @@ public class FailingBeforeAndAfterMethodsTests { @BeforeTransaction public void beforeTransaction() { - // See SPR-8116 - //org.testng.Assert.fail("always failing beforeTransaction()"); + org.testng.Assert.fail("always failing beforeTransaction()"); } } @@ -251,8 +250,7 @@ public class FailingBeforeAndAfterMethodsTests { @AfterTransaction public void afterTransaction() { - // See SPR-8116 - //org.testng.Assert.fail("always failing afterTransaction()"); + org.testng.Assert.fail("always failing afterTransaction()"); } } diff --git a/spring-test/src/test/java/org/springframework/test/context/testng/SPR-9398.txt b/spring-test/src/test/java/org/springframework/test/context/testng/SPR-9398.txt deleted file mode 100644 index dfbdc9ad831..00000000000 --- a/spring-test/src/test/java/org/springframework/test/context/testng/SPR-9398.txt +++ /dev/null @@ -1,3 +0,0 @@ -TODO [SPR-9398] re-enable TestNG support for spring-test. - -These TestNG test classes are currently not run at all. From 10dceb182e86fb79f6ad36a92d5f28088a319379 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 21 Dec 2012 21:33:11 +0100 Subject: [PATCH 020/143] Marked LocalCacheProviderProxy as deprecated (following Hibernate 3.3.'s CacheProvider deprecation) --- .../orm/hibernate3/LocalCacheProviderProxy.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spring-orm/src/main/java/org/springframework/orm/hibernate3/LocalCacheProviderProxy.java b/spring-orm/src/main/java/org/springframework/orm/hibernate3/LocalCacheProviderProxy.java index dcf15059d3a..1c7353697ad 100644 --- a/spring-orm/src/main/java/org/springframework/orm/hibernate3/LocalCacheProviderProxy.java +++ b/spring-orm/src/main/java/org/springframework/orm/hibernate3/LocalCacheProviderProxy.java @@ -30,7 +30,10 @@ import org.hibernate.cache.CacheProvider; * @author Juergen Hoeller * @since 2.5.1 * @see LocalSessionFactoryBean#setCacheProvider + * @deprecated as of Spring 3.0, following Hibernate 3.3's deprecation + * of the CacheProvider SPI */ +@Deprecated public class LocalCacheProviderProxy implements CacheProvider { private final CacheProvider cacheProvider; From 3a9ca4a6bb8289b8495f22a9e60dc7799c93493b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 3 Jan 2013 16:31:00 +0100 Subject: [PATCH 021/143] MessageSourceResourceBundle overrides JDK 1.6 containsKey method, avoiding NPE in getKeys Issue: SPR-10136 --- .../support/MessageSourceResourceBundle.java | 29 +++++++++++++++---- .../ResourceBundleMessageSourceTests.java | 13 ++++++++- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/support/MessageSourceResourceBundle.java b/spring-context/src/main/java/org/springframework/context/support/MessageSourceResourceBundle.java index 8dc2c1ffb3e..605aa72e871 100644 --- a/spring-context/src/main/java/org/springframework/context/support/MessageSourceResourceBundle.java +++ b/spring-context/src/main/java/org/springframework/context/support/MessageSourceResourceBundle.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,9 +70,9 @@ public class MessageSourceResourceBundle extends ResourceBundle { * Returns {@code null} if the message could not be resolved. */ @Override - protected Object handleGetObject(String code) { + protected Object handleGetObject(String key) { try { - return this.messageSource.getMessage(code, null, this.locale); + return this.messageSource.getMessage(key, null, this.locale); } catch (NoSuchMessageException ex) { return null; @@ -80,12 +80,29 @@ public class MessageSourceResourceBundle extends ResourceBundle { } /** - * This implementation returns {@code null}, as a MessageSource does - * not allow for enumerating the defined message codes. + * This implementation checks whether the target MessageSource can resolve + * a message for the given key, translating {@code NoSuchMessageException} + * accordingly. In contrast to ResourceBundle's default implementation in + * JDK 1.6, this does not rely on the capability to enumerate message keys. + */ + @Override + public boolean containsKey(String key) { + try { + this.messageSource.getMessage(key, null, this.locale); + return true; + } + catch (NoSuchMessageException ex) { + return false; + } + } + + /** + * This implementation throws {@code UnsupportedOperationException}, + * as a MessageSource does not allow for enumerating the defined message codes. */ @Override public Enumeration getKeys() { - return null; + throw new UnsupportedOperationException("MessageSourceResourceBundle does not support enumerating its keys"); } /** diff --git a/spring-context/src/test/java/org/springframework/context/support/ResourceBundleMessageSourceTests.java b/spring-context/src/test/java/org/springframework/context/support/ResourceBundleMessageSourceTests.java index 7e45ce98985..c0121a4a2dd 100644 --- a/spring-context/src/test/java/org/springframework/context/support/ResourceBundleMessageSourceTests.java +++ b/spring-context/src/test/java/org/springframework/context/support/ResourceBundleMessageSourceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -340,6 +340,17 @@ public class ResourceBundleMessageSourceTests extends TestCase { assertEquals(0, filenames.size()); } + public void testMessageSourceResourceBundle() { + ResourceBundleMessageSource ms = new ResourceBundleMessageSource(); + ms.setBasename("org/springframework/context/support/messages"); + MessageSourceResourceBundle rbe = new MessageSourceResourceBundle(ms, Locale.ENGLISH); + assertEquals("message1", rbe.getString("code1")); + assertTrue(rbe.containsKey("code1")); + MessageSourceResourceBundle rbg = new MessageSourceResourceBundle(ms, Locale.GERMAN); + assertEquals("nachricht2", rbg.getString("code2")); + assertTrue(rbg.containsKey("code2")); + } + @Override protected void tearDown() throws Exception { if (JdkVersion.getMajorJavaVersion() >= JdkVersion.JAVA_16) { From 97ae403b53b5ca2402c5d651dfdc6295d6372735 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 4 Jan 2013 12:25:24 +0100 Subject: [PATCH 022/143] AbstractAdvisingBeanPostProcessor caches per bean target class, working for null bean names as well Issue: SPR-10144 --- .../AbstractAdvisingBeanPostProcessor.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/AbstractAdvisingBeanPostProcessor.java b/spring-aop/src/main/java/org/springframework/aop/framework/AbstractAdvisingBeanPostProcessor.java index 9e6baee9327..5d76f6c8a5e 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/AbstractAdvisingBeanPostProcessor.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/AbstractAdvisingBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -47,7 +47,7 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyConfig */ private int order = Ordered.LOWEST_PRECEDENCE; - private final Map eligibleBeans = new ConcurrentHashMap(64); + private final Map eligibleBeans = new ConcurrentHashMap(64); public void setBeanClassLoader(ClassLoader beanClassLoader) { @@ -94,19 +94,21 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyConfig /** * Check whether the given bean is eligible for advising with this * post-processor's {@link Advisor}. - *

Implements caching of {@code canApply} results per bean name. + *

Implements caching of {@code canApply} results per bean target class. + * Can be overridden e.g. to specifically exclude certain beans by name. * @param bean the bean instance * @param beanName the name of the bean + * @see AopUtils#getTargetClass(Object) * @see AopUtils#canApply(Advisor, Class) */ protected boolean isEligible(Object bean, String beanName) { - Boolean eligible = this.eligibleBeans.get(beanName); + Class targetClass = AopUtils.getTargetClass(bean); + Boolean eligible = this.eligibleBeans.get(targetClass); if (eligible != null) { return eligible; } - Class targetClass = AopUtils.getTargetClass(bean); eligible = AopUtils.canApply(this.advisor, targetClass); - this.eligibleBeans.put(beanName, eligible); + this.eligibleBeans.put(targetClass, eligible); return eligible; } From f8a7cf9f515f0d77fa515cec6af22ad1c8f2a58b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 9 Jan 2013 18:10:25 +0100 Subject: [PATCH 023/143] MimeMessageHelper encodes attachment filename if not ASCII compliant Issue: SPR-9258 --- .../mail/javamail/MimeMessageHelper.java | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/spring-context-support/src/main/java/org/springframework/mail/javamail/MimeMessageHelper.java b/spring-context-support/src/main/java/org/springframework/mail/javamail/MimeMessageHelper.java index 47a115e9805..4184b2f3b41 100644 --- a/spring-context-support/src/main/java/org/springframework/mail/javamail/MimeMessageHelper.java +++ b/spring-context-support/src/main/java/org/springframework/mail/javamail/MimeMessageHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -35,6 +35,7 @@ import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; import javax.mail.internet.MimePart; +import javax.mail.internet.MimeUtility; import org.springframework.core.io.InputStreamSource; import org.springframework.core.io.Resource; @@ -960,7 +961,7 @@ public class MimeMessageHelper { * @see #addInline(String, javax.activation.DataSource) */ public void addInline(String contentId, InputStreamSource inputStreamSource, String contentType) - throws MessagingException { + throws MessagingException { Assert.notNull(inputStreamSource, "InputStreamSource must not be null"); if (inputStreamSource instanceof Resource && ((Resource) inputStreamSource).isOpen()) { @@ -989,11 +990,16 @@ public class MimeMessageHelper { public void addAttachment(String attachmentFilename, DataSource dataSource) throws MessagingException { Assert.notNull(attachmentFilename, "Attachment filename must not be null"); Assert.notNull(dataSource, "DataSource must not be null"); - MimeBodyPart mimeBodyPart = new MimeBodyPart(); - mimeBodyPart.setDisposition(MimeBodyPart.ATTACHMENT); - mimeBodyPart.setFileName(attachmentFilename); - mimeBodyPart.setDataHandler(new DataHandler(dataSource)); - getRootMimeMultipart().addBodyPart(mimeBodyPart); + try { + MimeBodyPart mimeBodyPart = new MimeBodyPart(); + mimeBodyPart.setDisposition(MimeBodyPart.ATTACHMENT); + mimeBodyPart.setFileName(MimeUtility.encodeText(attachmentFilename)); + mimeBodyPart.setDataHandler(new DataHandler(dataSource)); + getRootMimeMultipart().addBodyPart(mimeBodyPart); + } + catch (UnsupportedEncodingException ex) { + throw new MessagingException("Failed to encode attachment filename", ex); + } } /** @@ -1035,7 +1041,7 @@ public class MimeMessageHelper { * @see org.springframework.core.io.Resource */ public void addAttachment(String attachmentFilename, InputStreamSource inputStreamSource) - throws MessagingException { + throws MessagingException { String contentType = getFileTypeMap().getContentType(attachmentFilename); addAttachment(attachmentFilename, inputStreamSource, contentType); @@ -1059,7 +1065,7 @@ public class MimeMessageHelper { */ public void addAttachment( String attachmentFilename, InputStreamSource inputStreamSource, String contentType) - throws MessagingException { + throws MessagingException { Assert.notNull(inputStreamSource, "InputStreamSource must not be null"); if (inputStreamSource instanceof Resource && ((Resource) inputStreamSource).isOpen()) { From 5e8e901aba9abc55a9f3dedd0b5d5a0d8841357f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 9 Jan 2013 18:24:41 +0100 Subject: [PATCH 024/143] FreeMarkerConfigurationFactory properly supports TemplateLoaders when recreating Configurations Issue: SPR-9389 --- .../FreeMarkerConfigurationFactory.java | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/spring-context-support/src/main/java/org/springframework/ui/freemarker/FreeMarkerConfigurationFactory.java b/spring-context-support/src/main/java/org/springframework/ui/freemarker/FreeMarkerConfigurationFactory.java index 574c7695e7f..027f0f4a6bc 100644 --- a/spring-context-support/src/main/java/org/springframework/ui/freemarker/FreeMarkerConfigurationFactory.java +++ b/spring-context-support/src/main/java/org/springframework/ui/freemarker/FreeMarkerConfigurationFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,7 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; @@ -147,7 +148,7 @@ public class FreeMarkerConfigurationFactory { * @see #setPostTemplateLoaders */ @Deprecated - public void setTemplateLoaders(TemplateLoader[] templateLoaders) { + public void setTemplateLoaders(TemplateLoader... templateLoaders) { if (templateLoaders != null) { this.templateLoaders.addAll(Arrays.asList(templateLoaders)); } @@ -164,7 +165,7 @@ public class FreeMarkerConfigurationFactory { * @see #setTemplateLoaderPaths * @see #postProcessTemplateLoaders */ - public void setPreTemplateLoaders(TemplateLoader[] preTemplateLoaders) { + public void setPreTemplateLoaders(TemplateLoader... preTemplateLoaders) { this.preTemplateLoaders = Arrays.asList(preTemplateLoaders); } @@ -179,7 +180,7 @@ public class FreeMarkerConfigurationFactory { * @see #setTemplateLoaderPaths * @see #postProcessTemplateLoaders */ - public void setPostTemplateLoaders(TemplateLoader[] postTemplateLoaders) { + public void setPostTemplateLoaders(TemplateLoader... postTemplateLoaders) { this.postTemplateLoaders = Arrays.asList(postTemplateLoaders); } @@ -211,7 +212,7 @@ public class FreeMarkerConfigurationFactory { * @see SpringTemplateLoader * @see #setTemplateLoaders */ - public void setTemplateLoaderPaths(String[] templateLoaderPaths) { + public void setTemplateLoaderPaths(String... templateLoaderPaths) { this.templateLoaderPaths = templateLoaderPaths; } @@ -229,7 +230,7 @@ public class FreeMarkerConfigurationFactory { * Return the Spring ResourceLoader to use for loading FreeMarker template files. */ protected ResourceLoader getResourceLoader() { - return resourceLoader; + return this.resourceLoader; } /** @@ -252,7 +253,7 @@ public class FreeMarkerConfigurationFactory { * Return whether to prefer file system access for template loading. */ protected boolean isPreferFileSystemAccess() { - return preferFileSystemAccess; + return this.preferFileSystemAccess; } @@ -293,25 +294,27 @@ public class FreeMarkerConfigurationFactory { config.setDefaultEncoding(this.defaultEncoding); } + List templateLoaders = new LinkedList(this.templateLoaders); + // Register template loaders that are supposed to kick in early. if (this.preTemplateLoaders != null) { - this.templateLoaders.addAll(this.preTemplateLoaders); + templateLoaders.addAll(this.preTemplateLoaders); } // Register default template loaders. if (this.templateLoaderPaths != null) { for (String path : this.templateLoaderPaths) { - this.templateLoaders.add(getTemplateLoaderForPath(path)); + templateLoaders.add(getTemplateLoaderForPath(path)); } } - postProcessTemplateLoaders(this.templateLoaders); + postProcessTemplateLoaders(templateLoaders); // Register template loaders that are supposed to kick in late. if (this.postTemplateLoaders != null) { - this.templateLoaders.addAll(this.postTemplateLoaders); + templateLoaders.addAll(this.postTemplateLoaders); } - TemplateLoader loader = getAggregateTemplateLoader(this.templateLoaders); + TemplateLoader loader = getAggregateTemplateLoader(templateLoaders); if (loader != null) { config.setTemplateLoader(loader); } From f6d7518013d97ed9f1edfa4c6b65065eafac445b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 9 Jan 2013 18:42:10 +0100 Subject: [PATCH 025/143] SpringContextResourceAdapter implements equals/hashCode according to the JCA 1.5 contract Issue: SPR-9162 --- .../context/SpringContextResourceAdapter.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/spring-tx/src/main/java/org/springframework/jca/context/SpringContextResourceAdapter.java b/spring-tx/src/main/java/org/springframework/jca/context/SpringContextResourceAdapter.java index 25bac956685..41bf5447278 100644 --- a/spring-tx/src/main/java/org/springframework/jca/context/SpringContextResourceAdapter.java +++ b/spring-tx/src/main/java/org/springframework/jca/context/SpringContextResourceAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -33,6 +33,7 @@ import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.StandardEnvironment; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** @@ -228,4 +229,17 @@ public class SpringContextResourceAdapter implements ResourceAdapter { return null; } + + @Override + public boolean equals(Object obj) { + return (obj instanceof SpringContextResourceAdapter && + ObjectUtils.nullSafeEquals(getContextConfigLocation(), + ((SpringContextResourceAdapter) obj).getContextConfigLocation())); + } + + @Override + public int hashCode() { + return ObjectUtils.nullSafeHashCode(getContextConfigLocation()); + } + } From dae4485155096c1b9db1389fc90058c48d8e7906 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 10 Jan 2013 16:47:27 +0100 Subject: [PATCH 026/143] Added dedicated sort method to AnnotationAwareOrderComparator Issue: SPR-9625 --- .../AnnotationAwareOrderComparator.java | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAwareOrderComparator.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAwareOrderComparator.java index a24f2cde3ac..bf7232e10a2 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAwareOrderComparator.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAwareOrderComparator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,10 @@ package org.springframework.core.annotation; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + import org.springframework.core.OrderComparator; import org.springframework.core.Ordered; @@ -37,7 +41,7 @@ public class AnnotationAwareOrderComparator extends OrderComparator { /** * Shared default instance of AnnotationAwareOrderComparator. */ - public static AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator(); + public static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator(); @Override @@ -54,4 +58,31 @@ public class AnnotationAwareOrderComparator extends OrderComparator { return Ordered.LOWEST_PRECEDENCE; } + + /** + * Sort the given List with a default AnnotationAwareOrderComparator. + *

Optimized to skip sorting for lists with size 0 or 1, + * in order to avoid unnecessary array extraction. + * @param list the List to sort + * @see java.util.Collections#sort(java.util.List, java.util.Comparator) + */ + public static void sort(List list) { + if (list.size() > 1) { + Collections.sort(list, INSTANCE); + } + } + + /** + * Sort the given array with a default AnnotationAwareOrderComparator. + *

Optimized to skip sorting for lists with size 0 or 1, + * in order to avoid unnecessary array extraction. + * @param array the array to sort + * @see java.util.Arrays#sort(Object[], java.util.Comparator) + */ + public static void sort(Object[] array) { + if (array.length > 1) { + Arrays.sort(array, INSTANCE); + } + } + } From e806c4eb3d5bd660c169e2b215a4e29b1f43bf35 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 10 Jan 2013 16:55:32 +0100 Subject: [PATCH 027/143] AnnotationAwareOrderComparator is able to sort Class objects as well Issue: SPR-10152 --- .../AnnotationAwareOrderComparator.java | 3 +- .../AnnotationAwareOrderComparatorTests.java | 43 ++++++++++++++++--- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAwareOrderComparator.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAwareOrderComparator.java index bf7232e10a2..c9d1fe47c55 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAwareOrderComparator.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAwareOrderComparator.java @@ -50,7 +50,8 @@ public class AnnotationAwareOrderComparator extends OrderComparator { return ((Ordered) obj).getOrder(); } if (obj != null) { - Order order = obj.getClass().getAnnotation(Order.class); + Class clazz = (obj instanceof Class ? (Class) obj : obj.getClass()); + Order order = clazz.getAnnotation(Order.class); if (order != null) { return order.value(); } diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAwareOrderComparatorTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAwareOrderComparatorTests.java index 9bdd19f54b9..47634bb7604 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAwareOrderComparatorTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAwareOrderComparatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -13,16 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.core.annotation; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; -import org.junit.Test; - /** - * Unit tests for {@link AnnotationAwareOrderComparator}. - * + * @author Juergen Hoeller * @author Oliver Gierke */ public class AnnotationAwareOrderComparatorTests { @@ -31,4 +34,34 @@ public class AnnotationAwareOrderComparatorTests { public void instanceVariableIsAnAnnotationAwareOrderComparator() { assertThat(AnnotationAwareOrderComparator.INSTANCE, is(instanceOf(AnnotationAwareOrderComparator.class))); } + + @Test + public void sortInstances() { + List list = new ArrayList<>(); + list.add(new B()); + list.add(new A()); + AnnotationAwareOrderComparator.sort(list); + assertTrue(list.get(0) instanceof A); + assertTrue(list.get(1) instanceof B); + } + + @Test + public void sortClasses() { + List list = new ArrayList<>(); + list.add(B.class); + list.add(A.class); + AnnotationAwareOrderComparator.sort(list); + assertEquals(A.class, list.get(0)); + assertEquals(B.class, list.get(1)); + } + + + @Order(1) + private static class A { + } + + @Order(2) + private static class B { + } + } From ea823fd398d0a3030c477399a33074fc6c6acae6 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 10 Jan 2013 17:01:18 +0100 Subject: [PATCH 028/143] AnnotationAwareOrderComparator etc --- src/dist/changelog.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/dist/changelog.txt b/src/dist/changelog.txt index b9726400ae4..f81937f9b5b 100644 --- a/src/dist/changelog.txt +++ b/src/dist/changelog.txt @@ -6,11 +6,18 @@ http://www.springsource.org Changes in version 3.2.1 (2013-01-24) -------------------------------------- +* AnnotationAwareOrderComparator is able to sort Class objects as well (SPR-10152) +* added dedicated sort method to AnnotationAwareOrderComparator (SPR-9625) * fixed QualifierAnnotationAutowireCandidateResolver's detection of custom qualifier annotations (SPR-10107) * fixed AbstractAutoProxyCreator to accept null bean names again (SPR-10108) +* AbstractAdvisingBeanPostProcessor caches per bean target class, working for null bean names as well (SPR-10144) +* MessageSourceResourceBundle overrides JDK 1.6 containsKey method, avoiding NPE in getKeys (SPR-10136) * spring-task-3.2.xsd allows for SpEL expressions in initial-delay attribute (SPR-10102) * JmsTemplate uses configured receiveTimeout if shorter than remaining transaction timeout (SPR-10109) * added MappingJackson2MessageConverter for JMS (SPR-10099) +* MimeMessageHelper encodes attachment filename if not ASCII compliant (SPR-9258) +* FreeMarkerConfigurationFactory properly supports TemplateLoaders when recreating Configurations (SPR-9389) +* SpringContextResourceAdapter implements equals/hashCode according to the JCA 1.5 contract (SPR-9162) Changes in version 3.2 GA (2012-12-13) From 2427391286f489e11981e1072aaebbf78dd5d3f7 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Thu, 10 Jan 2013 17:33:59 +0100 Subject: [PATCH 029/143] Polish Javadoc in CookieValue and RequestHeader --- .../web/bind/annotation/CookieValue.java | 8 ++++---- .../web/bind/annotation/RequestHeader.java | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/bind/annotation/CookieValue.java b/spring-web/src/main/java/org/springframework/web/bind/annotation/CookieValue.java index 7f469a43185..6af1c621dea 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/annotation/CookieValue.java +++ b/spring-web/src/main/java/org/springframework/web/bind/annotation/CookieValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -55,14 +55,14 @@ public @interface CookieValue { * in case the header is missing in the request. Switch this to * {@code false} if you prefer a {@code null} in case of the * missing header. - *

Alternatively, provide a {@link #defaultValue() defaultValue}, - * which implicitly sets this flag to {@code false}. + *

Alternatively, provide a {@link #defaultValue}, which implicitly sets + * this flag to {@code false}. */ boolean required() default true; /** * The default value to use as a fallback. Supplying a default value implicitly - * sets {@link #required()} to false. + * sets {@link #required} to {@code false}. */ String defaultValue() default ValueConstants.DEFAULT_NONE; diff --git a/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestHeader.java b/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestHeader.java index 9d094d75d32..47d2a567277 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestHeader.java +++ b/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestHeader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,14 +49,14 @@ public @interface RequestHeader { *

Default is {@code true}, leading to an exception thrown in case * of the header missing in the request. Switch this to {@code false} * if you prefer a {@code null} in case of the header missing. - *

Alternatively, provide a {@link #defaultValue() defaultValue}, - * which implicitely sets this flag to {@code false}. + *

Alternatively, provide a {@link #defaultValue}, which implicitly sets + * this flag to {@code false}. */ boolean required() default true; /** - * The default value to use as a fallback. Supplying a default value implicitely - * sets {@link #required()} to false. + * The default value to use as a fallback. Supplying a default value implicitly + * sets {@link #required} to {@code false}. */ String defaultValue() default ValueConstants.DEFAULT_NONE; From 91da1383143bc15f945935960a79bc253bd1ce16 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Thu, 10 Jan 2013 15:17:37 -0600 Subject: [PATCH 030/143] Use explicit JDK versions in aspects.gradle Previously aspects.gradle used the Gradle conventions for the source and target compatibility. This means that unless the conventions were updated the current JDK would be used for both source and target compatibilty. Since an update to build.gradle changed to configure the compileJava and compileTestJava tasks explicitly spring-aspects has been compiled with JDK 7 compatibility. This commit explicitly uses the source and target compatibility from spring-core to ensure that aspects.gradle is kept up to date. Issue: SPR-10161 --- spring-aspects/aspects.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spring-aspects/aspects.gradle b/spring-aspects/aspects.gradle index 0432414467b..c99fa031837 100644 --- a/spring-aspects/aspects.gradle +++ b/spring-aspects/aspects.gradle @@ -21,6 +21,9 @@ task compileJava(overwrite: true) { inputs.files(project.sourceSets.main.allSource + project.sourceSets.main.compileClasspath) outputs.dir outputDir + ext.sourceCompatibility = project(":spring-core").compileJava.sourceCompatibility + ext.targetCompatibility = project(":spring-core").compileJava.targetCompatibility + doLast{ ant.taskdef(resource: "org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties", classpath: configurations.ajc.asPath) @@ -51,6 +54,9 @@ task compileTestJava(overwrite: true) { inputs.files(project.sourceSets.test.allSource + project.sourceSets.test.compileClasspath) outputs.dir outputDir + ext.sourceCompatibility = project(":spring-core").compileTestJava.sourceCompatibility + ext.targetCompatibility = project(":spring-core").compileTestJava.targetCompatibility + doLast{ ant.taskdef(resource: "org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties", classpath: configurations.ajc.asPath) From 18bf860c273179f95719d80b14d17c0c9ba99c38 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Fri, 11 Jan 2013 00:31:37 +0100 Subject: [PATCH 031/143] Update Javadoc for MockMvc Deleted reference to the obsolete configureWarRootDir() method from the stand-alone spring-test-mvc project. --- .../java/org/springframework/test/web/servlet/MockMvc.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/MockMvc.java b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/MockMvc.java index a074ff6d147..b18fc3554fa 100644 --- a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/MockMvc.java +++ b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/MockMvc.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -38,7 +38,7 @@ import org.springframework.util.Assert; * * WebApplicationContext wac = ...; * - * MockMvc mockMvc = webAppContextSetup(wac).configureWarRootDir("src/main/webapp", false).build() + * MockMvc mockMvc = webAppContextSetup(wac).build(); * * mockMvc.perform(get("/form")) * .andExpect(status().isOk()) From a0e5394203d3f485c592c2d05d7d1a6b3cff6ced Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 10 Jan 2013 19:01:40 -0800 Subject: [PATCH 032/143] Exclude stax test dependency Exclude transitive stax 1.0 dependency to prevent compile time eclipse errors. --- build.gradle | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 3ddef1b2398..82a22f4922c 100644 --- a/build.gradle +++ b/build.gradle @@ -235,7 +235,9 @@ project("spring-core") { optional("net.sf.jopt-simple:jopt-simple:3.0") optional("log4j:log4j:1.2.17") testCompile("xmlunit:xmlunit:1.3") - testCompile("org.codehaus.woodstox:wstx-asl:3.2.7") + testCompile("org.codehaus.woodstox:wstx-asl:3.2.7") { + exclude group: "stax", module: "stax-api" + } } jar { From c425d22151bf4a84459be1703b54143b88e4f547 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 10 Jan 2013 19:02:56 -0800 Subject: [PATCH 033/143] Exclude eclipse WTP generated artifacts --- .gitignore | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index c9e0aca55ea..67d798efd45 100644 --- a/.gitignore +++ b/.gitignore @@ -13,8 +13,6 @@ jmx.log derby.log spring-test/test-output/ .gradle -.classpath -.project argfile* pom.xml @@ -22,6 +20,11 @@ pom.xml buildSrc/build /spring-*/build +# Eclipse artifacts, including WTP generated manifests +.classpath +.project +spring-*/src/main/java/META-INF/MANIFEST.MF + # IDEA artifacts and output dirs *.iml *.ipr From b8f408ed5faae1bf266331063bc2ea575962bead Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Tue, 8 Jan 2013 17:02:43 +0100 Subject: [PATCH 034/143] Replace space indentation with tabs Issue: SPR-9990 --- build.gradle | 14 +- .../gradle/SplitPackageDetectorPlugin.groovy | 156 +++++++++--------- 2 files changed, 85 insertions(+), 85 deletions(-) diff --git a/build.gradle b/build.gradle index 697cdbea53d..f67e2740c0c 100644 --- a/build.gradle +++ b/build.gradle @@ -753,18 +753,18 @@ configure(rootProject) { apply plugin: "docbook-reference" apply plugin: "groovy" apply from: "${gradleScriptDir}/jdiff.gradle" - apply plugin: org.springframework.build.gradle.SplitPackageDetectorPlugin + apply plugin: org.springframework.build.gradle.SplitPackageDetectorPlugin - reference { + reference { sourceDir = file("src/reference/docbook") pdfFilename = "spring-framework-reference.pdf" } - diagnoseSplitPackages { - projectsToScan = project.subprojects - project(":spring-instrument-tomcat") // SPR-10150 - } + diagnoseSplitPackages { + projectsToScan = project.subprojects - project(":spring-instrument-tomcat") // SPR-10150 + } - // don"t publish the default jar for the root project + // don"t publish the default jar for the root project configurations.archives.artifacts.clear() dependencies { // for integration tests @@ -788,7 +788,7 @@ configure(rootProject) { testCompile("hsqldb:hsqldb:${hsqldbVersion}") } - check.dependsOn diagnoseSplitPackages + check.dependsOn diagnoseSplitPackages task api(type: Javadoc) { group = "Documentation" diff --git a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy index 01c36fad7a2..eddfec385c4 100644 --- a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy +++ b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy @@ -31,101 +31,101 @@ import org.gradle.plugins.ide.eclipse.model.EclipseClasspath import org.gradle.plugins.ide.idea.IdeaPlugin class SplitPackageDetectorPlugin implements Plugin { - public void apply(Project project) { - Task diagnoseSplitPackages = project.tasks.add('diagnoseSplitPackages', SplitPackageDetectorTask.class) - diagnoseSplitPackages.setDescription('Detects packages which will be split across JARs') - } + public void apply(Project project) { + Task diagnoseSplitPackages = project.tasks.add('diagnoseSplitPackages', SplitPackageDetectorTask.class) + diagnoseSplitPackages.setDescription('Detects packages which will be split across JARs') + } } public class SplitPackageDetectorTask extends DefaultTask { - @Input - Set projectsToScan + @Input + Set projectsToScan - @TaskAction - public final void diagnoseSplitPackages() { - def Map mergeMap = [:] - def projects = projectsToScan.findAll { it.plugins.findPlugin(org.springframework.build.gradle.MergePlugin) }.findAll { it.merge.into } - projects.each { p -> - mergeMap.put(p, p.merge.into) - } - def splitFound = new org.springframework.build.gradle.SplitPackageDetector(projectsToScan, mergeMap, project.logger).diagnoseSplitPackages(); - assert !splitFound // see error log messages for details of split packages - } + @TaskAction + public final void diagnoseSplitPackages() { + def Map mergeMap = [:] + def projects = projectsToScan.findAll { it.plugins.findPlugin(org.springframework.build.gradle.MergePlugin) }.findAll { it.merge.into } + projects.each { p -> + mergeMap.put(p, p.merge.into) + } + def splitFound = new org.springframework.build.gradle.SplitPackageDetector(projectsToScan, mergeMap, project.logger).diagnoseSplitPackages(); + assert !splitFound // see error log messages for details of split packages + } } class SplitPackageDetector { - private static final String HIDDEN_DIRECTORY_PREFIX = "." + private static final String HIDDEN_DIRECTORY_PREFIX = "." - private static final String JAVA_FILE_SUFFIX = ".java" + private static final String JAVA_FILE_SUFFIX = ".java" - private static final String SRC_MAIN_JAVA = "src" + File.separator + "main" + File.separator + "java" + private static final String SRC_MAIN_JAVA = "src" + File.separator + "main" + File.separator + "java" - private static final String PACKAGE_SEPARATOR = "." + private static final String PACKAGE_SEPARATOR = "." - private final Map mergeMap + private final Map mergeMap - private final Map> pkgMap = [:] + private final Map> pkgMap = [:] - private final logger + private final logger - SplitPackageDetector(projectsToScan, mergeMap, logger) { - this.mergeMap = mergeMap - this.logger = logger - projectsToScan.each { Project p -> - def dir = p.projectDir - def packages = getPackagesInDirectory(dir) - if (!packages.isEmpty()) { - pkgMap.put(p, packages) - } - } - } + SplitPackageDetector(projectsToScan, mergeMap, logger) { + this.mergeMap = mergeMap + this.logger = logger + projectsToScan.each { Project p -> + def dir = p.projectDir + def packages = getPackagesInDirectory(dir) + if (!packages.isEmpty()) { + pkgMap.put(p, packages) + } + } + } - private File[] dirList(String dir) { - dirList(new File(dir)) - } + private File[] dirList(String dir) { + dirList(new File(dir)) + } - private File[] dirList(File dir) { - dir.listFiles({ file -> file.isDirectory() && !file.getName().startsWith(HIDDEN_DIRECTORY_PREFIX) } as FileFilter) - } + private File[] dirList(File dir) { + dir.listFiles({ file -> file.isDirectory() && !file.getName().startsWith(HIDDEN_DIRECTORY_PREFIX) } as FileFilter) + } - private Set getPackagesInDirectory(File dir) { - def pkgs = new HashSet() - addPackagesInDirectory(pkgs, new File(dir, SRC_MAIN_JAVA), "") - return pkgs; - } + private Set getPackagesInDirectory(File dir) { + def pkgs = new HashSet() + addPackagesInDirectory(pkgs, new File(dir, SRC_MAIN_JAVA), "") + return pkgs; + } - boolean diagnoseSplitPackages() { - def splitFound = false; - def projs = pkgMap.keySet().toArray() - def numProjects = projs.length - for (int i = 0; i < numProjects - 1; i++) { - for (int j = i + 1; j < numProjects - 1; j++) { - def pi = projs[i] - def pkgi = new HashSet(pkgMap.get(pi)) - def pj = projs[j] - def pkgj = pkgMap.get(pj) - pkgi.retainAll(pkgj) - if (!pkgi.isEmpty() && mergeMap.get(pi) != pj && mergeMap.get(pj) != pi) { - pkgi.each { pkg -> - def readablePkg = pkg.substring(1).replaceAll(File.separator, PACKAGE_SEPARATOR) - logger.error("Package '$readablePkg' is split between $pi and $pj") - } - splitFound = true - } - } - } - return splitFound - } + boolean diagnoseSplitPackages() { + def splitFound = false; + def projs = pkgMap.keySet().toArray() + def numProjects = projs.length + for (int i = 0; i < numProjects - 1; i++) { + for (int j = i + 1; j < numProjects - 1; j++) { + def pi = projs[i] + def pkgi = new HashSet(pkgMap.get(pi)) + def pj = projs[j] + def pkgj = pkgMap.get(pj) + pkgi.retainAll(pkgj) + if (!pkgi.isEmpty() && mergeMap.get(pi) != pj && mergeMap.get(pj) != pi) { + pkgi.each { pkg -> + def readablePkg = pkg.substring(1).replaceAll(File.separator, PACKAGE_SEPARATOR) + logger.error("Package '$readablePkg' is split between $pi and $pj") + } + splitFound = true + } + } + } + return splitFound + } - private void addPackagesInDirectory(HashSet packages, File dir, String pkg) { - def scanDir = new File(dir, pkg) - def File[] javaFiles = scanDir.listFiles({ file -> !file.isDirectory() && file.getName().endsWith(JAVA_FILE_SUFFIX) } as FileFilter) - if (javaFiles != null && javaFiles.length != 0) { - packages.add(pkg) - } - dirList(scanDir).each { File subDir -> - addPackagesInDirectory(packages, dir, pkg + File.separator + subDir.getName()) - } - } -} \ No newline at end of file + private void addPackagesInDirectory(HashSet packages, File dir, String pkg) { + def scanDir = new File(dir, pkg) + def File[] javaFiles = scanDir.listFiles({ file -> !file.isDirectory() && file.getName().endsWith(JAVA_FILE_SUFFIX) } as FileFilter) + if (javaFiles != null && javaFiles.length != 0) { + packages.add(pkg) + } + dirList(scanDir).each { File subDir -> + addPackagesInDirectory(packages, dir, pkg + File.separator + subDir.getName()) + } + } +} From 654c07db344310d8582c433dc643b0a256c8cc7e Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Fri, 11 Jan 2013 15:54:11 +0100 Subject: [PATCH 035/143] Refactor detect-split-packages Gradle plugin - Use conventional plugin naming, i.e. "detect-split-packages" instead of applying plugin based on fully-qualified class name - Rename "diagnose" => "detect" consistently throughout plugin, task and method names and generally refactor naming throughout to follow "detect split packages" phrasing - Add Javadoc to DetectSplitPackagesPlugin - Improve error reporting when split packages are detected Upon detecting one or more split packages, `detectSplitPackages` now fails idiomatically, throwing a GradleException to signal task failure (as opposed to the previous approach of using an assert assertion), and the output reads as follows: $ gradle detectSplitPackages [...] :buildSrc:build UP-TO-DATE :detectSplitPackages FAILED FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':detectSplitPackages'. > The following split package(s) have been detected: - org.springframework.beans (split across spring-beans and spring-orm) - org.springframework.core.env (split across spring-context and spring-core) - DetectSplitPackagesTask now automatically attaches itself to `check` task lifecycle if the enclosing project contains a `check` task - DetectSplitPackagesTask adds itself to the 'Verification' task group, ensuring that it shows up correctly in `gradle tasks` task listings - packagesToScan now defaults to all subprojects; users may then customize this by removing individual subprojects from the collection Issue: SPR-9990 --- build.gradle | 8 +- .../gradle/DetectSplitPackagesPlugin.groovy | 157 ++++++++++++++++++ .../gradle/SplitPackageDetectorPlugin.groovy | 131 --------------- .../detect-split-packages.properties | 1 + 4 files changed, 161 insertions(+), 136 deletions(-) create mode 100644 buildSrc/src/main/groovy/org/springframework/build/gradle/DetectSplitPackagesPlugin.groovy delete mode 100644 buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy create mode 100644 buildSrc/src/main/resources/META-INF/gradle-plugins/detect-split-packages.properties diff --git a/build.gradle b/build.gradle index f67e2740c0c..11e9ed8becd 100644 --- a/build.gradle +++ b/build.gradle @@ -752,16 +752,16 @@ configure(rootProject) { apply plugin: "docbook-reference" apply plugin: "groovy" + apply plugin: "detect-split-packages" apply from: "${gradleScriptDir}/jdiff.gradle" - apply plugin: org.springframework.build.gradle.SplitPackageDetectorPlugin reference { sourceDir = file("src/reference/docbook") pdfFilename = "spring-framework-reference.pdf" } - diagnoseSplitPackages { - projectsToScan = project.subprojects - project(":spring-instrument-tomcat") // SPR-10150 + detectSplitPackages { + projectsToScan -= project(":spring-instrument-tomcat") } // don"t publish the default jar for the root project @@ -788,8 +788,6 @@ configure(rootProject) { testCompile("hsqldb:hsqldb:${hsqldbVersion}") } - check.dependsOn diagnoseSplitPackages - task api(type: Javadoc) { group = "Documentation" description = "Generates aggregated Javadoc API documentation." diff --git a/buildSrc/src/main/groovy/org/springframework/build/gradle/DetectSplitPackagesPlugin.groovy b/buildSrc/src/main/groovy/org/springframework/build/gradle/DetectSplitPackagesPlugin.groovy new file mode 100644 index 00000000000..8b73878694e --- /dev/null +++ b/buildSrc/src/main/groovy/org/springframework/build/gradle/DetectSplitPackagesPlugin.groovy @@ -0,0 +1,157 @@ +/* + * Copyright 2002-2013 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.build.gradle + +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.TaskAction + +/** + * Gradle plugin that detects identically named, non-empty packages split across multiple + * subprojects, e.g. "org.springframework.context.annotation" existing in both spring-core + * and spring-aspects. Adds a 'detectSplitPackages' task to the current project's task + * collection. If the project already contains a 'check' task (i.e. is a typical Gradle + * project with the "java" plugin applied), the 'check' task will be updated to depend on + * the execution of 'detectSplitPackages'. + * + * By default, all subprojects will be scanned. Use the 'projectsToScan' task property to + * modify this value. Example usage: + * + * apply plugin: 'detect-split-packages // typically applied to root project + * + * detectSplitPackages { + * packagesToScan -= project(":spring-xyz") // scan every project but spring-xyz + * } + * + * @author Rob Winch + * @author Glyn Normington + * @author Chris Beams + */ +public class DetectSplitPackagesPlugin implements Plugin { + public void apply(Project project) { + def tasks = project.tasks + Task detectSplitPackages = tasks.add('detectSplitPackages', DetectSplitPackagesTask.class) + if (tasks.asMap.containsKey('check')) { + tasks.getByName('check').dependsOn detectSplitPackages + } + } +} + +public class DetectSplitPackagesTask extends DefaultTask { + + private static final String JAVA_FILE_SUFFIX = ".java" + private static final String PACKAGE_SEPARATOR = "." + private static final String HIDDEN_DIRECTORY_PREFIX = "." + + @Input + Set projectsToScan = project.subprojects + + public DetectSplitPackagesTask() { + this.group = 'Verification' + this.description = 'Detects packages split across two or more subprojects.' + } + + @TaskAction + public void detectSplitPackages() { + def splitPackages = doDetectSplitPackages() + if (!splitPackages.isEmpty()) { + def message = "The following split package(s) have been detected:\n" + splitPackages.each { pkg, mod -> + message += " - ${pkg} (split across ${mod[0].name} and ${mod[1].name})\n" + } + throw new GradleException(message) + } + } + + private Map> doDetectSplitPackages() { + def splitPackages = [:] + def mergedProjects = findMergedProjects() + def packagesByProject = mapPackagesByProject() + + def projects = packagesByProject.keySet().toArray() + def nProjects = projects.length + + for (int i = 0; i < nProjects - 1; i++) { + for (int j = i + 1; j < nProjects - 1; j++) { + def prj_i = projects[i] + def prj_j = projects[j] + + def pkgs_i = new HashSet(packagesByProject.get(prj_i)) + def pkgs_j = packagesByProject.get(prj_j) + pkgs_i.retainAll(pkgs_j) + + if (!pkgs_i.isEmpty() + && mergedProjects.get(prj_i) != prj_j + && mergedProjects.get(prj_j) != prj_i) { + pkgs_i.each { pkg -> + def readablePkg = pkg.substring(1).replaceAll(File.separator, PACKAGE_SEPARATOR) + splitPackages[readablePkg] = [prj_i, prj_j] + } + } + } + } + return splitPackages; + } + + private Map> mapPackagesByProject() { + def packagesByProject = [:] + this.projectsToScan.each { Project p -> + def packages = new HashSet() + p.sourceSets.main.java.srcDirs.each { File dir -> + findPackages(packages, dir, "") + } + if (!packages.isEmpty()) { + packagesByProject.put(p, packages) + } + } + return packagesByProject; + } + + private Map findMergedProjects() { + def mergedProjects = [:] + this.projectsToScan.findAll { p -> + p.plugins.findPlugin(MergePlugin) + }.findAll { p -> + p.merge.into + }.each { p -> + mergedProjects.put(p, p.merge.into) + } + return mergedProjects + } + + private static void findPackages(Set packages, File dir, String packagePath) { + def scanDir = new File(dir, packagePath) + def File[] javaFiles = scanDir.listFiles({ file -> + !file.isDirectory() && file.name.endsWith(JAVA_FILE_SUFFIX) + } as FileFilter) + + if (javaFiles != null && javaFiles.length != 0) { + packages.add(packagePath) + } + + scanDir.listFiles({ File file -> + file.isDirectory() && !file.name.startsWith(HIDDEN_DIRECTORY_PREFIX) + } as FileFilter).each { File subDir -> + findPackages(packages, dir, packagePath + File.separator + subDir.name) + } + } +} + diff --git a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy b/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy deleted file mode 100644 index eddfec385c4..00000000000 --- a/buildSrc/src/main/groovy/org/springframework/build/gradle/SplitPackageDetectorPlugin.groovy +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2013 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.build.gradle - -import org.gradle.api.DefaultTask -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.Task -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.ProjectDependency -import org.gradle.api.artifacts.maven.Conf2ScopeMapping -import org.gradle.api.plugins.MavenPlugin -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.TaskAction -import org.gradle.plugins.ide.eclipse.EclipsePlugin -import org.gradle.plugins.ide.eclipse.model.EclipseClasspath -import org.gradle.plugins.ide.idea.IdeaPlugin - -class SplitPackageDetectorPlugin implements Plugin { - public void apply(Project project) { - Task diagnoseSplitPackages = project.tasks.add('diagnoseSplitPackages', SplitPackageDetectorTask.class) - diagnoseSplitPackages.setDescription('Detects packages which will be split across JARs') - } -} - -public class SplitPackageDetectorTask extends DefaultTask { - @Input - Set projectsToScan - - @TaskAction - public final void diagnoseSplitPackages() { - def Map mergeMap = [:] - def projects = projectsToScan.findAll { it.plugins.findPlugin(org.springframework.build.gradle.MergePlugin) }.findAll { it.merge.into } - projects.each { p -> - mergeMap.put(p, p.merge.into) - } - def splitFound = new org.springframework.build.gradle.SplitPackageDetector(projectsToScan, mergeMap, project.logger).diagnoseSplitPackages(); - assert !splitFound // see error log messages for details of split packages - } -} - -class SplitPackageDetector { - - private static final String HIDDEN_DIRECTORY_PREFIX = "." - - private static final String JAVA_FILE_SUFFIX = ".java" - - private static final String SRC_MAIN_JAVA = "src" + File.separator + "main" + File.separator + "java" - - private static final String PACKAGE_SEPARATOR = "." - - private final Map mergeMap - - private final Map> pkgMap = [:] - - private final logger - - SplitPackageDetector(projectsToScan, mergeMap, logger) { - this.mergeMap = mergeMap - this.logger = logger - projectsToScan.each { Project p -> - def dir = p.projectDir - def packages = getPackagesInDirectory(dir) - if (!packages.isEmpty()) { - pkgMap.put(p, packages) - } - } - } - - private File[] dirList(String dir) { - dirList(new File(dir)) - } - - private File[] dirList(File dir) { - dir.listFiles({ file -> file.isDirectory() && !file.getName().startsWith(HIDDEN_DIRECTORY_PREFIX) } as FileFilter) - } - - private Set getPackagesInDirectory(File dir) { - def pkgs = new HashSet() - addPackagesInDirectory(pkgs, new File(dir, SRC_MAIN_JAVA), "") - return pkgs; - } - - boolean diagnoseSplitPackages() { - def splitFound = false; - def projs = pkgMap.keySet().toArray() - def numProjects = projs.length - for (int i = 0; i < numProjects - 1; i++) { - for (int j = i + 1; j < numProjects - 1; j++) { - def pi = projs[i] - def pkgi = new HashSet(pkgMap.get(pi)) - def pj = projs[j] - def pkgj = pkgMap.get(pj) - pkgi.retainAll(pkgj) - if (!pkgi.isEmpty() && mergeMap.get(pi) != pj && mergeMap.get(pj) != pi) { - pkgi.each { pkg -> - def readablePkg = pkg.substring(1).replaceAll(File.separator, PACKAGE_SEPARATOR) - logger.error("Package '$readablePkg' is split between $pi and $pj") - } - splitFound = true - } - } - } - return splitFound - } - - private void addPackagesInDirectory(HashSet packages, File dir, String pkg) { - def scanDir = new File(dir, pkg) - def File[] javaFiles = scanDir.listFiles({ file -> !file.isDirectory() && file.getName().endsWith(JAVA_FILE_SUFFIX) } as FileFilter) - if (javaFiles != null && javaFiles.length != 0) { - packages.add(pkg) - } - dirList(scanDir).each { File subDir -> - addPackagesInDirectory(packages, dir, pkg + File.separator + subDir.getName()) - } - } -} diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/detect-split-packages.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/detect-split-packages.properties new file mode 100644 index 00000000000..10b1e4e981d --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/detect-split-packages.properties @@ -0,0 +1 @@ +implementation-class=org.springframework.build.gradle.DetectSplitPackagesPlugin From 74137794da867703ef8399c9b21e0ca5066f72b3 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Fri, 11 Jan 2013 16:28:48 +0100 Subject: [PATCH 036/143] Polish changes from pull request #205 - Replace space intendation with tabs - Remove leading tabs on otherwise empty lines - Remove illegal {@link ...} syntax in Javadoc @see reference Issue: SPR-10093 --- .../servlet/setup/DefaultMockMvcBuilder.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilder.java b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilder.java index 984be0c8542..be50b4aee05 100644 --- a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilder.java +++ b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilder.java @@ -54,7 +54,7 @@ public class DefaultMockMvcBuilder extends MockMvcB private final List globalResultMatchers = new ArrayList(); private final List globalResultHandlers = new ArrayList(); - + private Boolean dispatchOptions = Boolean.FALSE; @@ -180,17 +180,17 @@ public class DefaultMockMvcBuilder extends MockMvcB this.globalResultHandlers.add(resultHandler); return (T) this; } - + /** - * Should the {@link DispatcherServlet} dispatch OPTIONS request to controllers. - * @param dispatchOptions - * @see {@link DispatcherServlet#setDispatchOptionsRequest(boolean)} - */ + * Should the {@link DispatcherServlet} dispatch OPTIONS request to controllers. + * @param dispatchOptions + * @see DispatcherServlet#setDispatchOptionsRequest(boolean) + */ @SuppressWarnings("unchecked") - public final T dispatchOptions(boolean dispatchOptions) { - this.dispatchOptions = dispatchOptions; - return (T) this; - } + public final T dispatchOptions(boolean dispatchOptions) { + this.dispatchOptions = dispatchOptions; + return (T) this; + } /** * Build a {@link MockMvc} instance. From c1fe3c056a26c80ffe81d2504664be3e276033c0 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 11 Jan 2013 12:19:52 -0800 Subject: [PATCH 037/143] Polish DetectSplitPackagesPlugin Polish DetectSplitPackagesPlugin to favor double-quoted strings. --- .../build/gradle/DetectSplitPackagesPlugin.groovy | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/buildSrc/src/main/groovy/org/springframework/build/gradle/DetectSplitPackagesPlugin.groovy b/buildSrc/src/main/groovy/org/springframework/build/gradle/DetectSplitPackagesPlugin.groovy index 8b73878694e..ddafa956ef7 100644 --- a/buildSrc/src/main/groovy/org/springframework/build/gradle/DetectSplitPackagesPlugin.groovy +++ b/buildSrc/src/main/groovy/org/springframework/build/gradle/DetectSplitPackagesPlugin.groovy @@ -24,6 +24,7 @@ import org.gradle.api.Task import org.gradle.api.tasks.Input import org.gradle.api.tasks.TaskAction + /** * Gradle plugin that detects identically named, non-empty packages split across multiple * subprojects, e.g. "org.springframework.context.annotation" existing in both spring-core @@ -48,9 +49,9 @@ import org.gradle.api.tasks.TaskAction public class DetectSplitPackagesPlugin implements Plugin { public void apply(Project project) { def tasks = project.tasks - Task detectSplitPackages = tasks.add('detectSplitPackages', DetectSplitPackagesTask.class) - if (tasks.asMap.containsKey('check')) { - tasks.getByName('check').dependsOn detectSplitPackages + Task detectSplitPackages = tasks.add("detectSplitPackages", DetectSplitPackagesTask.class) + if (tasks.asMap.containsKey("check")) { + tasks.getByName("check").dependsOn detectSplitPackages } } } @@ -65,8 +66,8 @@ public class DetectSplitPackagesTask extends DefaultTask { Set projectsToScan = project.subprojects public DetectSplitPackagesTask() { - this.group = 'Verification' - this.description = 'Detects packages split across two or more subprojects.' + this.group = "Verification" + this.description = "Detects packages split across two or more subprojects." } @TaskAction From 5b147bfba8d5d5837b96357a52867e433137f64b Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Fri, 11 Jan 2013 21:31:46 +0100 Subject: [PATCH 038/143] Improve speed of spring-test build - Now excluding *TestSuite classes from the JUnit test task. - Renamed SpringJUnit4SuiteTests to SpringJUnit4TestSuite so that it is no longer executed in the build. - Reduced sleep time in various timing related tests. --- build.gradle | 2 +- .../context/ClassLevelDirtiesContextTests.java | 8 +++++++- .../context/junit4/RepeatedSpringRunnerTests.java | 14 +++++++------- ...4SuiteTests.java => SpringJUnit4TestSuite.java} | 11 ++++++----- .../context/junit4/TimedSpringRunnerTests.java | 8 ++++---- 5 files changed, 25 insertions(+), 18 deletions(-) rename spring-test/src/test/java/org/springframework/test/context/junit4/{SpringJUnit4SuiteTests.java => SpringJUnit4TestSuite.java} (95%) diff --git a/build.gradle b/build.gradle index 09a4ff20a8d..d3b51341d9c 100644 --- a/build.gradle +++ b/build.gradle @@ -655,7 +655,7 @@ project("spring-test") { dependsOn testNG useJUnit() // "TestCase" classes are run by other test classes, not the build. - exclude "**/*TestCase.class" + exclude(["**/*TestCase.class", "**/*TestSuite.class"]) } dependencies { diff --git a/spring-test/src/test/java/org/springframework/test/context/ClassLevelDirtiesContextTests.java b/spring-test/src/test/java/org/springframework/test/context/ClassLevelDirtiesContextTests.java index 3768b03cb63..bcfe0d3828c 100644 --- a/spring-test/src/test/java/org/springframework/test/context/ClassLevelDirtiesContextTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/ClassLevelDirtiesContextTests.java @@ -29,6 +29,7 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Configuration; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -179,9 +180,14 @@ public class ClassLevelDirtiesContextTests { @RunWith(SpringJUnit4ClassRunner.class) @TestExecutionListeners( { DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class }) - @ContextConfiguration("/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerAppCtxTests-context.xml") + @ContextConfiguration public static abstract class BaseTestCase { + @Configuration + static class Config { + /* no beans */ + } + @Autowired protected ApplicationContext applicationContext; diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/RepeatedSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/RepeatedSpringRunnerTests.java index 49d5ad5e7a4..7f6a937ce38 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/RepeatedSpringRunnerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/RepeatedSpringRunnerTests.java @@ -156,34 +156,34 @@ public class RepeatedSpringRunnerTests { public static final class TimedRepeatedTestCase extends AbstractRepeatedTestCase { @Test - @Timed(millis = 10000) + @Timed(millis = 1000) @Repeat(5) public void repeatedFiveTimesButDoesNotExceedTimeout() throws Exception { incrementInvocationCount(); } @Test - @Timed(millis = 100) + @Timed(millis = 10) @Repeat(1) public void singleRepetitionExceedsTimeout() throws Exception { incrementInvocationCount(); - Thread.sleep(250); + Thread.sleep(15); } @Test - @Timed(millis = 200) + @Timed(millis = 20) @Repeat(4) public void firstRepetitionOfManyExceedsTimeout() throws Exception { incrementInvocationCount(); - Thread.sleep(250); + Thread.sleep(25); } @Test - @Timed(millis = 1000) + @Timed(millis = 100) @Repeat(10) public void collectiveRepetitionsExceedTimeout() throws Exception { incrementInvocationCount(); - Thread.sleep(150); + Thread.sleep(11); } } diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4SuiteTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4TestSuite.java similarity index 95% rename from spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4SuiteTests.java rename to spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4TestSuite.java index 2f293d88475..72ddd1b8c4f 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4SuiteTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4TestSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,15 +42,16 @@ import org.springframework.test.context.junit4.profile.xml.DevProfileXmlConfigTe /** * JUnit test suite for tests involving {@link SpringJUnit4ClassRunner} and the - * Spring TestContext Framework. + * Spring TestContext Framework; only intended to be run manually as a + * convenience. * *

This test suite serves a dual purpose of verifying that tests run with * {@link SpringJUnit4ClassRunner} can be used in conjunction with JUnit's * {@link Suite} runner. * *

Note that tests included in this suite will be executed at least twice if - * run from an automated build process, test runner, etc. that is configured to - * run tests based on a "*Tests.class" pattern match. + * run from an automated build process, test runner, etc. that is not configured + * to exclude tests based on a "*TestSuite.class" pattern match. * * @author Sam Brannen * @since 2.5 @@ -104,6 +105,6 @@ StandardJUnit4FeaturesTests.class,// TimedTransactionalSpringRunnerTests.class,// HibernateSessionFlushingTests.class // }) -public class SpringJUnit4SuiteTests { +public class SpringJUnit4TestSuite { /* this test case consists entirely of tests loaded as a suite. */ } diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java index a3679ba75e1..db8690072e3 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java @@ -76,16 +76,16 @@ public class TimedSpringRunnerTests { } // Should Fail due to timeout. - @Test(timeout = 200) + @Test(timeout = 10) public void testJUnitTimeoutWithOneSecondWait() throws Exception { - Thread.sleep(1000); + Thread.sleep(20); } // Should Fail due to timeout. @Test - @Timed(millis = 200) + @Timed(millis = 10) public void testSpringTimeoutWithOneSecondWait() throws Exception { - Thread.sleep(1000); + Thread.sleep(20); } // Should Fail due to duplicate configuration. From 2db7a1228836ff7b39047fe137fe43a915f35e94 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 11 Jan 2013 12:31:16 -0800 Subject: [PATCH 039/143] Generate eclipse project settings from gradle Update gradle to generate the following project specific eclipse settings: - Java formatting - Cleanup options - Warning settings - Code template with copyright header - WTP module meta-data In addition this commit changes the eclipse project .classpath file to output test and main classes to different folders. This is required to prevent eclipse WTP from packaging test classes into /WEB-INF/lib jar files. Issue: SPR-9518 --- build.gradle | 2 - gradle/ide.gradle | 50 ++- src/eclipse/org.eclipse.jdt.core.prefs | 388 ++++++++++++++++++ src/eclipse/org.eclipse.jdt.ui.prefs | 62 +++ src/eclipse/org.eclipse.wst.common.component | 6 + ....eclipse.wst.common.project.facet.core.xml | 7 + 6 files changed, 512 insertions(+), 3 deletions(-) create mode 100644 src/eclipse/org.eclipse.jdt.core.prefs create mode 100644 src/eclipse/org.eclipse.jdt.ui.prefs create mode 100644 src/eclipse/org.eclipse.wst.common.component create mode 100644 src/eclipse/org.eclipse.wst.common.project.facet.core.xml diff --git a/build.gradle b/build.gradle index d3b51341d9c..228f10b07c3 100644 --- a/build.gradle +++ b/build.gradle @@ -21,8 +21,6 @@ configure(allprojects) { project -> apply plugin: "propdeps" apply plugin: "java" - apply plugin: "propdeps-eclipse" - apply plugin: "propdeps-idea" apply plugin: "test-source-set-dependencies" apply from: "${gradleScriptDir}/ide.gradle" diff --git a/gradle/ide.gradle b/gradle/ide.gradle index e1bb117641e..d0071ebaa3f 100644 --- a/gradle/ide.gradle +++ b/gradle/ide.gradle @@ -1,7 +1,12 @@ import org.gradle.plugins.ide.eclipse.model.ProjectDependency +import org.gradle.plugins.ide.eclipse.model.SourceFolder + +apply plugin: "propdeps-eclipse" +apply plugin: "propdeps-idea" + +// Replace classpath entries with project dependencies (GRADLE-1116) eclipse.classpath.file.whenMerged { classpath -> - // GRADLE-1116 def regexp = /.*?\/([^\/]+)\/build\/[^\/]+\/(?:main|test)/ // only match those that end in main or test (avoids removing necessary entries like build/classes/jaxb) def projectOutputDependencies = classpath.entries.findAll { entry -> entry.path =~ regexp } projectOutputDependencies.each { entry -> @@ -19,3 +24,46 @@ eclipse.classpath.file.whenMerged { classpath -> } classpath.entries.removeAll { entry -> (entry.path =~ /(?!.*?repack.*\.jar).*?\/([^\/]+)\/build\/libs\/[^\/]+\.jar/) } } + + +// Use separate main/test outputs (prevents WTP from packaging test classes) +eclipse.classpath.defaultOutputDir = file(project.name+"/eclipse/bin") +eclipse.classpath.file.whenMerged { classpath -> + classpath.entries.findAll{ it instanceof SourceFolder }.each { + it.output = "build/eclipse/" + it.path.split("/")[1] + } +} + +// Allow projects to be used as WPT modules +eclipse.project.natures "org.eclipse.wst.common.project.facet.core.nature" + + +// Include project specific settings +task eclipseSettings(type: Copy) { + from rootProject.files( + "src/eclipse/org.eclipse.jdt.ui.prefs", + "src/eclipse/org.eclipse.wst.common.project.facet.core.xml") + into project.file('.settings/') +} + +task eclipseWstComponent(type: Copy) { + from rootProject.files( + "src/eclipse/org.eclipse.wst.common.component") + into project.file('.settings/') + expand(deployname: project.name) +} + +task eclipseJdtPrepare(type: Copy) { + from rootProject.file("src/eclipse/org.eclipse.jdt.core.prefs") + into project.file(".settings/") +} + +task cleanEclipseJdtUi(type: Delete) { + delete project.file(".settings/org.eclipse.jdt.ui.prefs"); + delete project.file(".settings/org.eclipse.wst.common.component"); + delete project.file(".settings/org.eclipse.wst.common.project.facet.core.xml"); +} + +tasks["eclipseJdt"].dependsOn(eclipseJdtPrepare) +tasks["cleanEclipse"].dependsOn(cleanEclipseJdtUi) +tasks["eclipse"].dependsOn(eclipseSettings, eclipseWstComponent) diff --git a/src/eclipse/org.eclipse.jdt.core.prefs b/src/eclipse/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..d6f23343f57 --- /dev/null +++ b/src/eclipse/org.eclipse.jdt.core.prefs @@ -0,0 +1,388 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/main/resources=UTF-8 +encoding//src/test/java=UTF-8 +encoding//src/test/resources=UTF-8 +org.eclipse.jdt.core.codeComplete.argumentPrefixes= +org.eclipse.jdt.core.codeComplete.argumentSuffixes= +org.eclipse.jdt.core.codeComplete.fieldPrefixes= +org.eclipse.jdt.core.codeComplete.fieldSuffixes= +org.eclipse.jdt.core.codeComplete.localPrefixes= +org.eclipse.jdt.core.codeComplete.localSuffixes= +org.eclipse.jdt.core.codeComplete.staticFieldPrefixes= +org.eclipse.jdt.core.codeComplete.staticFieldSuffixes= +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=protected +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nullReference=ignore +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore +org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.7 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=80 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=0 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=1 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=1 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=1 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert +org.eclipse.jdt.core.formatter.comment.line_length=90 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=1 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=90 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=tab +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=false +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true diff --git a/src/eclipse/org.eclipse.jdt.ui.prefs b/src/eclipse/org.eclipse.jdt.ui.prefs new file mode 100644 index 00000000000..47eb08906bb --- /dev/null +++ b/src/eclipse/org.eclipse.jdt.ui.prefs @@ -0,0 +1,62 @@ +cleanup.add_default_serial_version_id=true +cleanup.add_generated_serial_version_id=false +cleanup.add_missing_annotations=false +cleanup.add_missing_deprecated_annotations=true +cleanup.add_missing_methods=false +cleanup.add_missing_nls_tags=false +cleanup.add_missing_override_annotations=true +cleanup.add_missing_override_annotations_interface_methods=true +cleanup.add_serial_version_id=false +cleanup.always_use_blocks=true +cleanup.always_use_parentheses_in_expressions=false +cleanup.always_use_this_for_non_static_field_access=true +cleanup.always_use_this_for_non_static_method_access=false +cleanup.convert_to_enhanced_for_loop=false +cleanup.correct_indentation=false +cleanup.format_source_code=false +cleanup.format_source_code_changes_only=false +cleanup.make_local_variable_final=false +cleanup.make_parameters_final=false +cleanup.make_private_fields_final=true +cleanup.make_type_abstract_if_missing_method=false +cleanup.make_variable_declarations_final=true +cleanup.never_use_blocks=false +cleanup.never_use_parentheses_in_expressions=true +cleanup.organize_imports=false +cleanup.qualify_static_field_accesses_with_declaring_class=false +cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +cleanup.qualify_static_member_accesses_with_declaring_class=true +cleanup.qualify_static_method_accesses_with_declaring_class=false +cleanup.remove_private_constructors=true +cleanup.remove_trailing_whitespaces=true +cleanup.remove_trailing_whitespaces_all=true +cleanup.remove_trailing_whitespaces_ignore_empty=false +cleanup.remove_unnecessary_casts=true +cleanup.remove_unnecessary_nls_tags=true +cleanup.remove_unused_imports=true +cleanup.remove_unused_local_variables=false +cleanup.remove_unused_private_fields=true +cleanup.remove_unused_private_members=false +cleanup.remove_unused_private_methods=true +cleanup.remove_unused_private_types=true +cleanup.sort_members=false +cleanup.sort_members_all=false +cleanup.use_blocks=true +cleanup.use_blocks_only_for_return_and_throw=false +cleanup.use_parentheses_in_expressions=false +cleanup.use_this_for_non_static_field_access=true +cleanup.use_this_for_non_static_field_access_only_if_necessary=false +cleanup.use_this_for_non_static_method_access=false +cleanup.use_this_for_non_static_method_access_only_if_necessary=true +cleanup_profile=_Spring +cleanup_settings_version=2 +eclipse.preferences.version=1 +formatter_profile=_Spring +formatter_settings_version=12 +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=java;javax;org;com; +org.eclipse.jdt.ui.javadoc=true +org.eclipse.jdt.ui.ondemandthreshold=9999 +org.eclipse.jdt.ui.staticondemandthreshold=9999 +org.eclipse.jdt.ui.text.custom_code_templates= diff --git a/src/eclipse/org.eclipse.wst.common.component b/src/eclipse/org.eclipse.wst.common.component new file mode 100644 index 00000000000..ce05d97c85e --- /dev/null +++ b/src/eclipse/org.eclipse.wst.common.component @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/eclipse/org.eclipse.wst.common.project.facet.core.xml b/src/eclipse/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 00000000000..e4a5a385a27 --- /dev/null +++ b/src/eclipse/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,7 @@ + + + + + + + From f0a1ff2d762d0cc4aac176e1be55d8758f85db0e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 11 Jan 2013 21:55:57 +0100 Subject: [PATCH 040/143] JDBC parameter binding uses JDBC 3.0 ParameterMetaData (if available) for type determination Forward-ported from 3.1.2, with minor modifications for defensiveness against the JDBC driver. Issue: SPR-10084 --- .../core/PreparedStatementCreatorFactory.java | 14 +++---- .../jdbc/core/StatementCreatorUtils.java | 40 +++++++++++++------ 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/PreparedStatementCreatorFactory.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/PreparedStatementCreatorFactory.java index 7efe962c642..6ba84b72b82 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/PreparedStatementCreatorFactory.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/PreparedStatementCreatorFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -146,7 +146,7 @@ public class PreparedStatementCreatorFactory { * Return a new PreparedStatementSetter for the given parameters. * @param params list of parameters (may be {@code null}) */ - public PreparedStatementSetter newPreparedStatementSetter(List params) { + public PreparedStatementSetter newPreparedStatementSetter(List params) { return new PreparedStatementCreatorImpl(params != null ? params : Collections.emptyList()); } @@ -225,7 +225,7 @@ public class PreparedStatementCreatorFactory { } public PreparedStatement createPreparedStatement(Connection con) throws SQLException { - PreparedStatement ps = null; + PreparedStatement ps; if (generatedKeysColumnNames != null || returnGeneratedKeys) { try { if (generatedKeysColumnNames != null) { @@ -235,10 +235,10 @@ public class PreparedStatementCreatorFactory { ps = con.prepareStatement(this.actualSql, PreparedStatement.RETURN_GENERATED_KEYS); } } - catch (AbstractMethodError ex) { + catch (AbstractMethodError err) { throw new InvalidDataAccessResourceUsageException( - "The JDBC driver is not compliant to JDBC 3.0 and thus " + - "does not support retrieval of auto-generated keys", ex); + "Your JDBC driver is not compliant with JDBC 3.0 - " + + "it does not support retrieval of auto-generated keys", err); } } else if (resultSetType == ResultSet.TYPE_FORWARD_ONLY && !updatableResults) { @@ -263,7 +263,7 @@ public class PreparedStatementCreatorFactory { int sqlColIndx = 1; for (int i = 0; i < this.parameters.size(); i++) { Object in = this.parameters.get(i); - SqlParameter declaredParameter = null; + SqlParameter declaredParameter; // SqlParameterValue overrides declared parameter metadata, in particular for // independence from the declared parameter position in case of named parameters. if (in instanceof SqlParameterValue) { diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java index 1e6a263ca4a..ea8e74501e9 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,6 +22,7 @@ import java.math.BigInteger; import java.sql.Blob; import java.sql.Clob; import java.sql.DatabaseMetaData; +import java.sql.ParameterMetaData; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Types; @@ -189,7 +190,7 @@ public abstract class StatementCreatorUtils { if (inValue instanceof SqlParameterValue) { SqlParameterValue parameterValue = (SqlParameterValue) inValue; if (logger.isDebugEnabled()) { - logger.debug("Overriding typeinfo with runtime info from SqlParameterValue: column index " + paramIndex + + logger.debug("Overriding type info with runtime info from SqlParameterValue: column index " + paramIndex + ", SQL type " + parameterValue.getSqlType() + ", Type name " + parameterValue.getTypeName()); } @@ -228,18 +229,31 @@ public abstract class StatementCreatorUtils { boolean useSetObject = false; sqlType = Types.NULL; try { - DatabaseMetaData dbmd = ps.getConnection().getMetaData(); - String databaseProductName = dbmd.getDatabaseProductName(); - String jdbcDriverName = dbmd.getDriverName(); - if (databaseProductName.startsWith("Informix") || - jdbcDriverName.startsWith("Microsoft SQL Server")) { - useSetObject = true; + ParameterMetaData pmd = null; + try { + pmd = ps.getParameterMetaData(); } - else if (databaseProductName.startsWith("DB2") || - jdbcDriverName.startsWith("jConnect") || - jdbcDriverName.startsWith("SQLServer")|| - jdbcDriverName.startsWith("Apache Derby")) { - sqlType = Types.VARCHAR; + catch (Throwable ex) { + // JDBC driver not compliant with JDBC 3.0 + // -> proceed with database-specific checks + } + if (pmd != null) { + sqlType = pmd.getParameterType(paramIndex); + } + else { + DatabaseMetaData dbmd = ps.getConnection().getMetaData(); + String databaseProductName = dbmd.getDatabaseProductName(); + String jdbcDriverName = dbmd.getDriverName(); + if (databaseProductName.startsWith("Informix") || + jdbcDriverName.startsWith("Microsoft SQL Server")) { + useSetObject = true; + } + else if (databaseProductName.startsWith("DB2") || + jdbcDriverName.startsWith("jConnect") || + jdbcDriverName.startsWith("SQLServer")|| + jdbcDriverName.startsWith("Apache Derby")) { + sqlType = Types.VARCHAR; + } } } catch (Throwable ex) { From 54c873b4c430a6c13698080fcde99835ca2a541b Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 11 Jan 2013 16:25:47 -0500 Subject: [PATCH 041/143] Support multiple Validators in DataBinder DataBinder now allows registering additional Validator instances. This may be useful when adding a Spring Validator to a globally registered JSR-303 LocalValidatorFactoryBean. Issue: SPR-9436 --- .../validation/DataBinder.java | 76 +++++++++++++++---- 1 file changed, 60 insertions(+), 16 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/validation/DataBinder.java b/spring-context/src/main/java/org/springframework/validation/DataBinder.java index a9d5d835873..35e0ea4a568 100644 --- a/spring-context/src/main/java/org/springframework/validation/DataBinder.java +++ b/spring-context/src/main/java/org/springframework/validation/DataBinder.java @@ -18,7 +18,11 @@ package org.springframework.validation; import java.beans.PropertyEditor; import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; @@ -141,7 +145,7 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter { private BindingErrorProcessor bindingErrorProcessor = new DefaultBindingErrorProcessor(); - private Validator validator; + private final List validators = new ArrayList(); private ConversionService conversionService; @@ -493,21 +497,58 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter { /** * Set the Validator to apply after each binding step. + * @see #addValidators(Validator...) + * @see #replaceValidators(Validator...) */ public void setValidator(Validator validator) { - if (validator != null && (getTarget() != null && !validator.supports(getTarget().getClass()))) { - throw new IllegalStateException("Invalid target for Validator [" + validator + "]: " + getTarget()); + assertValidators(validator); + this.validators.clear(); + this.validators.add(validator); + } + + private void assertValidators(Validator... validators) { + Assert.notNull(validators, "Validators required"); + for (Validator validator : validators) { + if (validator != null && (getTarget() != null && !validator.supports(getTarget().getClass()))) { + throw new IllegalStateException("Invalid target for Validator [" + validator + "]: " + getTarget()); + } } - this.validator = validator; } /** - * Return the Validator to apply after each binding step, if any. + * Add Validators to apply after each binding step. + * @see #setValidator(Validator) + * @see #replaceValidators(Validator...) */ - public Validator getValidator() { - return this.validator; + public void addValidators(Validator... validators) { + assertValidators(validators); + this.validators.addAll(Arrays.asList(validators)); } + /** + * Replace the Validators to apply after each binding step. + * @see #setValidator(Validator) + * @see #addValidators(Validator...) + */ + public void replaceValidators(Validator... validators) { + assertValidators(validators); + this.validators.clear(); + this.validators.addAll(Arrays.asList(validators)); + } + + /** + * Return the primary Validator to apply after each binding step, if any. + */ + public Validator getValidator() { + return this.validators.size() > 0 ? this.validators.get(0) : null; + } + + /** + * Return the Validators to apply after data binding. + */ + public List getValidators() { + return Collections.unmodifiableList(this.validators); + } //--------------------------------------------------------------------- // Implementation of PropertyEditorRegistry/TypeConverter interface @@ -708,28 +749,31 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter { /** - * Invoke the specified Validator, if any. + * Invoke the specified Validators, if any. * @see #setValidator(Validator) * @see #getBindingResult() */ public void validate() { - this.validator.validate(getTarget(), getBindingResult()); + for (Validator validator : this.validators) { + validator.validate(getTarget(), getBindingResult()); + } } /** - * Invoke the specified Validator, if any, with the given validation hints. + * Invoke the specified Validators, if any, with the given validation hints. *

Note: Validation hints may get ignored by the actual target Validator. * @param validationHints one or more hint objects to be passed to a {@link SmartValidator} * @see #setValidator(Validator) * @see SmartValidator#validate(Object, Errors, Object...) */ public void validate(Object... validationHints) { - Validator validator = getValidator(); - if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) { - ((SmartValidator) validator).validate(getTarget(), getBindingResult(), validationHints); - } - else if (validator != null) { - validator.validate(getTarget(), getBindingResult()); + for (Validator validator : getValidators()) { + if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) { + ((SmartValidator) validator).validate(getTarget(), getBindingResult(), validationHints); + } + else if (validator != null) { + validator.validate(getTarget(), getBindingResult()); + } } } From 5ddc313beffac6459f027e9c41cfc3597bf184f4 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 11 Jan 2013 14:19:59 -0800 Subject: [PATCH 042/143] Upgrade to prop-deps gradle plugin 0.0.3 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 228f10b07c3..d37870c20ff 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ buildscript { maven { url "http://repo.springsource.org/plugins-release" } } dependencies { - classpath("org.springframework.build.gradle:propdeps-plugin:0.0.1") + classpath("org.springframework.build.gradle:propdeps-plugin:0.0.3") classpath("org.springframework.build.gradle:docbook-reference-plugin:0.2.4") } } From fce7adc400d4b519da40d361a1b1f62fc58e185a Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 11 Jan 2013 14:55:52 -0800 Subject: [PATCH 043/143] Consider bridge methods in SpEL properties Revert ReflectivePropertyAccessor changes from 107fafb and instead consider all methods when resolving properties. Methods are now sorted such that non-bridge methods are considered before bridge methods. Issue: SPR-10162 --- .../support/ReflectivePropertyAccessor.java | 26 +++++++++++++++---- .../expression/spel/SpelReproTests.java | 23 +++++++++++++++- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java index 97d87d50c3b..7b7ce34179a 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java @@ -21,6 +21,8 @@ import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Comparator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -310,13 +312,13 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { * Find a getter method for the specified property. */ protected Method findGetterForProperty(String propertyName, Class clazz, boolean mustBeStatic) { - Method[] ms = clazz.getMethods(); + Method[] ms = getSortedClassMethods(clazz); String propertyMethodSuffix = getPropertyMethodSuffix(propertyName); // Try "get*" method... String getterName = "get" + propertyMethodSuffix; for (Method method : ms) { - if (!method.isBridge() && method.getName().equals(getterName) && method.getParameterTypes().length == 0 && + if (method.getName().equals(getterName) && method.getParameterTypes().length == 0 && (!mustBeStatic || Modifier.isStatic(method.getModifiers()))) { return method; } @@ -324,7 +326,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { // Try "is*" method... getterName = "is" + propertyMethodSuffix; for (Method method : ms) { - if (!method.isBridge() && method.getName().equals(getterName) && method.getParameterTypes().length == 0 && + if (method.getName().equals(getterName) && method.getParameterTypes().length == 0 && (boolean.class.equals(method.getReturnType()) || Boolean.class.equals(method.getReturnType())) && (!mustBeStatic || Modifier.isStatic(method.getModifiers()))) { return method; @@ -337,10 +339,10 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { * Find a setter method for the specified property. */ protected Method findSetterForProperty(String propertyName, Class clazz, boolean mustBeStatic) { - Method[] methods = clazz.getMethods(); + Method[] methods = getSortedClassMethods(clazz); String setterName = "set" + getPropertyMethodSuffix(propertyName); for (Method method : methods) { - if (!method.isBridge() && method.getName().equals(setterName) && method.getParameterTypes().length == 1 && + if (method.getName().equals(setterName) && method.getParameterTypes().length == 1 && (!mustBeStatic || Modifier.isStatic(method.getModifiers()))) { return method; } @@ -348,6 +350,20 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { return null; } + /** + * Returns class methods ordered with non bridge methods appearing higher. + */ + private Method[] getSortedClassMethods(Class clazz) { + Method[] methods = clazz.getMethods(); + Arrays.sort(methods, new Comparator() { + @Override + public int compare(Method o1, Method o2) { + return (o1.isBridge() == o2.isBridge()) ? 0 : (o1.isBridge() ? 1 : -1); + } + }); + return methods; + } + protected String getPropertyMethodSuffix(String propertyName) { if (propertyName.length() > 1 && Character.isUpperCase(propertyName.charAt(1))) { return propertyName; diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java index e1122ab04f9..9cb0a582532 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,6 +56,7 @@ import static org.junit.Assert.*; * @author Andy Clement * @author Juergen Hoeller * @author Clark Duplichien + * @author Phillip Webb */ public class SpelReproTests extends ExpressionTestCase { @@ -1656,6 +1657,15 @@ public class SpelReproTests extends ExpressionTestCase { assertEquals(Integer.class, value.getTypeDescriptor().getType()); } + @Test + public void SPR_10162_onlyBridgeMethodTest() throws Exception { + ReflectivePropertyAccessor accessor = new ReflectivePropertyAccessor(); + StandardEvaluationContext context = new StandardEvaluationContext(); + Object target = new OnlyBridgeMethod(); + TypedValue value = accessor.read(context, target , "property"); + assertEquals(Integer.class, value.getTypeDescriptor().getType()); + } + @Test public void SPR_10091_simpleTestValueType() { ExpressionParser parser = new SpelExpressionParser(); @@ -1722,4 +1732,15 @@ public class SpelReproTests extends ExpressionTestCase { } } + static class PackagePrivateClassWithGetter { + + public Integer getProperty() { + return null; + } + } + + public static class OnlyBridgeMethod extends PackagePrivateClassWithGetter { + + } + } From e8fcde09abc958436ddd7a15d7e708080de72cfd Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 11 Jan 2013 15:12:59 -0800 Subject: [PATCH 044/143] Polish author and copyright year --- .../expression/spel/support/ReflectivePropertyAccessor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java index 7b7ce34179a..ffe188bae83 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -44,6 +44,7 @@ import org.springframework.util.StringUtils; * * @author Andy Clement * @author Juergen Hoeller + * @author Phillip Webb * @since 3.0 */ public class ReflectivePropertyAccessor implements PropertyAccessor { From e4c1361c60450c7aef16e28ee6db80e96c4965ef Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 11 Jan 2013 15:25:12 -0800 Subject: [PATCH 045/143] Remove accidentally committed interface @Override --- .../expression/spel/support/ReflectivePropertyAccessor.java | 1 - 1 file changed, 1 deletion(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java index ffe188bae83..015c02fd52d 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java @@ -357,7 +357,6 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { private Method[] getSortedClassMethods(Class clazz) { Method[] methods = clazz.getMethods(); Arrays.sort(methods, new Comparator() { - @Override public int compare(Method o1, Method o2) { return (o1.isBridge() == o2.isBridge()) ? 0 : (o1.isBridge() ? 1 : -1); } From 8a37521a3cd5dc0d51599abf9824c35513816fbb Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Mon, 14 Jan 2013 11:40:21 +0100 Subject: [PATCH 046/143] Fix copyright year & method names in spring-test This commit fixes the copyright year for changes made in commit 5b147bfba8d5d5837b96357a52867e433137f64b. In addition, method names have been changed to reflect the semantic changes made in that same commit. --- .../test/context/ClassLevelDirtiesContextTests.java | 4 ++-- .../context/junit4/RepeatedSpringRunnerTests.java | 2 +- .../test/context/junit4/TimedSpringRunnerTests.java | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/spring-test/src/test/java/org/springframework/test/context/ClassLevelDirtiesContextTests.java b/spring-test/src/test/java/org/springframework/test/context/ClassLevelDirtiesContextTests.java index bcfe0d3828c..0be28a871e1 100644 --- a/spring-test/src/test/java/org/springframework/test/context/ClassLevelDirtiesContextTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/ClassLevelDirtiesContextTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -38,7 +38,7 @@ import org.springframework.test.context.support.DependencyInjectionTestExecution import org.springframework.test.context.support.DirtiesContextTestExecutionListener; /** - * JUnit 4 based integration test which verifies correct {@link ContextCache + * JUnit 4 based integration test which verifies correct {@linkplain ContextCache * application context caching} in conjunction with the * {@link SpringJUnit4ClassRunner} and the {@link DirtiesContext * @DirtiesContext} annotation at the class level. diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/RepeatedSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/RepeatedSpringRunnerTests.java index 7f6a937ce38..6b7241261be 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/RepeatedSpringRunnerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/RepeatedSpringRunnerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java index db8690072e3..446bbb95d33 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -64,34 +64,34 @@ public class TimedSpringRunnerTests { // Should Pass. @Test(timeout = 2000) - public void testJUnitTimeoutWithNoOp() { + public void jUnitTimeoutWithNoOp() { /* no-op */ } // Should Pass. @Test @Timed(millis = 2000) - public void testSpringTimeoutWithNoOp() { + public void springTimeoutWithNoOp() { /* no-op */ } // Should Fail due to timeout. @Test(timeout = 10) - public void testJUnitTimeoutWithOneSecondWait() throws Exception { + public void jUnitTimeoutWithSleep() throws Exception { Thread.sleep(20); } // Should Fail due to timeout. @Test @Timed(millis = 10) - public void testSpringTimeoutWithOneSecondWait() throws Exception { + public void springTimeoutWithSleep() throws Exception { Thread.sleep(20); } // Should Fail due to duplicate configuration. @Test(timeout = 200) @Timed(millis = 200) - public void testSpringAndJUnitTimeout() { + public void springAndJUnitTimeouts() { /* no-op */ } } From 8694a0aac06388332373765a3cf0a494b0243a90 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Mon, 14 Jan 2013 12:24:12 +0100 Subject: [PATCH 047/143] Minor changes to AnnotationUtils - Polished Javadoc for isAnnotationDeclaredLocally() and isAnnotationInherited(). - Removed unnecessary call to Arrays.asList() in isAnnotationDeclaredLocally(). --- .../core/annotation/AnnotationUtils.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index e3dce1b2b4d..a1701c9db13 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,7 @@ package org.springframework.core.annotation; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; -import java.util.Arrays; + import java.util.Map; import java.util.WeakHashMap; @@ -255,8 +255,9 @@ public abstract class AnnotationUtils { * declared locally on the supplied {@code clazz}. The supplied {@link Class} * may represent any type. *

Note: This method does not determine if the annotation is - * {@link java.lang.annotation.Inherited inherited}. For greater clarity regarding inherited - * annotations, consider using {@link #isAnnotationInherited(Class, Class)} instead. + * {@linkplain java.lang.annotation.Inherited inherited}. For greater clarity + * regarding inherited annotations, consider using + * {@link #isAnnotationInherited(Class, Class)} instead. * @param annotationType the Class object corresponding to the annotation type * @param clazz the Class object corresponding to the class on which to check for the annotation * @return {@code true} if an annotation for the specified {@code annotationType} @@ -268,7 +269,7 @@ public abstract class AnnotationUtils { Assert.notNull(annotationType, "Annotation type must not be null"); Assert.notNull(clazz, "Class must not be null"); boolean declaredLocally = false; - for (Annotation annotation : Arrays.asList(clazz.getDeclaredAnnotations())) { + for (Annotation annotation : clazz.getDeclaredAnnotations()) { if (annotation.annotationType().equals(annotationType)) { declaredLocally = true; break; @@ -279,16 +280,16 @@ public abstract class AnnotationUtils { /** * Determine whether an annotation for the specified {@code annotationType} is present - * on the supplied {@code clazz} and is {@link java.lang.annotation.Inherited inherited} - * i.e., not declared locally for the class). + * on the supplied {@code clazz} and is {@linkplain java.lang.annotation.Inherited inherited} + * (i.e., not declared locally for the class). *

If the supplied {@code clazz} is an interface, only the interface itself will be checked. * In accordance with standard meta-annotation semantics, the inheritance hierarchy for interfaces - * will not be traversed. See the {@link java.lang.annotation.Inherited JavaDoc} for the - * @Inherited meta-annotation for further details regarding annotation inheritance. + * will not be traversed. See the {@linkplain java.lang.annotation.Inherited Javadoc} for the + * {@code @Inherited} meta-annotation for further details regarding annotation inheritance. * @param annotationType the Class object corresponding to the annotation type * @param clazz the Class object corresponding to the class on which to check for the annotation * @return {@code true} if an annotation for the specified {@code annotationType} is present - * on the supplied {@code clazz} and is {@link java.lang.annotation.Inherited inherited} + * on the supplied {@code clazz} and is inherited * @see Class#isAnnotationPresent(Class) * @see #isAnnotationDeclaredLocally(Class, Class) */ From f57c24cd8c84f0456766c4efc30916affc5d39a5 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 14 Jan 2013 10:25:33 -0800 Subject: [PATCH 048/143] Allow 'gradle eclipse' without 'cleanEclipse' Allow eclipse projects to be regenerated without first requiring the 'gradle cleanEclipse' task to be executed. --- gradle/ide.gradle | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gradle/ide.gradle b/gradle/ide.gradle index d0071ebaa3f..05b62dfb099 100644 --- a/gradle/ide.gradle +++ b/gradle/ide.gradle @@ -28,6 +28,13 @@ eclipse.classpath.file.whenMerged { classpath -> // Use separate main/test outputs (prevents WTP from packaging test classes) eclipse.classpath.defaultOutputDir = file(project.name+"/eclipse/bin") +eclipse.classpath.file.beforeMerged { classpath -> + classpath.entries.findAll{ it instanceof SourceFolder }.each { + if(it.output.startsWith("build/eclipse")) { + it.output = null + } + } +} eclipse.classpath.file.whenMerged { classpath -> classpath.entries.findAll{ it instanceof SourceFolder }.each { it.output = "build/eclipse/" + it.path.split("/")[1] From 8bb67149a79d20c649902eb0984b766141d94581 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 14 Jan 2013 11:42:12 -0800 Subject: [PATCH 049/143] Fix eclipse .settings generation Fix issues where gradle would not regenerate .settings files due to the task being considered UP-TO-DATE. --- gradle/ide.gradle | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/gradle/ide.gradle b/gradle/ide.gradle index 05b62dfb099..af43e6bc9d9 100644 --- a/gradle/ide.gradle +++ b/gradle/ide.gradle @@ -51,6 +51,7 @@ task eclipseSettings(type: Copy) { "src/eclipse/org.eclipse.jdt.ui.prefs", "src/eclipse/org.eclipse.wst.common.project.facet.core.xml") into project.file('.settings/') + outputs.upToDateWhen { false } } task eclipseWstComponent(type: Copy) { @@ -58,17 +59,20 @@ task eclipseWstComponent(type: Copy) { "src/eclipse/org.eclipse.wst.common.component") into project.file('.settings/') expand(deployname: project.name) + outputs.upToDateWhen { false } } task eclipseJdtPrepare(type: Copy) { from rootProject.file("src/eclipse/org.eclipse.jdt.core.prefs") into project.file(".settings/") + outputs.upToDateWhen { false } } task cleanEclipseJdtUi(type: Delete) { - delete project.file(".settings/org.eclipse.jdt.ui.prefs"); - delete project.file(".settings/org.eclipse.wst.common.component"); - delete project.file(".settings/org.eclipse.wst.common.project.facet.core.xml"); + delete project.file(".settings/org.eclipse.jdt.ui.prefs") + delete project.file("org.eclipse.jdt.core.prefs") + delete project.file(".settings/org.eclipse.wst.common.component") + delete project.file(".settings/org.eclipse.wst.common.project.facet.core.xml") } tasks["eclipseJdt"].dependsOn(eclipseJdtPrepare) From d40bd8bd192bbbc6d9e9bf1dbabfae4a48d9f4d1 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 14 Jan 2013 11:43:58 -0800 Subject: [PATCH 050/143] Tweak eclipse warning settings Hide raw-type, generic and resource leak warnings. --- src/eclipse/org.eclipse.jdt.core.prefs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/eclipse/org.eclipse.jdt.core.prefs b/src/eclipse/org.eclipse.jdt.core.prefs index d6f23343f57..2286420ffe9 100644 --- a/src/eclipse/org.eclipse.jdt.core.prefs +++ b/src/eclipse/org.eclipse.jdt.core.prefs @@ -11,6 +11,11 @@ org.eclipse.jdt.core.codeComplete.localPrefixes= org.eclipse.jdt.core.codeComplete.localSuffixes= org.eclipse.jdt.core.codeComplete.staticFieldPrefixes= org.eclipse.jdt.core.codeComplete.staticFieldSuffixes= +org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve @@ -30,6 +35,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod= org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore @@ -45,10 +51,12 @@ org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=protected +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=default org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled @@ -65,12 +73,17 @@ org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignor org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error org.eclipse.jdt.core.compiler.problem.nullReference=ignore +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore -org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore +org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore @@ -84,6 +97,7 @@ org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore From bff36fb1456ff498354960a725f63f9116ee5b74 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 14 Jan 2013 14:07:26 -0800 Subject: [PATCH 051/143] Improve exceptions for multi-operand expressions Fix SpEL expression parser and tokenizer to provide better exceptions when dealing with operations that expect two operands. For example, prior to this commit the expression '/foo' would throw a NPE due to missing operands to the left of '/'. Issue: SPR-10146 --- .../expression/spel/SpelMessage.java | 5 +-- .../InternalSpelExpressionParser.java | 26 +++++++++++----- .../expression/spel/standard/Tokenizer.java | 17 +++++++--- .../expression/spel/SpelReproTests.java | 31 +++++++++++++++++-- 4 files changed, 62 insertions(+), 17 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java index a7f457e147d..d9ae1a2efb1 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,7 +108,8 @@ public enum SpelMessage { OPERAND_NOT_INCREMENTABLE(Kind.ERROR,1066,"the expression component ''{0}'' does not support increment"), // OPERAND_NOT_DECREMENTABLE(Kind.ERROR,1067,"the expression component ''{0}'' does not support decrement"), // NOT_ASSIGNABLE(Kind.ERROR,1068,"the expression component ''{0}'' is not assignable"), // - ; + MISSING_CHARACTER(Kind.ERROR,1069,"missing expected character ''{0}''"), + LEFT_OPERAND_PROBLEM(Kind.ERROR,1070, "Problem parsing left operand"); private Kind kind; private int code; diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java index be8e988157b..c243b5d7f4b 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -37,6 +37,7 @@ import org.springframework.util.StringUtils; * Hand written SpEL parser. Instances are reusable but are not thread safe. * * @author Andy Clement + * @author Phillip Webb * @since 3.0 */ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { @@ -104,8 +105,8 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { Token t = peekToken(); if (t.kind==TokenKind.ASSIGN) { // a=b if (expr==null) { - expr = new NullLiteral(toPos(t.startpos-1,t.endpos-1)); - } + expr = new NullLiteral(toPos(t.startpos-1,t.endpos-1)); + } nextToken(); SpelNodeImpl assignedValue = eatLogicalOrExpression(); return new Assign(toPos(t),expr,assignedValue); @@ -139,7 +140,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { while (peekIdentifierToken("or") || peekToken(TokenKind.SYMBOLIC_OR)) { Token t = nextToken(); //consume OR SpelNodeImpl rhExpr = eatLogicalAndExpression(); - checkRightOperand(t,rhExpr); + checkOperands(t,expr,rhExpr); expr = new OpOr(toPos(t),expr,rhExpr); } return expr; @@ -151,7 +152,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { while (peekIdentifierToken("and") || peekToken(TokenKind.SYMBOLIC_AND)) { Token t = nextToken();// consume 'AND' SpelNodeImpl rhExpr = eatRelationalExpression(); - checkRightOperand(t,rhExpr); + checkOperands(t,expr,rhExpr); expr = new OpAnd(toPos(t),expr,rhExpr); } return expr; @@ -164,7 +165,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { if (relationalOperatorToken != null) { Token t = nextToken(); //consume relational operator token SpelNodeImpl rhExpr = eatSumExpression(); - checkRightOperand(t,rhExpr); + checkOperands(t,expr,rhExpr); TokenKind tk = relationalOperatorToken.kind; if (relationalOperatorToken.isNumericRelationalOperator()) { int pos = toPos(t); @@ -217,7 +218,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { while (peekToken(TokenKind.STAR,TokenKind.DIV,TokenKind.MOD)) { Token t = nextToken(); // consume STAR/DIV/MOD SpelNodeImpl rhExpr = eatPowerIncDecExpression(); - checkRightOperand(t,rhExpr); + checkOperands(t,expr,rhExpr); if (t.kind==TokenKind.STAR) { expr = new OpMultiply(toPos(t),expr,rhExpr); } else if (t.kind==TokenKind.DIV) { @@ -836,6 +837,17 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { } } + private void checkOperands(Token token, SpelNodeImpl left, SpelNodeImpl right) { + checkLeftOperand(token, left); + checkRightOperand(token, right); + } + + private void checkLeftOperand(Token token, SpelNodeImpl operandExpression) { + if (operandExpression==null) { + raiseInternalException(token.startpos,SpelMessage.LEFT_OPERAND_PROBLEM); + } + } + private void checkRightOperand(Token token, SpelNodeImpl operandExpression) { if (operandExpression==null) { raiseInternalException(token.startpos,SpelMessage.RIGHT_OPERAND_PROBLEM); diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java index 396c8f17ffc..4223a405424 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,6 +29,7 @@ import org.springframework.util.Assert; * Lex some input data into a stream of tokens that can then be parsed. * * @author Andy Clement + * @author Phillip Webb * @since 3.0 */ class Tokenizer { @@ -137,14 +138,20 @@ class Tokenizer { } break; case '&': - if (isTwoCharToken(TokenKind.SYMBOLIC_AND)) { - pushPairToken(TokenKind.SYMBOLIC_AND); + if (!isTwoCharToken(TokenKind.SYMBOLIC_AND)) { + throw new InternalParseException(new SpelParseException( + expressionString, pos, + SpelMessage.MISSING_CHARACTER, "&")); } + pushPairToken(TokenKind.SYMBOLIC_AND); break; case '|': - if (isTwoCharToken(TokenKind.SYMBOLIC_OR)) { - pushPairToken(TokenKind.SYMBOLIC_OR); + if (!isTwoCharToken(TokenKind.SYMBOLIC_OR)) { + throw new InternalParseException(new SpelParseException( + expressionString, pos, + SpelMessage.MISSING_CHARACTER, "|")); } + pushPairToken(TokenKind.SYMBOLIC_OR); break; case '?': if (isTwoCharToken(TokenKind.SELECT)) { diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java index 9cb0a582532..01ff05220e2 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java @@ -16,6 +16,11 @@ package org.springframework.expression.spel; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; @@ -26,8 +31,9 @@ import java.util.Map; import java.util.Properties; import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; - +import org.junit.rules.ExpectedException; import org.springframework.core.convert.TypeDescriptor; import org.springframework.expression.AccessException; import org.springframework.expression.BeanResolver; @@ -48,8 +54,6 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardTypeLocator; import org.springframework.expression.spel.testresources.le.div.mod.reserved.Reserver; -import static org.junit.Assert.*; - /** * Reproduction tests cornering various SpEL JIRA issues. * @@ -60,6 +64,9 @@ import static org.junit.Assert.*; */ public class SpelReproTests extends ExpressionTestCase { + @Rule + public ExpectedException thrown = ExpectedException.none(); + @Test public void testNPE_SPR5661() { evaluate("joinThreeStrings('a',null,'c')", "anullc", String.class); @@ -1694,6 +1701,24 @@ public class SpelReproTests extends ExpressionTestCase { Object value = parser.parseExpression("primitiveProperty").getValue(evaluationContext); } + @Test + public void SPR_10146_malformedExpressions() throws Exception { + doTestSpr10146("/foo", "EL1070E:(pos 0): Problem parsing left operand"); + doTestSpr10146("*foo", "EL1070E:(pos 0): Problem parsing left operand"); + doTestSpr10146("%foo", "EL1070E:(pos 0): Problem parsing left operand"); + doTestSpr10146("foo", "EL1070E:(pos 0): Problem parsing left operand"); + doTestSpr10146("&&foo", "EL1070E:(pos 0): Problem parsing left operand"); + doTestSpr10146("||foo", "EL1070E:(pos 0): Problem parsing left operand"); + doTestSpr10146("&foo", "EL1069E:(pos 0): missing expected character '&'"); + doTestSpr10146("|foo", "EL1069E:(pos 0): missing expected character '|'"); + } + + private void doTestSpr10146(String expression, String expectedMessage) { + thrown.expect(SpelParseException.class); + thrown.expectMessage(expectedMessage); + new SpelExpressionParser().parseExpression(expression); + } public static class BooleanHolder { From ad91fa63faa28370c0263e2dada2405a3e48592f Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 14 Jan 2013 15:49:38 -0800 Subject: [PATCH 052/143] SpEL support for static finals on interfaces Update ReflectivePropertyAccessor to search for fields on super classes and implemented interfaces. Although the javadoc Class.getFields() implies that all public fields of class should be returned SpelReproTests demonstrates that this is not always the case. Issue: SPR-10125 --- .../support/ReflectivePropertyAccessor.java | 12 ++++++++++ .../expression/spel/SpelReproTests.java | 23 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java index 015c02fd52d..4c31c3f6b26 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java @@ -383,6 +383,18 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { return field; } } + if(clazz.getSuperclass() != null) { + Field field = findField(name, clazz.getSuperclass(), mustBeStatic); + if(field != null) { + return field; + } + } + for (Class implementedInterface : clazz.getInterfaces()) { + Field field = findField(name, implementedInterface, mustBeStatic); + if(field != null) { + return field; + } + } return null; } diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java index 01ff05220e2..765422302a7 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java @@ -16,8 +16,10 @@ package org.springframework.expression.spel; +import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -1720,6 +1722,15 @@ public class SpelReproTests extends ExpressionTestCase { new SpelExpressionParser().parseExpression(expression); } + @Test + public void SPR_10125() throws Exception { + StandardEvaluationContext context = new StandardEvaluationContext(); + String fromInterface = parser.parseExpression("T("+StaticFinalImpl1.class.getName()+").VALUE").getValue(context, String.class); + assertThat(fromInterface, is("interfaceValue")); + String fromClass = parser.parseExpression("T("+StaticFinalImpl2.class.getName()+").VALUE").getValue(context, String.class); + assertThat(fromClass, is("interfaceValue")); + } + public static class BooleanHolder { private Boolean simpleProperty = true; @@ -1768,4 +1779,16 @@ public class SpelReproTests extends ExpressionTestCase { } + public static interface StaticFinal { + public static final String VALUE = "interfaceValue"; + } + + public abstract static class AbstractStaticFinal implements StaticFinal { + } + + public static class StaticFinalImpl1 extends AbstractStaticFinal implements StaticFinal { + } + + public static class StaticFinalImpl2 extends AbstractStaticFinal { + } } From ad025b59c5e933fb7181c76f530e1f4ecc8f7c50 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 14 Jan 2013 18:19:44 -0500 Subject: [PATCH 053/143] Prepend context/servlet path to FormTag action The Form tag now fills in the context and servlet path if not present in the specified action. Issue: SPR-8684 --- .../web/servlet/support/RequestContext.java | 13 +++--- .../web/servlet/tags/form/FormTag.java | 5 +++ .../web/servlet/tags/form/FormTagTests.java | 44 +++++++++++++++++++ 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/RequestContext.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/RequestContext.java index 3f586a94467..cf63a425c33 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/RequestContext.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/RequestContext.java @@ -36,6 +36,7 @@ import org.springframework.ui.context.ThemeSource; import org.springframework.ui.context.support.ResourceBundleThemeSource; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; import org.springframework.validation.BindException; import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; @@ -422,14 +423,14 @@ public class RequestContext { * context path and the servlet path of the original request. This is useful * for building links to other resources within the application where a * servlet mapping of the style {@code "/main/*"} is used. - *

Delegates to the UrlPathHelper for decoding the context path. - * @see javax.servlet.http.HttpServletRequest#getContextPath - * @see javax.servlet.http.HttpServletRequest#getServletPath() - * @see #getUrlPathHelper + * Delegates to the UrlPathHelper to determine the context and servlet path. */ public String getPathToServlet() { - return this.urlPathHelper.getOriginatingContextPath(this.request) - + this.urlPathHelper.getOriginatingServletPath(this.request); + String path = this.urlPathHelper.getOriginatingContextPath(this.request); + if (StringUtils.hasText(this.urlPathHelper.getPathWithinServletMapping(this.request))) { + path += this.urlPathHelper.getOriginatingServletPath(this.request); + } + return path; } /** diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java index d3b6752adef..120b910727f 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java @@ -32,6 +32,7 @@ import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.servlet.support.RequestDataValueProcessor; import org.springframework.web.util.HtmlUtils; +import org.springframework.web.util.UrlPathHelper; /** * Databinding-aware JSP tag for rendering an HTML '{@code form}' whose @@ -411,6 +412,10 @@ public class FormTag extends AbstractHtmlElementTag { protected String resolveAction() throws JspException { String action = getAction(); if (StringUtils.hasText(action)) { + String pathToServlet = getRequestContext().getPathToServlet(); + if (action.startsWith("/") && !action.startsWith(getRequestContext().getContextPath())) { + action = pathToServlet + action; + } action = getDisplayString(evaluate(ACTION_ATTRIBUTE, action)); return processAction(action); } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java index 23bacd5b945..c3cd6764afc 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java @@ -166,6 +166,50 @@ public class FormTagTests extends AbstractHtmlElementTagTests { assertAttributeNotPresent(output, "name"); } + public void testPrependServletPath() throws Exception { + + this.request.setContextPath("/myApp"); + this.request.setServletPath("/main"); + this.request.setPathInfo("/index.html"); + + String commandName = "myCommand"; + String action = "/form.html"; + String enctype = "my/enctype"; + String method = "POST"; + String onsubmit = "onsubmit"; + String onreset = "onreset"; + + this.tag.setCommandName(commandName); + this.tag.setAction(action); + this.tag.setMethod(method); + this.tag.setEnctype(enctype); + this.tag.setOnsubmit(onsubmit); + this.tag.setOnreset(onreset); + + int result = this.tag.doStartTag(); + assertEquals(Tag.EVAL_BODY_INCLUDE, result); + assertEquals("Form attribute not exposed", commandName, + getPageContext().getAttribute(FormTag.MODEL_ATTRIBUTE_VARIABLE_NAME, PageContext.REQUEST_SCOPE)); + + result = this.tag.doEndTag(); + assertEquals(Tag.EVAL_PAGE, result); + + this.tag.doFinally(); + assertNull("Form attribute not cleared after tag ends", + getPageContext().getAttribute(FormTag.MODEL_ATTRIBUTE_VARIABLE_NAME, PageContext.REQUEST_SCOPE)); + + String output = getOutput(); + assertFormTagOpened(output); + assertFormTagClosed(output); + + assertContainsAttribute(output, "action", "/myApp/main/form.html"); + assertContainsAttribute(output, "method", method); + assertContainsAttribute(output, "enctype", enctype); + assertContainsAttribute(output, "onsubmit", onsubmit); + assertContainsAttribute(output, "onreset", onreset); + assertAttributeNotPresent(output, "name"); + } + public void testWithNullResolvedCommand() throws Exception { try { tag.setCommandName("${null}"); From a16bad04f0939f04210f937fbff8fb620951fb90 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 14 Jan 2013 21:04:18 -0500 Subject: [PATCH 054/143] Update Validation chapter The Validation chapter now includes information on combining JSR-303 Bean Validation with additional Spring Validator's that don't require the use of annotations. Issue: SPR-9437 --- src/reference/docbook/validation.xml | 42 ++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/reference/docbook/validation.xml b/src/reference/docbook/validation.xml index 5ad3faeee8a..2eb98a76b02 100644 --- a/src/reference/docbook/validation.xml +++ b/src/reference/docbook/validation.xml @@ -12,6 +12,24 @@

Introduction + + JSR-303 Bean Validation + + The Spring Framework supports JSR-303 Bean Validation adapting + it to Spring's Validator interface. + + An application can choose to enable JSR-303 Bean Validation once globally, + as described in , and use it + exclusively for all validation needs. + + An application can also register + additional Spring Validator instances + per DataBinder instance, as described in + . This may be useful for + plugging in validation logic without the use of annotations. + + + There are pros and cons for considering validation as business logic, and Spring offers a design for validation (and data binding) that does not exclude either one of them. Specifically validation should not be tied to @@ -1778,6 +1796,16 @@ binder.validate(); // get BindingResult that includes any validation errors BindingResult results = binder.getBindingResult(); + + A DataBinder can also be configured with multiple + Validator instances + via dataBinder.addValidators + and dataBinder.replaceValidators. + This is useful when combining globally configured JSR-303 Bean Validation + with a Spring Validator configured + locally on a DataBinder instance. + See . +
@@ -1847,6 +1875,20 @@ public class MyController { ]]> + + To combine a global and a local validator, configure the + global validator as shown above and then add a local validator: + + +
From 9dc7b5feef01272af0dc563248a5e60e6ada9be3 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 14 Jan 2013 21:25:11 -0500 Subject: [PATCH 055/143] Use MessageSource to resolve @ResponseStatus.reason The reason attribute of @ResponseStatus can now be a code resolvable through the ApplicationContext's MessageSource. Issue: SPR-6044 --- .../ResponseStatusExceptionResolver.java | 18 +++++++++-- .../ResponseStatusExceptionResolverTests.java | 32 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolver.java index 0d58e8c134c..3e5fd736592 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,6 +19,9 @@ package org.springframework.web.servlet.mvc.annotation; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.springframework.context.MessageSource; +import org.springframework.context.MessageSourceAware; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.ResponseStatus; @@ -32,9 +35,17 @@ import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver; *

This exception resolver is enabled by default in the {@link org.springframework.web.servlet.DispatcherServlet}. * * @author Arjen Poutsma + * @author Rossen Stoyanchev * @since 3.0 */ -public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver { +public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver implements MessageSourceAware { + + private MessageSource messageSource; + + + public void setMessageSource(MessageSource messageSource) { + this.messageSource = messageSource; + } @Override protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @@ -69,6 +80,9 @@ public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionRes int statusCode = responseStatus.value().value(); String reason = responseStatus.reason(); + if (this.messageSource != null) { + reason = this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale()); + } if (!StringUtils.hasLength(reason)) { response.sendError(statusCode); } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolverTests.java index 684550e18d3..25348f53f7a 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolverTests.java @@ -1,13 +1,20 @@ package org.springframework.web.servlet.mvc.annotation; import static org.junit.Assert.*; + +import java.util.Locale; + import org.junit.Before; import org.junit.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.context.support.StaticMessageSource; import org.springframework.http.HttpStatus; import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.mock.web.test.MockHttpServletResponse; import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.context.support.StaticWebApplicationContext; import org.springframework.web.servlet.ModelAndView; /** @author Arjen Poutsma */ @@ -46,6 +53,24 @@ public class ResponseStatusExceptionResolverTests { assertEquals("Invalid status reason", "You suck!", response.getErrorMessage()); } + @Test + public void statusCodeAndReasonMessage() { + Locale locale = Locale.CHINESE; + LocaleContextHolder.setLocale(locale); + try { + StaticMessageSource messageSource = new StaticMessageSource(); + messageSource.addMessage("gone.reason", locale, "Gone reason message"); + exceptionResolver.setMessageSource(messageSource); + + StatusCodeAndReasonMessageException ex = new StatusCodeAndReasonMessageException(); + ModelAndView mav = exceptionResolver.resolveException(request, response, null, ex); + assertEquals("Invalid status reason", "Gone reason message", response.getErrorMessage()); + } + finally { + LocaleContextHolder.resetLocaleContext(); + } + } + @Test public void notAnnotated() { Exception ex = new Exception(); @@ -65,4 +90,11 @@ public class ResponseStatusExceptionResolverTests { private static class StatusCodeAndReasonException extends Exception { } + + @ResponseStatus(value = HttpStatus.GONE, reason = "gone.reason") + @SuppressWarnings("serial") + private static class StatusCodeAndReasonMessageException extends Exception { + + } + } From b2d6596901bc782a96685a0778d0da848e5755b6 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 14 Jan 2013 21:38:51 -0500 Subject: [PATCH 056/143] Add contentTypeCompatibleWith option to Spring MVC Test An expectation such as content().contentType(MediaType.TEXT_PLAIN) fails if the actual media type contains a charset or another parameter. A new method allows comparing the media type and subtype only via content().contentTypeCompatibleWith(MediaType.TEXT_PLAIN). Issue: SPR-10165 --- .../client/match/ContentRequestMatchers.java | 27 +++++++++++++-- .../servlet/result/ContentResultMatchers.java | 33 +++++++++++++++++-- .../resultmatchers/ContentAssertionTests.java | 6 ++-- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/spring-test-mvc/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java b/spring-test-mvc/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java index 5b89cef1a7a..1bcb49a5b99 100644 --- a/spring-test-mvc/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java +++ b/spring-test-mvc/src/main/java/org/springframework/test/web/client/match/ContentRequestMatchers.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,8 +16,8 @@ package org.springframework.test.web.client.match; import static org.springframework.test.util.AssertionErrors.assertEquals; -import static org.springframework.test.util.MatcherAssertionErrors.assertThat; import static org.springframework.test.util.AssertionErrors.assertTrue; +import static org.springframework.test.util.MatcherAssertionErrors.assertThat; import java.io.IOException; @@ -72,6 +72,29 @@ public class ContentRequestMatchers { }; } + /** + * Assert the request content type is compatible with the given + * content type as defined by {@link MediaType#isCompatibleWith(MediaType)}. + */ + public RequestMatcher contentTypeCompatibleWith(String contentType) { + return contentTypeCompatibleWith(MediaType.parseMediaType(contentType)); + } + + /** + * Assert the request content type is compatible with the given + * content type as defined by {@link MediaType#isCompatibleWith(MediaType)}. + */ + public RequestMatcher contentTypeCompatibleWith(final MediaType contentType) { + return new RequestMatcher() { + public void match(ClientHttpRequest request) throws IOException, AssertionError { + MediaType actualContentType = request.getHeaders().getContentType(); + assertTrue("Content type not set", actualContentType != null); + assertTrue("Content type [" + actualContentType + "] is not compatible with [" + contentType + "]", + actualContentType.isCompatibleWith(contentType)); + } + }; + } + /** * Get the body of the request as a UTF-8 string and appply the given {@link Matcher}. */ diff --git a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/result/ContentResultMatchers.java b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/result/ContentResultMatchers.java index b39875baaa5..c71dc98b7fd 100644 --- a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/result/ContentResultMatchers.java +++ b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/result/ContentResultMatchers.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -54,7 +54,9 @@ public class ContentResultMatchers { } /** - * Assert the ServletResponse content type. + * Assert the ServletResponse content type. The given content type must + * fully match including type, sub-type, and parameters. For checking + * only the type and sub-type see {@link #contentTypeCompatibleWith(String)}. */ public ResultMatcher contentType(String contentType) { return contentType(MediaType.parseMediaType(contentType)); @@ -62,6 +64,9 @@ public class ContentResultMatchers { /** * Assert the ServletResponse content type after parsing it as a MediaType. + * The given content type must fully match including type, sub-type, and + * parameters. For checking only the type and sub-type see + * {@link #contentTypeCompatibleWith(MediaType)}. */ public ResultMatcher contentType(final MediaType contentType) { return new ResultMatcher() { @@ -73,6 +78,30 @@ public class ContentResultMatchers { }; } + /** + * Assert the ServletResponse content type is compatible with the given + * content type as defined by {@link MediaType#isCompatibleWith(MediaType)}. + */ + public ResultMatcher contentTypeCompatibleWith(String contentType) { + return contentTypeCompatibleWith(MediaType.parseMediaType(contentType)); + } + + /** + * Assert the ServletResponse content type is compatible with the given + * content type as defined by {@link MediaType#isCompatibleWith(MediaType)}. + */ + public ResultMatcher contentTypeCompatibleWith(final MediaType contentType) { + return new ResultMatcher() { + public void match(MvcResult result) throws Exception { + String actual = result.getResponse().getContentType(); + assertTrue("Content type not set", actual != null); + MediaType actualContentType = MediaType.parseMediaType(actual); + assertTrue("Content type [" + actual + "] is not compatible with [" + contentType + "]", + actualContentType.isCompatibleWith(contentType)); + } + }; + } + /** * Assert the character encoding in the ServletResponse. * @see HttpServletResponse#getCharacterEncoding() diff --git a/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/ContentAssertionTests.java b/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/ContentAssertionTests.java index 80a0d138f34..4777e5e5e44 100644 --- a/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/ContentAssertionTests.java +++ b/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/ContentAssertionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -62,7 +62,9 @@ public class ContentAssertionTests { this.mockMvc.perform(get("/handleUtf8")) .andExpect(content().contentType(MediaType.valueOf("text/plain;charset=UTF-8"))) - .andExpect(content().contentType("text/plain;charset=UTF-8")); + .andExpect(content().contentType("text/plain;charset=UTF-8")) + .andExpect(content().contentTypeCompatibleWith("text/plan")) + .andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_PLAIN)); } @Test From 33ee0ea4a6eb4a47e4a4291e36124ec631a2f814 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 15 Jan 2013 11:56:27 +0100 Subject: [PATCH 057/143] Fix broken test in ContentAssertionTests --- .../standalone/resultmatchers/ContentAssertionTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/ContentAssertionTests.java b/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/ContentAssertionTests.java index 4777e5e5e44..568f765564a 100644 --- a/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/ContentAssertionTests.java +++ b/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/ContentAssertionTests.java @@ -63,7 +63,7 @@ public class ContentAssertionTests { this.mockMvc.perform(get("/handleUtf8")) .andExpect(content().contentType(MediaType.valueOf("text/plain;charset=UTF-8"))) .andExpect(content().contentType("text/plain;charset=UTF-8")) - .andExpect(content().contentTypeCompatibleWith("text/plan")) + .andExpect(content().contentTypeCompatibleWith("text/plain")) .andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_PLAIN)); } From f3e5f8bb4ee3c6066d7078f7d752d6ad7b85104c Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 15 Jan 2013 12:11:44 +0100 Subject: [PATCH 058/143] Update copyright year, author tags, and Javadoc --- .../web/servlet/support/RequestContext.java | 68 +++++++++++-------- .../web/servlet/tags/form/FormTag.java | 2 +- .../web/servlet/tags/form/FormTagTests.java | 1 + 3 files changed, 42 insertions(+), 29 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/RequestContext.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/RequestContext.java index cf63a425c33..cf33995355d 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/RequestContext.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/RequestContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -62,6 +62,7 @@ import org.springframework.web.util.WebUtils; * appropriate fallback for the locale (the HttpServletRequest's primary locale). * * @author Juergen Hoeller + * @author Rossen Stoyanchev * @since 03.03.2003 * @see org.springframework.web.servlet.DispatcherServlet * @see org.springframework.web.servlet.view.AbstractView#setRequestContextAttribute @@ -234,8 +235,10 @@ public class RequestContext { } /** - * Determine the fallback locale for this context.

The default implementation checks for a JSTL locale attribute - * in request, session or application scope; if not found, returns the {@code HttpServletRequest.getLocale()}. + * Determine the fallback locale for this context. + *

The default implementation checks for a JSTL locale attribute in request, + * session or application scope; if not found, returns the + * {@code HttpServletRequest.getLocale()}. * @return the fallback locale (never {@code null}) * @see javax.servlet.http.HttpServletRequest#getLocale() */ @@ -250,8 +253,8 @@ public class RequestContext { } /** - * Determine the fallback theme for this context.

The default implementation returns the default theme (with name - * "theme"). + * Determine the fallback theme for this context. + *

The default implementation returns the default theme (with name "theme"). * @return the fallback theme (never {@code null}) */ protected Theme getFallbackTheme() { @@ -310,8 +313,8 @@ public class RequestContext { } /** - * Return the current theme (never {@code null}).

Resolved lazily for more efficiency when theme support is - * not being used. + * Return the current theme (never {@code null}). + *

Resolved lazily for more efficiency when theme support is not being used. */ public final Theme getTheme() { if (this.theme == null) { @@ -351,7 +354,8 @@ public class RequestContext { /** * Set the UrlPathHelper to use for context path and request URI decoding. Can be used to pass a shared - * UrlPathHelper instance in.

A default UrlPathHelper is always available. + * UrlPathHelper instance in. + *

A default UrlPathHelper is always available. */ public void setUrlPathHelper(UrlPathHelper urlPathHelper) { Assert.notNull(urlPathHelper, "UrlPathHelper must not be null"); @@ -360,7 +364,8 @@ public class RequestContext { /** * Return the UrlPathHelper used for context path and request URI decoding. Can be used to configure the current - * UrlPathHelper.

A default UrlPathHelper is always available. + * UrlPathHelper. + *

A default UrlPathHelper is always available. */ public UrlPathHelper getUrlPathHelper() { return this.urlPathHelper; @@ -377,8 +382,8 @@ public class RequestContext { /** * Return the context path of the original request, that is, the path that indicates the current web application. - * This is useful for building links to other resources within the application.

Delegates to the UrlPathHelper - * for decoding. + * This is useful for building links to other resources within the application. + *

Delegates to the UrlPathHelper for decoding. * @see javax.servlet.http.HttpServletRequest#getContextPath * @see #getUrlPathHelper */ @@ -423,7 +428,7 @@ public class RequestContext { * context path and the servlet path of the original request. This is useful * for building links to other resources within the application where a * servlet mapping of the style {@code "/main/*"} is used. - * Delegates to the UrlPathHelper to determine the context and servlet path. + *

Delegates to the UrlPathHelper to determine the context and servlet path. */ public String getPathToServlet() { String path = this.urlPathHelper.getOriginatingContextPath(this.request); @@ -437,8 +442,8 @@ public class RequestContext { * Return the request URI of the original request, that is, the invoked URL without parameters. This is particularly * useful as HTML form action target, possibly in combination with the original query string.

Note this * implementation will correctly resolve to the URI of any originating root request in the presence of a forwarded - * request. However, this can only work when the Servlet 2.4 'forward' request attributes are present.

Delegates - * to the UrlPathHelper for decoding. + * request. However, this can only work when the Servlet 2.4 'forward' request attributes are present. + *

Delegates to the UrlPathHelper for decoding. * @see #getQueryString * @see org.springframework.web.util.UrlPathHelper#getOriginatingRequestUri * @see #getUrlPathHelper @@ -574,8 +579,9 @@ public class RequestContext { } /** - * Retrieve the theme message for the given code.

Note that theme messages are never HTML-escaped, as they - * typically denote theme-specific resource paths and not client-visible messages. + * Retrieve the theme message for the given code. + *

Note that theme messages are never HTML-escaped, as they typically denote + * theme-specific resource paths and not client-visible messages. * @param code code of the message * @param defaultMessage String to return if the lookup fails * @return the message @@ -585,8 +591,9 @@ public class RequestContext { } /** - * Retrieve the theme message for the given code.

Note that theme messages are never HTML-escaped, as they - * typically denote theme-specific resource paths and not client-visible messages. + * Retrieve the theme message for the given code. + *

Note that theme messages are never HTML-escaped, as they typically denote + * theme-specific resource paths and not client-visible messages. * @param code code of the message * @param args arguments for the message, or {@code null} if none * @param defaultMessage String to return if the lookup fails @@ -597,8 +604,9 @@ public class RequestContext { } /** - * Retrieve the theme message for the given code.

Note that theme messages are never HTML-escaped, as they - * typically denote theme-specific resource paths and not client-visible messages. + * Retrieve the theme message for the given code. + *

Note that theme messages are never HTML-escaped, as they typically denote + * theme-specific resource paths and not client-visible messages. * @param code code of the message * @param args arguments for the message as a List, or {@code null} if none * @param defaultMessage String to return if the lookup fails @@ -610,8 +618,9 @@ public class RequestContext { } /** - * Retrieve the theme message for the given code.

Note that theme messages are never HTML-escaped, as they - * typically denote theme-specific resource paths and not client-visible messages. + * Retrieve the theme message for the given code. + *

Note that theme messages are never HTML-escaped, as they typically denote + * theme-specific resource paths and not client-visible messages. * @param code code of the message * @return the message * @throws org.springframework.context.NoSuchMessageException if not found @@ -621,8 +630,9 @@ public class RequestContext { } /** - * Retrieve the theme message for the given code.

Note that theme messages are never HTML-escaped, as they - * typically denote theme-specific resource paths and not client-visible messages. + * Retrieve the theme message for the given code. + *

Note that theme messages are never HTML-escaped, as they typically denote + * theme-specific resource paths and not client-visible messages. * @param code code of the message * @param args arguments for the message, or {@code null} if none * @return the message @@ -633,8 +643,9 @@ public class RequestContext { } /** - * Retrieve the theme message for the given code.

Note that theme messages are never HTML-escaped, as they - * typically denote theme-specific resource paths and not client-visible messages. + * Retrieve the theme message for the given code. + *

Note that theme messages are never HTML-escaped, as they typically denote + * theme-specific resource paths and not client-visible messages. * @param code code of the message * @param args arguments for the message as a List, or {@code null} if none * @return the message @@ -645,8 +656,9 @@ public class RequestContext { } /** - * Retrieve the given MessageSourceResolvable in the current theme.

Note that theme messages are never - * HTML-escaped, as they typically denote theme-specific resource paths and not client-visible messages. + * Retrieve the given MessageSourceResolvable in the current theme. + *

Note that theme messages are never HTML-escaped, as they typically denote + * theme-specific resource paths and not client-visible messages. * @param resolvable the MessageSourceResolvable * @return the message * @throws org.springframework.context.NoSuchMessageException if not found diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java index 120b910727f..1af5e3972f5 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java @@ -32,7 +32,6 @@ import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.servlet.support.RequestDataValueProcessor; import org.springframework.web.util.HtmlUtils; -import org.springframework.web.util.UrlPathHelper; /** * Databinding-aware JSP tag for rendering an HTML '{@code form}' whose @@ -51,6 +50,7 @@ import org.springframework.web.util.UrlPathHelper; * @author Rob Harrop * @author Juergen Hoeller * @author Scott Andrews + * @author Rossen Stoyanchev * @since 2.0 * @see org.springframework.web.servlet.mvc.SimpleFormController */ diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java index c3cd6764afc..3f4247033b5 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java @@ -34,6 +34,7 @@ import org.springframework.web.servlet.support.RequestDataValueProcessor; * @author Juergen Hoeller * @author Scott Andrews * @author Jeremy Grelle + * @author Rossen Stoyanchev */ public class FormTagTests extends AbstractHtmlElementTagTests { From a3211782a6f82d589e226732a38dcd7117f103d6 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 15 Jan 2013 15:54:14 +0100 Subject: [PATCH 059/143] AbstractCacheManager accepts no caches defined, allowing for EHCache default cache setup Issue: SPR-7955 --- .../cache/support/AbstractCacheManager.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/cache/support/AbstractCacheManager.java b/spring-context/src/main/java/org/springframework/cache/support/AbstractCacheManager.java index 5d301d85f4c..29a5a98973b 100644 --- a/spring-context/src/main/java/org/springframework/cache/support/AbstractCacheManager.java +++ b/spring-context/src/main/java/org/springframework/cache/support/AbstractCacheManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -26,7 +26,6 @@ import java.util.concurrent.ConcurrentMap; import org.springframework.beans.factory.InitializingBean; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; -import org.springframework.util.Assert; /** * Abstract base class implementing the common {@link CacheManager} methods. @@ -45,10 +44,10 @@ public abstract class AbstractCacheManager implements CacheManager, Initializing public void afterPropertiesSet() { Collection caches = loadCaches(); - Assert.notEmpty(caches, "loadCaches must not return an empty Collection"); - this.cacheMap.clear(); // preserve the initial order of the cache names + this.cacheMap.clear(); + this.cacheNames.clear(); for (Cache cache : caches) { this.cacheMap.put(cache.getName(), decorateCache(cache)); this.cacheNames.add(cache.getName()); From abbe1db1065dd62fee790a33aae288d3e2a17a18 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 15 Jan 2013 16:52:09 +0100 Subject: [PATCH 060/143] Polishing along with backport --- .../spel/support/ReflectivePropertyAccessor.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java index 4c31c3f6b26..6c5b249114d 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java @@ -287,7 +287,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { private Method findGetterForProperty(String propertyName, Class clazz, Object target) { Method method = findGetterForProperty(propertyName, clazz, target instanceof Class); - if(method == null && target instanceof Class) { + if (method == null && target instanceof Class) { method = findGetterForProperty(propertyName, target.getClass(), false); } return method; @@ -295,7 +295,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { private Method findSetterForProperty(String propertyName, Class clazz, Object target) { Method method = findSetterForProperty(propertyName, clazz, target instanceof Class); - if(method == null && target instanceof Class) { + if (method == null && target instanceof Class) { method = findSetterForProperty(propertyName, target.getClass(), false); } return method; @@ -303,7 +303,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { private Field findField(String name, Class clazz, Object target) { Field field = findField(name, clazz, target instanceof Class); - if(field == null && target instanceof Class) { + if (field == null && target instanceof Class) { field = findField(name, target.getClass(), false); } return field; @@ -383,15 +383,17 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { return field; } } - if(clazz.getSuperclass() != null) { + // We'll search superclasses and implemented interfaces explicitly, + // although it shouldn't be necessary - however, see SPR-10125. + if (clazz.getSuperclass() != null) { Field field = findField(name, clazz.getSuperclass(), mustBeStatic); - if(field != null) { + if (field != null) { return field; } } for (Class implementedInterface : clazz.getInterfaces()) { Field field = findField(name, implementedInterface, mustBeStatic); - if(field != null) { + if (field != null) { return field; } } From 450dbb2810f40f55628df74fc5adb887ef8d657b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 15 Jan 2013 22:05:21 +0100 Subject: [PATCH 061/143] Reintroduced "mode" and "proxy-target-class" attributes in spring-task-3.1/3.2.xsd Issue: SPR-10177 --- .../scheduling/config/spring-task-3.1.xsd | 33 +++++++++++++++++++ .../scheduling/config/spring-task-3.2.xsd | 33 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/spring-context/src/main/resources/org/springframework/scheduling/config/spring-task-3.1.xsd b/spring-context/src/main/resources/org/springframework/scheduling/config/spring-task-3.1.xsd index 91b49de3275..56680707c22 100644 --- a/spring-context/src/main/resources/org/springframework/scheduling/config/spring-task-3.1.xsd +++ b/spring-context/src/main/resources/org/springframework/scheduling/config/spring-task-3.1.xsd @@ -52,6 +52,39 @@ ]]> + + + + + + + + + + + + + + + + diff --git a/spring-context/src/main/resources/org/springframework/scheduling/config/spring-task-3.2.xsd b/spring-context/src/main/resources/org/springframework/scheduling/config/spring-task-3.2.xsd index 34b479d12cb..eb23e55017b 100644 --- a/spring-context/src/main/resources/org/springframework/scheduling/config/spring-task-3.2.xsd +++ b/spring-context/src/main/resources/org/springframework/scheduling/config/spring-task-3.2.xsd @@ -52,6 +52,39 @@ ]]> + + + + + + + + + + + + + + + + From 2cd23a79a3a04b5bc003df57c0f89f7cae4968a9 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 15 Jan 2013 22:06:48 +0100 Subject: [PATCH 062/143] Polishing --- .../annotation/AnnotationScopeMetadataResolver.java | 8 ++++---- .../org/springframework/core/io/ClassPathResource.java | 10 +++------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java index f678425ea84..6a7db8bd313 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,9 +39,10 @@ import static org.springframework.context.annotation.MetadataUtils.*; */ public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver { + private final ScopedProxyMode defaultProxyMode; + protected Class scopeAnnotationType = Scope.class; - private final ScopedProxyMode defaultProxyMode; /** * Create a new instance of the {@code AnnotationScopeMetadataResolver} class. @@ -77,8 +78,7 @@ public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver { ScopeMetadata metadata = new ScopeMetadata(); if (definition instanceof AnnotatedBeanDefinition) { AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition; - AnnotationAttributes attributes = - attributesFor(annDef.getMetadata(), this.scopeAnnotationType); + AnnotationAttributes attributes = attributesFor(annDef.getMetadata(), this.scopeAnnotationType); if (attributes != null) { metadata.setScopeName(attributes.getString("value")); ScopedProxyMode proxyMode = attributes.getEnum("proxyMode"); diff --git a/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java b/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java index e881d4034c3..3e8467178d3 100644 --- a/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java @@ -205,18 +205,14 @@ public class ClassPathResource extends AbstractFileResolvingResource { */ public String getDescription() { StringBuilder builder = new StringBuilder("class path resource ["); - String pathToUse = path; - if (this.clazz != null && !pathToUse.startsWith("/")) { builder.append(ClassUtils.classPackageAsResourcePath(this.clazz)); builder.append('/'); } - if (pathToUse.startsWith("/")) { pathToUse = pathToUse.substring(1); } - builder.append(pathToUse); builder.append(']'); return builder.toString(); @@ -232,9 +228,9 @@ public class ClassPathResource extends AbstractFileResolvingResource { } if (obj instanceof ClassPathResource) { ClassPathResource otherRes = (ClassPathResource) obj; - return (this.path.equals(otherRes.path) - && ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) && ObjectUtils.nullSafeEquals( - this.clazz, otherRes.clazz)); + return (this.path.equals(otherRes.path) && + ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) && + ObjectUtils.nullSafeEquals(this.clazz, otherRes.clazz)); } return false; } From e44b4b831e198651316068a35730f7c861e4a4cd Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 15 Jan 2013 22:10:14 +0100 Subject: [PATCH 063/143] Further fixes for 3.2.1 --- src/dist/changelog.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/dist/changelog.txt b/src/dist/changelog.txt index f81937f9b5b..6f53ecc7317 100644 --- a/src/dist/changelog.txt +++ b/src/dist/changelog.txt @@ -6,13 +6,17 @@ http://www.springsource.org Changes in version 3.2.1 (2013-01-24) -------------------------------------- +* SpEL support for static finals on interfaces (SPR-10125) * AnnotationAwareOrderComparator is able to sort Class objects as well (SPR-10152) * added dedicated sort method to AnnotationAwareOrderComparator (SPR-9625) * fixed QualifierAnnotationAutowireCandidateResolver's detection of custom qualifier annotations (SPR-10107) * fixed AbstractAutoProxyCreator to accept null bean names again (SPR-10108) * AbstractAdvisingBeanPostProcessor caches per bean target class, working for null bean names as well (SPR-10144) * MessageSourceResourceBundle overrides JDK 1.6 containsKey method, avoiding NPE in getKeys (SPR-10136) +* AbstractCacheManager accepts no caches defined, allowing for EHCache default cache setup (SPR-7955) * spring-task-3.2.xsd allows for SpEL expressions in initial-delay attribute (SPR-10102) +* reintroduced "mode" and "proxy-target-class" attributes in spring-task-3.1/3.2.xsd (SPR-10177) +* JDBC parameter binding uses JDBC 3.0 ParameterMetaData (if available) for type determination (SPR-10084) * JmsTemplate uses configured receiveTimeout if shorter than remaining transaction timeout (SPR-10109) * added MappingJackson2MessageConverter for JMS (SPR-10099) * MimeMessageHelper encodes attachment filename if not ASCII compliant (SPR-9258) From 6888a6f2867d6264fce832d8ae00205988ed5a94 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Tue, 15 Jan 2013 15:29:28 -0600 Subject: [PATCH 064/143] Restore *.aj whitespace The removal of whitespace to the *.aj files made in 1762157 cause NoSuchMethodError for code compiled against previous versions of spring-aspects due to a bug in AspectJ (see SPR-10178 for details). This commit reverts all the whitespace changes made in 1762157 which resolves the NoSuchMethodErrors. Issue: SPR-10178 --- .../aspectj/AbstractBeanConfigurerAspect.aj | 12 ++-- .../AbstractDependencyInjectionAspect.aj | 36 +++++----- ...nterfaceDrivenDependencyInjectionAspect.aj | 68 +++++++++---------- .../aspectj/AnnotationBeanConfigurerAspect.aj | 12 ++-- ...nterfaceDrivenDependencyInjectionAspect.aj | 38 +++++------ .../cache/aspectj/AbstractCacheAspect.aj | 4 +- .../cache/aspectj/AnnotationCacheAspect.aj | 2 +- .../AbstractMethodMockingControl.aj | 26 +++---- ...otationDrivenStaticEntityMockingControl.aj | 16 ++--- .../aspectj/JpaExceptionTranslatorAspect.aj | 38 +++-------- .../aspectj/AbstractAsyncExecutionAspect.aj | 4 +- .../aspectj/AbstractTransactionAspect.aj | 6 +- .../aspectj/AnnotationTransactionAspect.aj | 12 ++-- 13 files changed, 129 insertions(+), 145 deletions(-) diff --git a/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AbstractBeanConfigurerAspect.aj b/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AbstractBeanConfigurerAspect.aj index 760cea9dc1e..85342e838b8 100644 --- a/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AbstractBeanConfigurerAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AbstractBeanConfigurerAspect.aj @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + package org.springframework.beans.factory.aspectj; import org.aspectj.lang.annotation.SuppressAjWarnings; @@ -23,12 +23,12 @@ import org.springframework.beans.factory.wiring.BeanConfigurerSupport; * Abstract superaspect for AspectJ aspects that can perform Dependency * Injection on objects, however they may be created. Define the beanCreation() * pointcut in subaspects. - * + * *

Subaspects may also need a metadata resolution strategy, in the - * {@code BeanWiringInfoResolver} interface. The default implementation + * BeanWiringInfoResolver interface. The default implementation * looks for a bean with the same name as the FQN. This is the default name * of a bean in a Spring container if the id value is not supplied explicitly. - * + * * @author Rob Harrop * @author Rod Johnson * @author Adrian Colyer @@ -62,7 +62,7 @@ public abstract aspect AbstractBeanConfigurerAspect extends BeanConfigurerSuppor /** * The initialization of a new object. - * + * *

WARNING: Although this pointcut is non-abstract for backwards * compatibility reasons, it is meant to be overridden to select * initialization of any configurable bean. diff --git a/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AbstractDependencyInjectionAspect.aj b/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AbstractDependencyInjectionAspect.aj index 390e1dc989c..23b012ecc75 100644 --- a/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AbstractDependencyInjectionAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AbstractDependencyInjectionAspect.aj @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,7 +21,7 @@ import org.aspectj.lang.annotation.SuppressAjWarnings; /** * Abstract base aspect that can perform Dependency * Injection on objects, however they may be created. - * + * * @author Ramnivas Laddad * @since 2.5.2 */ @@ -29,26 +29,26 @@ public abstract aspect AbstractDependencyInjectionAspect { /** * Select construction join points for objects to inject dependencies */ - public abstract pointcut beanConstruction(Object bean); + public abstract pointcut beanConstruction(Object bean); /** * Select deserialization join points for objects to inject dependencies */ public abstract pointcut beanDeserialization(Object bean); - + /** * Select join points in a configurable bean */ public abstract pointcut inConfigurableBean(); - + /** * Select join points in beans to be configured prior to construction? * By default, use post-construction injection matching the default in the Configurable annotation. */ public pointcut preConstructionConfiguration() : if(false); - + /** - * Select the most-specific initialization join point + * Select the most-specific initialization join point * (most concrete class) for the initialization of an instance. */ public pointcut mostSpecificSubTypeConstruction() : @@ -58,25 +58,25 @@ public abstract aspect AbstractDependencyInjectionAspect { * Select least specific super type that is marked for DI (so that injection occurs only once with pre-construction inejection */ public abstract pointcut leastSpecificSuperTypeConstruction(); - + /** * Configure the bean */ public abstract void configureBean(Object bean); - - private pointcut preConstructionCondition() : + + private pointcut preConstructionCondition() : leastSpecificSuperTypeConstruction() && preConstructionConfiguration(); - + private pointcut postConstructionCondition() : mostSpecificSubTypeConstruction() && !preConstructionConfiguration(); - + /** * Pre-construction configuration. */ @SuppressAjWarnings("adviceDidNotMatch") - before(Object bean) : - beanConstruction(bean) && preConstructionCondition() && inConfigurableBean() { + before(Object bean) : + beanConstruction(bean) && preConstructionCondition() && inConfigurableBean() { configureBean(bean); } @@ -84,18 +84,18 @@ public abstract aspect AbstractDependencyInjectionAspect { * Post-construction configuration. */ @SuppressAjWarnings("adviceDidNotMatch") - after(Object bean) returning : + after(Object bean) returning : beanConstruction(bean) && postConstructionCondition() && inConfigurableBean() { configureBean(bean); } - + /** * Post-deserialization configuration. */ @SuppressAjWarnings("adviceDidNotMatch") - after(Object bean) returning : + after(Object bean) returning : beanDeserialization(bean) && inConfigurableBean() { configureBean(bean); } - + } diff --git a/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AbstractInterfaceDrivenDependencyInjectionAspect.aj b/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AbstractInterfaceDrivenDependencyInjectionAspect.aj index 5cd0140d464..8e8b634ef0f 100644 --- a/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AbstractInterfaceDrivenDependencyInjectionAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AbstractInterfaceDrivenDependencyInjectionAspect.aj @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -26,36 +26,36 @@ import java.io.Serializable; * upon deserialization. Subaspects need to simply provide definition for the configureBean() method. This * method may be implemented without relying on Spring container if so desired. *

- *

+ *

* There are two cases that needs to be handled: *

    - *
  1. Normal object creation via the '{@code new}' operator: this is - * taken care of by advising {@code initialization()} join points.
  2. + *
  3. Normal object creation via the 'new' operator: this is + * taken care of by advising initialization() join points.
  4. *
  5. Object creation through deserialization: since no constructor is * invoked during deserialization, the aspect needs to advise a method that a * deserialization mechanism is going to invoke. Ideally, we should not * require user classes to implement any specific method. This implies that * we need to introduce the chosen method. We should also handle the cases * where the chosen method is already implemented in classes (in which case, - * the user's implementation for that method should take precedence over the + * the user's implementation for that method should take precedence over the * introduced implementation). There are a few choices for the chosen method: *
      *
    • readObject(ObjectOutputStream): Java requires that the method must be - * {@code private}. Since aspects cannot introduce a private member, + * private

      . Since aspects cannot introduce a private member, * while preserving its name, this option is ruled out.
    • - *
    • readResolve(): Java doesn't pose any restriction on an access specifier. - * Problem solved! There is one (minor) limitation of this approach in - * that if a user class already has this method, that method must be - * {@code public}. However, this shouldn't be a big burden, since - * use cases that need classes to implement readResolve() (custom enums, + *
    • readResolve(): Java doesn't pose any restriction on an access specifier. + * Problem solved! There is one (minor) limitation of this approach in + * that if a user class already has this method, that method must be + * public. However, this shouldn't be a big burden, since + * use cases that need classes to implement readResolve() (custom enums, * for example) are unlikely to be marked as @Configurable, and - * in any case asking to make that method {@code public} should not + * in any case asking to make that method public should not * pose any undue burden.
    • *
    - * The minor collaboration needed by user classes (i.e., that the - * implementation of {@code readResolve()}, if any, must be - * {@code public}) can be lifted as well if we were to use an - * experimental feature in AspectJ - the {@code hasmethod()} PCD.
  6. + * The minor collaboration needed by user classes (i.e., that the + * implementation of readResolve(), if any, must be + * public) can be lifted as well if we were to use an + * experimental feature in AspectJ - the hasmethod() PCD. *
*

@@ -63,7 +63,7 @@ import java.io.Serializable; * is to use a 'declare parents' statement another aspect (a subaspect of this aspect would be a logical choice) * that declares the classes that need to be configured by supplying the {@link ConfigurableObject} interface. *

- * + * * @author Ramnivas Laddad * @since 2.5.2 */ @@ -71,8 +71,8 @@ public abstract aspect AbstractInterfaceDrivenDependencyInjectionAspect extends /** * Select initialization join point as object construction */ - public pointcut beanConstruction(Object bean) : - initialization(ConfigurableObject+.new(..)) && this(bean); + public pointcut beanConstruction(Object bean) : + initialization(ConfigurableObject+.new(..)) && this(bean); /** * Select deserialization join point made available through ITDs for ConfigurableDeserializationSupport @@ -80,40 +80,40 @@ public abstract aspect AbstractInterfaceDrivenDependencyInjectionAspect extends public pointcut beanDeserialization(Object bean) : execution(Object ConfigurableDeserializationSupport+.readResolve()) && this(bean); - + public pointcut leastSpecificSuperTypeConstruction() : initialization(ConfigurableObject.new(..)); - - - + + + // Implementation to support re-injecting dependencies once an object is deserialized /** - * Declare any class implementing Serializable and ConfigurableObject as also implementing - * ConfigurableDeserializationSupport. This allows us to introduce the readResolve() + * Declare any class implementing Serializable and ConfigurableObject as also implementing + * ConfigurableDeserializationSupport. This allows us to introduce the readResolve() * method and select it with the beanDeserialization() pointcut. - * + * *

Here is an improved version that uses the hasmethod() pointcut and lifts * even the minor requirement on user classes: * *

declare parents: ConfigurableObject+ Serializable+
-	 *		            && !hasmethod(Object readResolve() throws ObjectStreamException)
+	 *		            && !hasmethod(Object readResolve() throws ObjectStreamException) 
 	 *		            implements ConfigurableDeserializationSupport;
 	 * 
*/ - declare parents: + declare parents: ConfigurableObject+ && Serializable+ implements ConfigurableDeserializationSupport; - + /** - * A marker interface to which the {@code readResolve()} is introduced. + * A marker interface to which the readResolve() is introduced. */ static interface ConfigurableDeserializationSupport extends Serializable { } - + /** - * Introduce the {@code readResolve()} method so that we can advise its + * Introduce the readResolve() method so that we can advise its * execution to configure the object. - * + * *

Note if a method with the same signature already exists in a - * {@code Serializable} class of ConfigurableObject type, + * Serializable class of ConfigurableObject type, * that implementation will take precedence (a good thing, since we are * merely interested in an opportunity to detect deserialization.) */ diff --git a/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AnnotationBeanConfigurerAspect.aj b/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AnnotationBeanConfigurerAspect.aj index 5313df9ed16..4cdc292dbbc 100644 --- a/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AnnotationBeanConfigurerAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AnnotationBeanConfigurerAspect.aj @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,7 +32,7 @@ import org.springframework.beans.factory.wiring.BeanConfigurerSupport; * annotation to identify which classes need autowiring. * *

The bean name to look up will be taken from the - * {@code @Configurable} annotation if specified, otherwise the + * @Configurable annotation if specified, otherwise the * default bean name to look up will be the FQN of the class being configured. * * @author Rod Johnson @@ -43,7 +43,7 @@ import org.springframework.beans.factory.wiring.BeanConfigurerSupport; * @see org.springframework.beans.factory.annotation.Configurable * @see org.springframework.beans.factory.annotation.AnnotationBeanWiringInfoResolver */ -public aspect AnnotationBeanConfigurerAspect +public aspect AnnotationBeanConfigurerAspect extends AbstractInterfaceDrivenDependencyInjectionAspect implements BeanFactoryAware, InitializingBean, DisposableBean { @@ -51,7 +51,7 @@ public aspect AnnotationBeanConfigurerAspect public pointcut inConfigurableBean() : @this(Configurable); - public pointcut preConstructionConfiguration() : preConstructionConfigurationSupport(*); + public pointcut preConstructionConfiguration() : preConstructionConfigurationSupport(*); declare parents: @Configurable * implements ConfigurableObject; @@ -80,10 +80,10 @@ public aspect AnnotationBeanConfigurerAspect private pointcut preConstructionConfigurationSupport(Configurable c) : @this(c) && if(c.preConstruction()); /* - * This declaration shouldn't be needed, + * This declaration shouldn't be needed, * except for an AspectJ bug (https://bugs.eclipse.org/bugs/show_bug.cgi?id=214559) */ - declare parents: @Configurable Serializable+ + declare parents: @Configurable Serializable+ implements ConfigurableDeserializationSupport; } diff --git a/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/GenericInterfaceDrivenDependencyInjectionAspect.aj b/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/GenericInterfaceDrivenDependencyInjectionAspect.aj index 351c11d7d9e..03f446ca789 100644 --- a/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/GenericInterfaceDrivenDependencyInjectionAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/GenericInterfaceDrivenDependencyInjectionAspect.aj @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,38 +17,38 @@ package org.springframework.beans.factory.aspectj; /** * Generic-based dependency injection aspect. - *

- * This aspect allows users to implement efficient, type-safe dependency injection without + *

+ * This aspect allows users to implement efficient, type-safe dependency injection without * the use of the @Configurable annotation. - * - * The subaspect of this aspect doesn't need to include any AOP constructs. - * For example, here is a subaspect that configures the {@code PricingStrategyClient} objects. + * + * The subaspect of this aspect doesn't need to include any AOP constructs. + * For example, here is a subaspect that configures the PricingStrategyClient objects. *

- * aspect PricingStrategyDependencyInjectionAspect
+ * aspect PricingStrategyDependencyInjectionAspect 
  *        extends GenericInterfaceDrivenDependencyInjectionAspect {
  *     private PricingStrategy pricingStrategy;
- *
- *     public void configure(PricingStrategyClient bean) {
- *         bean.setPricingStrategy(pricingStrategy);
- *     }
- *
- *     public void setPricingStrategy(PricingStrategy pricingStrategy) {
- *         this.pricingStrategy = pricingStrategy;
+ *     
+ *     public void configure(PricingStrategyClient bean) { 
+ *         bean.setPricingStrategy(pricingStrategy); 
  *     }
+ *     
+ *     public void setPricingStrategy(PricingStrategy pricingStrategy) { 
+ *         this.pricingStrategy = pricingStrategy; 
+ *     } 
  * }
  * 
* @author Ramnivas Laddad * @since 3.0.0 */ public abstract aspect GenericInterfaceDrivenDependencyInjectionAspect extends AbstractInterfaceDrivenDependencyInjectionAspect { - declare parents: I implements ConfigurableObject; - + declare parents: I implements ConfigurableObject; + public pointcut inConfigurableBean() : within(I+); - + public final void configureBean(Object bean) { configure((I)bean); } - - // Unfortunately, erasure used with generics won't allow to use the same named method + + // Unfortunately, erasure used with generics won't allow to use the same named method protected abstract void configure(I bean); } diff --git a/spring-aspects/src/main/java/org/springframework/cache/aspectj/AbstractCacheAspect.aj b/spring-aspects/src/main/java/org/springframework/cache/aspectj/AbstractCacheAspect.aj index a9a6da82e98..dbe15b8fb3a 100644 --- a/spring-aspects/src/main/java/org/springframework/cache/aspectj/AbstractCacheAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/cache/aspectj/AbstractCacheAspect.aj @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -62,7 +62,7 @@ public abstract aspect AbstractCacheAspect extends CacheAspectSupport { } }; - return execute(aspectJInvoker, thisJoinPoint.getTarget(), method, thisJoinPoint.getArgs()); + return execute(aspectJInvoker, thisJoinPoint.getTarget(), method, thisJoinPoint.getArgs()); } /** diff --git a/spring-aspects/src/main/java/org/springframework/cache/aspectj/AnnotationCacheAspect.aj b/spring-aspects/src/main/java/org/springframework/cache/aspectj/AnnotationCacheAspect.aj index be3957a3a08..5418488b532 100644 --- a/spring-aspects/src/main/java/org/springframework/cache/aspectj/AnnotationCacheAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/cache/aspectj/AnnotationCacheAspect.aj @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. diff --git a/spring-aspects/src/main/java/org/springframework/mock/staticmock/AbstractMethodMockingControl.aj b/spring-aspects/src/main/java/org/springframework/mock/staticmock/AbstractMethodMockingControl.aj index e373607d558..7ab3fcd0c34 100644 --- a/spring-aspects/src/main/java/org/springframework/mock/staticmock/AbstractMethodMockingControl.aj +++ b/spring-aspects/src/main/java/org/springframework/mock/staticmock/AbstractMethodMockingControl.aj @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,9 +23,9 @@ import java.util.List; /** * Abstract aspect to enable mocking of methods picked out by a pointcut. * Sub-aspects must define the mockStaticsTestMethod() pointcut to - * indicate call stacks when mocking should be triggered, and the + * indicate call stacks when mocking should be triggered, and the * methodToMock() pointcut to pick out a method invocations to mock. - * + * * @author Rod Johnson * @author Ramnivas Laddad */ @@ -42,7 +42,7 @@ public abstract aspect AbstractMethodMockingControl percflow(mockStaticsTestMeth // Represents a list of expected calls to static entity methods // Public to allow inserted code to access: is this normal?? public class Expectations { - + // Represents an expected call to a static entity method private class Call { private final String signature; @@ -50,21 +50,21 @@ public abstract aspect AbstractMethodMockingControl percflow(mockStaticsTestMeth private Object responseObject; // return value or throwable private CallResponse responseType = CallResponse.nothing; - + public Call(String name, Object[] args) { this.signature = name; this.args = args; } - + public boolean hasResponseSpecified() { return responseType != CallResponse.nothing; } - + public void setReturnVal(Object retVal) { this.responseObject = retVal; responseType = CallResponse.return_; } - + public void setThrow(Throwable throwable) { this.responseObject = throwable; responseType = CallResponse.throw_; @@ -89,7 +89,7 @@ public abstract aspect AbstractMethodMockingControl percflow(mockStaticsTestMeth } } } - + private List calls = new LinkedList(); // Calls already verified @@ -101,12 +101,12 @@ public abstract aspect AbstractMethodMockingControl percflow(mockStaticsTestMeth + " calls, received " + verified); } } - + /** * Validate the call and provide the expected return value * @param lastSig * @param args - * @return the return value + * @return */ public Object respond(String lastSig, Object[] args) { Call call = nextCall(); @@ -114,7 +114,7 @@ public abstract aspect AbstractMethodMockingControl percflow(mockStaticsTestMeth if (responseType == CallResponse.return_) { return call.returnValue(lastSig, args); } else if(responseType == CallResponse.throw_) { - return call.throwException(lastSig, args); + return (RuntimeException)call.throwException(lastSig, args); } else if(responseType == CallResponse.nothing) { // do nothing } @@ -175,7 +175,7 @@ public abstract aspect AbstractMethodMockingControl percflow(mockStaticsTestMeth return expectations.respond(thisJoinPointStaticPart.toLongString(), thisJoinPoint.getArgs()); } } - + public void expectReturnInternal(Object retVal) { if (!recording) { throw new IllegalStateException("Not recording: Cannot set return value"); diff --git a/spring-aspects/src/main/java/org/springframework/mock/staticmock/AnnotationDrivenStaticEntityMockingControl.aj b/spring-aspects/src/main/java/org/springframework/mock/staticmock/AnnotationDrivenStaticEntityMockingControl.aj index 1c50c64bbe6..031978ca91f 100644 --- a/spring-aspects/src/main/java/org/springframework/mock/staticmock/AnnotationDrivenStaticEntityMockingControl.aj +++ b/spring-aspects/src/main/java/org/springframework/mock/staticmock/AnnotationDrivenStaticEntityMockingControl.aj @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,16 +18,16 @@ package org.springframework.mock.staticmock; /** * Annotation-based aspect to use in test build to enable mocking static methods - * on JPA-annotated {@code @Entity} classes, as used by Roo for finders. + * on JPA-annotated @Entity classes, as used by Roo for finders. * - *

Mocking will occur in the call stack of any method in a class (typically a test class) - * that is annotated with the @MockStaticEntityMethods annotation. + *

Mocking will occur in the call stack of any method in a class (typically a test class) + * that is annotated with the @MockStaticEntityMethods annotation. * *

Also provides static methods to simplify the programming model for * entering playback mode and setting expected return values. * *

Usage: - *

    + *
      *
    1. Annotate a test class with @MockStaticEntityMethods. *
    2. In each test method, AnnotationDrivenStaticEntityMockingControl will begin in recording mode. * Invoke static methods on Entity classes, with each recording-mode invocation @@ -37,20 +37,20 @@ package org.springframework.mock.staticmock; *
    3. Call the code you wish to test that uses the static methods. Verification will * occur automatically. *
    - * + * * @author Rod Johnson * @author Ramnivas Laddad * @see MockStaticEntityMethods */ public aspect AnnotationDrivenStaticEntityMockingControl extends AbstractMethodMockingControl { - + /** * Stop recording mock calls and enter playback state */ public static void playback() { AnnotationDrivenStaticEntityMockingControl.aspectOf().playbackInternal(); } - + public static void expectReturn(Object retVal) { AnnotationDrivenStaticEntityMockingControl.aspectOf().expectReturnInternal(retVal); } diff --git a/spring-aspects/src/main/java/org/springframework/orm/jpa/aspectj/JpaExceptionTranslatorAspect.aj b/spring-aspects/src/main/java/org/springframework/orm/jpa/aspectj/JpaExceptionTranslatorAspect.aj index 3594725aaee..6ff44249d08 100644 --- a/spring-aspects/src/main/java/org/springframework/orm/jpa/aspectj/JpaExceptionTranslatorAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/orm/jpa/aspectj/JpaExceptionTranslatorAspect.aj @@ -1,19 +1,3 @@ -/* - * Copyright 2002-2012 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.orm.jpa.aspectj; import javax.persistence.EntityManager; @@ -25,14 +9,14 @@ import org.springframework.dao.DataAccessException; import org.springframework.orm.jpa.EntityManagerFactoryUtils; public aspect JpaExceptionTranslatorAspect { - pointcut entityManagerCall(): call(* EntityManager.*(..)) || call(* EntityManagerFactory.*(..)) || call(* EntityTransaction.*(..)) || call(* Query.*(..)); - - after() throwing(RuntimeException re): entityManagerCall() { - DataAccessException dex = EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(re); - if (dex != null) { - throw dex; - } else { - throw re; - } - } -} + pointcut entityManagerCall(): call(* EntityManager.*(..)) || call(* EntityManagerFactory.*(..)) || call(* EntityTransaction.*(..)) || call(* Query.*(..)); + + after() throwing(RuntimeException re): entityManagerCall() { + DataAccessException dex = EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(re); + if (dex != null) { + throw dex; + } else { + throw re; + } + } +} \ No newline at end of file diff --git a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj index a64def7ed11..6cb3ad60e39 100644 --- a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -28,7 +28,7 @@ import org.springframework.core.task.AsyncTaskExecutor; /** * Abstract aspect that routes selected methods asynchronously. * - *

    This aspect needs to be injected with an implementation of + *

    This aspect needs to be injected with an implementation of * {@link Executor} to activate it for a specific thread pool. * Otherwise it will simply delegate all calls synchronously. * diff --git a/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AbstractTransactionAspect.aj b/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AbstractTransactionAspect.aj index ed0956ef9fe..3ce264d9e1a 100644 --- a/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AbstractTransactionAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AbstractTransactionAspect.aj @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,7 +25,7 @@ import org.springframework.transaction.interceptor.TransactionAttributeSource; /** * Abstract superaspect for AspectJ transaction aspects. Concrete - * subaspects will implement the {@code transactionalMethodExecution()} + * subaspects will implement the transactionalMethodExecution() * pointcut using a strategy such as Java 5 annotations. * *

    Suitable for use inside or outside the Spring IoC container. @@ -66,7 +66,7 @@ public abstract aspect AbstractTransactionAspect extends TransactionAspectSuppor @SuppressAjWarnings("adviceDidNotMatch") after(Object txObject) throwing(Throwable t) : transactionalMethodExecution(txObject) { try { - completeTransactionAfterThrowing(TransactionAspectSupport.currentTransactionInfo(), t); + completeTransactionAfterThrowing(TransactionAspectSupport.currentTransactionInfo(), t); } catch (Throwable t2) { logger.error("Failed to close transaction after throwing in a transactional method", t2); diff --git a/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AnnotationTransactionAspect.aj b/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AnnotationTransactionAspect.aj index 1fe8de9030b..2ea8f9e3f58 100644 --- a/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AnnotationTransactionAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AnnotationTransactionAspect.aj @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,17 +21,17 @@ import org.springframework.transaction.annotation.Transactional; /** * Concrete AspectJ transaction aspect using Spring's @Transactional annotation. - * + * *

    When using this aspect, you must annotate the implementation class * (and/or methods within that class), not the interface (if any) that - * the class implements. AspectJ follows Java's rule that annotations on + * the class implements. AspectJ follows Java's rule that annotations on * interfaces are not inherited. * *

    An @Transactional annotation on a class specifies the default transaction * semantics for the execution of any public operation in the class. * *

    An @Transactional annotation on a method within the class overrides the - * default transaction semantics given by the class annotation (if present). + * default transaction semantics given by the class annotation (if present). * Any method may be annotated (regardless of visibility). * Annotating non-public methods directly is the only way * to get transaction demarcation for the execution of such operations. @@ -57,7 +57,7 @@ public aspect AnnotationTransactionAspect extends AbstractTransactionAspect { execution(public * ((@Transactional *)+).*(..)) && within(@Transactional *); /** - * Matches the execution of any method with the + * Matches the execution of any method with the * Transactional annotation. */ private pointcut executionOfTransactionalMethod() : @@ -66,7 +66,7 @@ public aspect AnnotationTransactionAspect extends AbstractTransactionAspect { /** * Definition of pointcut from super aspect - matched join points * will have Spring transaction management applied. - */ + */ protected pointcut transactionalMethodExecution(Object txObject) : (executionOfAnyPublicMethodInAtTransactionalType() || executionOfTransactionalMethod() ) From 26d5ef93e6ddb7d3ba5774b5e427b0dc6b61516f Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Thu, 20 Dec 2012 00:26:08 +0100 Subject: [PATCH 065/143] Handle non-void write methods deterministically This change resolves a specific issue with processing java.math.BigDecimal via ExtendedBeanInfo. BigDecimal has a particular constellation of #setScale methods that, prior to this change, had the potential to cause ExtendedBeanInfo to throw an IntrospectionException depending on the order in which the methods were processed. Because JDK 7 no longer returns deterministic results from Class#getDeclaredMethods, it became a genuine possibility - indeed a statistical certainty that the 'wrong' setScale method handling order happens sooner or later. Typically one could observe this failure once out of every four test runs. This commit introduces deterministic method ordering of all discovered non-void returning write methods in such a way that solves the problem for BigDecimal as well as for any other class having a similar method arrangement. Also: - Remove unnecessary cast - Pass no method information to PropertyDescriptor superclasses when invoking super(...). This ensures that any 'type mismatch' IntrospectionExceptions are handled locally in ExtendedBeanInfo and its Simple* PropertyDescriptor variants where we have full control. Issue: SPR-10111, SPR-9702 Backport-Commit: aa3e0be (forward-ported via cherry-pick from 3.1.x) --- .../beans/ExtendedBeanInfo.java | 13 ++++++++-- .../beans/ExtendedBeanInfoTests.java | 26 ++++++++++++------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java b/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java index 54467cd3ea8..c3609c10c22 100644 --- a/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java +++ b/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java @@ -31,6 +31,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.List; @@ -116,6 +117,14 @@ class ExtendedBeanInfo implements BeanInfo { matches.add(method); } } + // sort non-void returning write methods to guard against the ill effects of + // non-deterministic sorting of methods returned from Class#getDeclaredMethods + // under JDK 7. See http://bugs.sun.com/view_bug.do?bug_id=7023180 + Collections.sort(matches, new Comparator() { + public int compare(Method m1, Method m2) { + return m2.toString().compareTo(m1.toString()); + } + }); return matches; } @@ -264,7 +273,7 @@ class SimpleNonIndexedPropertyDescriptor extends PropertyDescriptor { public SimpleNonIndexedPropertyDescriptor(String propertyName, Method readMethod, Method writeMethod) throws IntrospectionException { - super(propertyName, readMethod, writeMethod); + super(propertyName, null, null); this.setReadMethod(readMethod); this.setWriteMethod(writeMethod); this.propertyType = findPropertyType(readMethod, writeMethod); @@ -353,7 +362,7 @@ class SimpleIndexedPropertyDescriptor extends IndexedPropertyDescriptor { Method indexedReadMethod, Method indexedWriteMethod) throws IntrospectionException { - super(propertyName, readMethod, writeMethod, indexedReadMethod, indexedWriteMethod); + super(propertyName, null, null, null, null); this.setReadMethod(readMethod); this.setWriteMethod(writeMethod); this.propertyType = findPropertyType(readMethod, writeMethod); diff --git a/spring-beans/src/test/java/org/springframework/beans/ExtendedBeanInfoTests.java b/spring-beans/src/test/java/org/springframework/beans/ExtendedBeanInfoTests.java index aaf6e56092d..ec2ca31e2bb 100644 --- a/spring-beans/src/test/java/org/springframework/beans/ExtendedBeanInfoTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/ExtendedBeanInfoTests.java @@ -23,6 +23,7 @@ import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; +import java.math.BigDecimal; import org.junit.Test; @@ -192,7 +193,6 @@ public class ExtendedBeanInfoTests { } } class Child extends Parent { - @Override public Integer getProperty1() { return 2; } @@ -214,7 +214,6 @@ public class ExtendedBeanInfoTests { @Test public void cornerSpr9453() throws IntrospectionException { final class Bean implements Spr9453> { - @Override public Class getProp() { return null; } @@ -583,6 +582,20 @@ public class ExtendedBeanInfoTests { } } + /** + * Prior to SPR-10111 (a follow-up fix for SPR-9702), this method would throw an + * IntrospectionException regarding a "type mismatch between indexed and non-indexed + * methods" intermittently (approximately one out of every four times) under JDK 7 + * due to non-deterministic results from {@link Class#getDeclaredMethods()}. + * See http://bugs.sun.com/view_bug.do?bug_id=7023180 + * @see #cornerSpr9702() + */ + @Test + public void cornerSpr10111() throws Exception { + new ExtendedBeanInfo(Introspector.getBeanInfo(BigDecimal.class)); + } + + @Test public void subclassWriteMethodWithCovariantReturnType() throws IntrospectionException { @SuppressWarnings("unused") class B { @@ -590,9 +603,7 @@ public class ExtendedBeanInfoTests { public Number setFoo(String foo) { return null; } } class C extends B { - @Override public String getFoo() { return null; } - @Override public Integer setFoo(String foo) { return null; } } @@ -695,7 +706,7 @@ public class ExtendedBeanInfoTests { for (PropertyDescriptor pd : ebi.getPropertyDescriptors()) { if (pd.getName().equals("foo")) { - assertThat(pd.getWriteMethod(), is(C.class.getMethod("setFoo", int.class))); + assertThat(pd.getWriteMethod(), is(C.class.getMethod("setFoo", String.class))); return; } } @@ -733,7 +744,7 @@ public class ExtendedBeanInfoTests { assertThat(hasReadMethodForProperty(ebi, "dateFormat"), is(false)); assertThat(hasWriteMethodForProperty(ebi, "dateFormat"), is(true)); assertThat(hasIndexedReadMethodForProperty(ebi, "dateFormat"), is(false)); - assertThat(hasIndexedWriteMethodForProperty(ebi, "dateFormat"), is(true)); + assertThat(hasIndexedWriteMethodForProperty(ebi, "dateFormat"), is(trueUntilJdk17())); } @Test @@ -864,7 +875,6 @@ public class ExtendedBeanInfoTests { } interface TextBookOperations extends BookOperations { - @Override TextBook getBook(); } @@ -874,7 +884,6 @@ public class ExtendedBeanInfoTests { } class LawLibrary extends Library implements TextBookOperations { - @Override public LawBook getBook() { return null; } } @@ -889,7 +898,6 @@ public class ExtendedBeanInfoTests { } class B extends A { - @Override public boolean isTargetMethod() { return false; } From 19445508b31c9e032f62498521cd39c1d75902d5 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Wed, 16 Jan 2013 08:55:58 +0100 Subject: [PATCH 066/143] Polish build.gradle --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d37870c20ff..143e6a79dcf 100644 --- a/build.gradle +++ b/build.gradle @@ -776,7 +776,7 @@ configure(rootProject) { projectsToScan -= project(":spring-instrument-tomcat") } - // don"t publish the default jar for the root project + // don't publish the default jar for the root project configurations.archives.artifacts.clear() dependencies { // for integration tests From e659deab4f014d550ff2da5d81b0d76b9cdf2c19 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Wed, 16 Jan 2013 08:56:06 +0100 Subject: [PATCH 067/143] Remove .aj sources from spring-aspects jar Issue: SPR-10179 --- spring-aspects/aspects.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-aspects/aspects.gradle b/spring-aspects/aspects.gradle index c99fa031837..81728303163 100644 --- a/spring-aspects/aspects.gradle +++ b/spring-aspects/aspects.gradle @@ -33,7 +33,7 @@ task compileJava(overwrite: true) { destDir: outputDir.absolutePath, aspectPath: configurations.aspects.asPath, inpath: configurations.ajInpath.asPath, - sourceRootCopyFilter: "**/*.java", + sourceRootCopyFilter: "**/*.java,**/*.aj", classpath: (sourceSets.main.runtimeClasspath + configurations.rt).asPath) { sourceroots { sourceSets.main.java.srcDirs.each { From 6a5744e61cc0a60a812580c26b9b004709cbe6e0 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 16 Jan 2013 10:24:05 +0100 Subject: [PATCH 068/143] CachedIntrospectionResults.clearClassLoader(null) removes cached classes for the system class loader Issue: SPR-9189 --- .../beans/CachedIntrospectionResults.java | 11 ++++------- .../beans/CachedIntrospectionResultsTests.java | 13 ++++++++++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java index 47ff208dd4e..7c8cd51cece 100644 --- a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java +++ b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -107,9 +107,6 @@ public class CachedIntrospectionResults { * @param classLoader the ClassLoader to clear the cache for */ public static void clearClassLoader(ClassLoader classLoader) { - if (classLoader == null) { - return; - } synchronized (classCache) { for (Iterator it = classCache.keySet().iterator(); it.hasNext();) { Class beanClass = it.next(); @@ -199,12 +196,12 @@ public class CachedIntrospectionResults { * @param parent the parent ClassLoader to check for */ private static boolean isUnderneathClassLoader(ClassLoader candidate, ClassLoader parent) { - if (candidate == null) { - return false; - } if (candidate == parent) { return true; } + if (candidate == null) { + return false; + } ClassLoader classLoaderToCheck = candidate; while (classLoaderToCheck != null) { classLoaderToCheck = classLoaderToCheck.getParent(); diff --git a/spring-beans/src/test/java/org/springframework/beans/CachedIntrospectionResultsTests.java b/spring-beans/src/test/java/org/springframework/beans/CachedIntrospectionResultsTests.java index a4fe92acc96..618ced2e496 100644 --- a/spring-beans/src/test/java/org/springframework/beans/CachedIntrospectionResultsTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/CachedIntrospectionResultsTests.java @@ -18,9 +18,8 @@ package org.springframework.beans; import java.beans.BeanInfo; import java.beans.PropertyDescriptor; +import java.util.ArrayList; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import org.junit.Test; import org.springframework.core.OverridingClassLoader; @@ -37,7 +36,7 @@ import static org.junit.Assert.*; public final class CachedIntrospectionResultsTests { @Test - public void acceptClassLoader() throws Exception { + public void acceptAndClearClassLoader() throws Exception { BeanWrapper bw = new BeanWrapperImpl(TestBean.class); assertTrue(bw.isWritableProperty("name")); assertTrue(bw.isWritableProperty("age")); @@ -57,6 +56,14 @@ public final class CachedIntrospectionResultsTests { assertTrue(CachedIntrospectionResults.classCache.containsKey(TestBean.class)); } + @Test + public void clearClassLoaderForSystemClassLoader() throws Exception { + BeanUtils.getPropertyDescriptors(ArrayList.class); + assertTrue(CachedIntrospectionResults.classCache.containsKey(ArrayList.class)); + CachedIntrospectionResults.clearClassLoader(ArrayList.class.getClassLoader()); + assertFalse(CachedIntrospectionResults.classCache.containsKey(ArrayList.class)); + } + @Test public void shouldUseExtendedBeanInfoWhenApplicable() throws NoSuchMethodException, SecurityException { // given a class with a non-void returning setter method From d55877ccf2d050af4c4194c7983ffaac9db88aea Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 17 Jan 2013 12:30:02 +0100 Subject: [PATCH 069/143] spring-jms-3.2.xsd allows for SpEL expressions in prefetch and receive-timeout attributes Issue: SPR-9553 --- .../org/springframework/jms/config/spring-jms-3.2.xsd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-jms/src/main/resources/org/springframework/jms/config/spring-jms-3.2.xsd b/spring-jms/src/main/resources/org/springframework/jms/config/spring-jms-3.2.xsd index be338f0fc0d..c5ac3bb32d6 100644 --- a/spring-jms/src/main/resources/org/springframework/jms/config/spring-jms-3.2.xsd +++ b/spring-jms/src/main/resources/org/springframework/jms/config/spring-jms-3.2.xsd @@ -225,7 +225,7 @@ ]]> - + - + - + Date: Fri, 18 Jan 2013 13:42:21 +0100 Subject: [PATCH 070/143] Removed reference to AttributesJmxAttributeSource Issue: SPR-8916 --- src/reference/docbook/jmx.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reference/docbook/jmx.xml b/src/reference/docbook/jmx.xml index 85ae5d0d271..05e8588ebfa 100644 --- a/src/reference/docbook/jmx.xml +++ b/src/reference/docbook/jmx.xml @@ -1061,7 +1061,7 @@ public class AnnotationTestBean implements IJmxTestBean { + class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/> ]]> From cd2183f9e1eb7b098edaa86472014197f5d6e6aa Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Jan 2013 13:47:11 +0100 Subject: [PATCH 071/143] Fixed ConnectionSpecConnectionFactoryAdapter explanation Issue: SPR-9466 --- src/reference/docbook/cci.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/reference/docbook/cci.xml b/src/reference/docbook/cci.xml index e5fc3bb82f9..051d587da5b 100644 --- a/src/reference/docbook/cci.xml +++ b/src/reference/docbook/cci.xml @@ -169,9 +169,9 @@ allows for specifying a ConnectionSpec instance to use for all operations on a given factory. If the adapter's connectionSpec property is specified, the adapter - uses the getConnection variant without argument, else - the one with the ConnectionSpec - argument. + uses the getConnection variant with the + ConnectionSpec argument, otherwise + the variant without argument. <bean id="managedConnectionFactory" class="com.sun.connector.cciblackbox.CciLocalTxManagedConnectionFactory"> From 13cf1fceaa88add6c02b4cebb58c7a5721c93946 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Jan 2013 13:49:57 +0100 Subject: [PATCH 072/143] Fixed reference to "fallbackToNoOpCache" flag Issue: SPR-9064 --- src/reference/docbook/cache.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/reference/docbook/cache.xml b/src/reference/docbook/cache.xml index b96eaa3c917..08d2f34c0e1 100644 --- a/src/reference/docbook/cache.xml +++ b/src/reference/docbook/cache.xml @@ -570,10 +570,10 @@ public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)]]>< - + ]]> - The CompositeCacheManager above chains multiple CacheManagers and additionally, through the addNoOpManager flag, adds a + The CompositeCacheManager above chains multiple CacheManagers and additionally, through the fallbackToNoOpCache flag, adds a no op cache that for all the definitions not handled by the configured cache managers. That is, every cache definition not found in either jdkCache or gemfireCache (configured above) will be handled by the no op cache, which will not store any information causing the target method to be executed every time. From be606f4169ce8a79f88ca0bfd02233095a3cff40 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Jan 2013 13:58:22 +0100 Subject: [PATCH 073/143] Added hint towards not using orm.hibernate3 classes with Hibernate 4 Issue: SPR-9365 --- .../org/springframework/orm/hibernate4/package-info.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/package-info.java b/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/package-info.java index 809ced77e67..6715263e756 100644 --- a/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/package-info.java +++ b/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/package-info.java @@ -6,10 +6,13 @@ * *

    Contains an implementation of Spring's transaction SPI for local Hibernate transactions. * This package is intentionally rather minimal, with no template classes or the like, - * in order to follow native Hibernate recommendations as closely as possible. + * in order to follow Hibernate recommendations as closely as possible. We recommend + * using Hibernate's native sessionFactory.getCurrentSession() style. * *

    This package supports Hibernate 4.x only. * See the {@code org.springframework.orm.hibernate3} package for Hibernate 3.x support. + * Note: Do not use HibernateTemplate or other classes from the hibernate3 package + * with Hibernate 4; this will lead to class definition exceptions at runtime. * */ package org.springframework.orm.hibernate4; From 20c4ba40d8735c0b86bc3e5dc20af4b7664b94ae Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Jan 2013 14:02:13 +0100 Subject: [PATCH 074/143] AbstractDriverBasedDataSource does not rely on Properties chaining anymore Issue: SPR-9461 --- .../jdbc/datasource/AbstractDriverBasedDataSource.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/AbstractDriverBasedDataSource.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/AbstractDriverBasedDataSource.java index 00aedb4c223..6df3d53e571 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/AbstractDriverBasedDataSource.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/AbstractDriverBasedDataSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -139,7 +139,8 @@ public abstract class AbstractDriverBasedDataSource extends AbstractDataSource { * @see java.sql.Driver#connect(String, java.util.Properties) */ protected Connection getConnectionFromDriver(String username, String password) throws SQLException { - Properties props = new Properties(getConnectionProperties()); + Properties props = new Properties(); + props.putAll(getConnectionProperties()); if (username != null) { props.setProperty("user", username); } From 721fa9db6a3416f805350b0ae2fba538cdf8e0e6 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Jan 2013 14:12:02 +0100 Subject: [PATCH 075/143] SpringBeanAutowiringInterceptor eagerly releases BeanFactory if post-construction fails Issue: SPR-10013 --- .../beans/factory/access/BeanFactoryReference.java | 6 ++---- .../interceptor/SpringBeanAutowiringInterceptor.java | 10 +++++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/access/BeanFactoryReference.java b/spring-beans/src/main/java/org/springframework/beans/factory/access/BeanFactoryReference.java index 35c3d3f51fa..c2c0a7c6294 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/access/BeanFactoryReference.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/access/BeanFactoryReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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.beans.factory.access; -import org.springframework.beans.FatalBeanException; import org.springframework.beans.factory.BeanFactory; /** @@ -49,11 +48,10 @@ public interface BeanFactoryReference { *

    In an EJB usage scenario this would normally be called from * {@code ejbRemove()} and {@code ejbPassivate()}. *

    This is safe to call multiple times. - * @throws FatalBeanException if the {@code BeanFactory} cannot be released * @see BeanFactoryLocator * @see org.springframework.context.access.ContextBeanFactoryReference * @see org.springframework.context.ConfigurableApplicationContext#close() */ - void release() throws FatalBeanException; + void release(); } diff --git a/spring-context/src/main/java/org/springframework/ejb/interceptor/SpringBeanAutowiringInterceptor.java b/spring-context/src/main/java/org/springframework/ejb/interceptor/SpringBeanAutowiringInterceptor.java index 89d50674517..33c15966306 100644 --- a/spring-context/src/main/java/org/springframework/ejb/interceptor/SpringBeanAutowiringInterceptor.java +++ b/spring-context/src/main/java/org/springframework/ejb/interceptor/SpringBeanAutowiringInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -71,8 +71,6 @@ import org.springframework.context.access.ContextSingletonBeanFactoryLocator; * @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor * @see org.springframework.context.access.ContextSingletonBeanFactoryLocator * @see #getBeanFactoryLocatorKey - * @see org.springframework.ejb.support.AbstractEnterpriseBean#setBeanFactoryLocator - * @see org.springframework.ejb.support.AbstractEnterpriseBean#setBeanFactoryLocatorKey */ public class SpringBeanAutowiringInterceptor { @@ -99,9 +97,15 @@ public class SpringBeanAutowiringInterceptor { invocationContext.proceed(); } catch (RuntimeException ex) { + doReleaseBean(invocationContext.getTarget()); throw ex; } + catch (Error err) { + doReleaseBean(invocationContext.getTarget()); + throw err; + } catch (Exception ex) { + doReleaseBean(invocationContext.getTarget()); // Cannot declare a checked exception on WebSphere here - so we need to wrap. throw new EJBException(ex); } From 3dd817585b6258fe51d3279262234dc41f34009b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Jan 2013 14:43:11 +0100 Subject: [PATCH 076/143] MBeanExporter does not log warnings for manually unregistered MBeans Issue: SPR-9451 --- .../jmx/support/MBeanRegistrationSupport.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/jmx/support/MBeanRegistrationSupport.java b/spring-context/src/main/java/org/springframework/jmx/support/MBeanRegistrationSupport.java index 74b0a3f3c04..38339d4b7f7 100644 --- a/spring-context/src/main/java/org/springframework/jmx/support/MBeanRegistrationSupport.java +++ b/spring-context/src/main/java/org/springframework/jmx/support/MBeanRegistrationSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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.jmx.support; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import javax.management.InstanceAlreadyExistsException; @@ -112,7 +113,7 @@ public class MBeanRegistrationSupport { /** * The beans that have been registered by this exporter. */ - protected final Set registeredBeans = new LinkedHashSet(); + private final Set registeredBeans = Collections.synchronizedSet(new LinkedHashSet()); /** * The policy used when registering an MBean and finding that it already exists. @@ -228,10 +229,9 @@ public class MBeanRegistrationSupport { * Unregisters all beans that have been registered by an instance of this class. */ protected void unregisterBeans() { - for (ObjectName objectName : this.registeredBeans) { + for (ObjectName objectName : new LinkedHashSet(this.registeredBeans)) { doUnregister(objectName); } - this.registeredBeans.clear(); } /** @@ -257,6 +257,7 @@ public class MBeanRegistrationSupport { logger.error("Could not unregister MBean [" + objectName + "]", ex); } } + this.registeredBeans.remove(objectName); } /** From ed952ccba1118af716747eb183ea95b5beef8421 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Jan 2013 14:52:24 +0100 Subject: [PATCH 077/143] LocalVariableTableParameterNameDiscoverer works for bridge methods as well Issue: SPR-9429 --- .../LocalVariableTableParameterNameDiscoverer.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/LocalVariableTableParameterNameDiscoverer.java b/spring-core/src/main/java/org/springframework/core/LocalVariableTableParameterNameDiscoverer.java index 588472532c2..3cab216bf03 100644 --- a/spring-core/src/main/java/org/springframework/core/LocalVariableTableParameterNameDiscoverer.java +++ b/spring-core/src/main/java/org/springframework/core/LocalVariableTableParameterNameDiscoverer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,15 +65,15 @@ public class LocalVariableTableParameterNameDiscoverer implements ParameterNameD public String[] getParameterNames(Method method) { - Class declaringClass = method.getDeclaringClass(); + Method originalMethod = BridgeMethodResolver.findBridgedMethod(method); + Class declaringClass = originalMethod.getDeclaringClass(); Map map = this.parameterNamesCache.get(declaringClass); if (map == null) { - // initialize cache map = inspectClass(declaringClass); this.parameterNamesCache.put(declaringClass, map); } if (map != NO_DEBUG_INFO_MAP) { - return map.get(method); + return map.get(originalMethod); } return null; } @@ -82,14 +82,12 @@ public class LocalVariableTableParameterNameDiscoverer implements ParameterNameD Class declaringClass = ctor.getDeclaringClass(); Map map = this.parameterNamesCache.get(declaringClass); if (map == null) { - // initialize cache map = inspectClass(declaringClass); this.parameterNamesCache.put(declaringClass, map); } if (map != NO_DEBUG_INFO_MAP) { return map.get(ctor); } - return null; } From 8c9383da7c9ce02408c4e22eb8b6fdc07043582d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Jan 2013 15:00:00 +0100 Subject: [PATCH 078/143] BridgeMethodResolver properly handles bridge methods in interfaces Issue: SPR-9330 --- .../org/springframework/core/BridgeMethodResolver.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java b/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java index c16cdb39dc2..1027d4dfc9d 100644 --- a/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java +++ b/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,7 +25,6 @@ import java.util.Arrays; import java.util.List; import java.util.Map; -import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; @@ -145,7 +144,7 @@ public abstract class BridgeMethodResolver { private static Method findGenericDeclaration(Method bridgeMethod) { // Search parent types for method that has same signature as bridge. Class superclass = bridgeMethod.getDeclaringClass().getSuperclass(); - while (!Object.class.equals(superclass)) { + while (superclass != null && !Object.class.equals(superclass)) { Method method = searchForMatch(superclass, bridgeMethod); if (method != null && !method.isBridge()) { return method; @@ -219,8 +218,6 @@ public abstract class BridgeMethodResolver { * @return whether signatures match as described */ public static boolean isVisibilityBridgeMethodPair(Method bridgeMethod, Method bridgedMethod) { - Assert.isTrue(bridgeMethod != null); - Assert.isTrue(bridgedMethod != null); if (bridgeMethod == bridgedMethod) { return true; } From 701c5f11100b56c4809d61bcb889294a1cb3eb3e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Jan 2013 15:20:55 +0100 Subject: [PATCH 079/143] ContextLoader properly detects pre-refreshed WebApplicationContext Issue: SPR-9996 --- .../web/context/ContextLoader.java | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java b/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java index 6afd42b16c7..9880e9875ee 100644 --- a/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java +++ b/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,11 +23,11 @@ import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; - import javax.servlet.ServletContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.access.BeanFactoryLocator; import org.springframework.beans.factory.access.BeanFactoryReference; @@ -280,7 +280,18 @@ public class ContextLoader { this.context = createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) { - configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext)this.context, servletContext); + ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; + if (!cwac.isActive()) { + // The context has not yet been refreshed -> provide services such as + // setting the parent context, setting the application context id, etc + if (cwac.getParent() == null) { + // The context instance was injected without an explicit parent -> + // determine parent for root web application context, if any. + ApplicationContext parent = loadParentContext(servletContext); + cwac.setParent(parent); + } + configureAndRefreshWebApplicationContext(cwac, servletContext); + } } servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); @@ -333,9 +344,7 @@ public class ContextLoader { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } - ConfigurableWebApplicationContext wac = - (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); - return wac; + return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); } /** @@ -370,10 +379,6 @@ public class ContextLoader { } } - // Determine parent for root web application context, if any. - ApplicationContext parent = loadParentContext(sc); - - wac.setParent(parent); wac.setServletContext(sc); String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM); if (initParameter != null) { @@ -472,11 +477,11 @@ public class ContextLoader { Class contextClass = applicationContext.getClass(); ArrayList> initializerInstances = - new ArrayList>(); + new ArrayList>(); for (Class> initializerClass : initializerClasses) { Class initializerContextClass = - GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); + GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); Assert.isAssignable(initializerContextClass, contextClass, String.format( "Could not add context initializer [%s] as its generic parameter [%s] " + "is not assignable from the type of application context used by this " + From cca255bc7964ede0cde75bfdea08ecba74f3a069 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Jan 2013 16:51:21 +0100 Subject: [PATCH 080/143] Added "exposeAccessContext" flag JndiRmiClientInterceptor/ProxyFactoryBean (for WebLogic) Issue: SPR-9428 --- .../rmi/JndiRmiClientInterceptor.java | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/remoting/rmi/JndiRmiClientInterceptor.java b/spring-context/src/main/java/org/springframework/remoting/rmi/JndiRmiClientInterceptor.java index 8660e2cf5be..caa7f51452a 100644 --- a/spring-context/src/main/java/org/springframework/remoting/rmi/JndiRmiClientInterceptor.java +++ b/spring-context/src/main/java/org/springframework/remoting/rmi/JndiRmiClientInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,7 @@ package org.springframework.remoting.rmi; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.rmi.RemoteException; - +import javax.naming.Context; import javax.naming.NamingException; import javax.rmi.PortableRemoteObject; @@ -88,6 +88,8 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho private boolean refreshStubOnConnectFailure = false; + private boolean exposeAccessContext = false; + private Object cachedStub; private final Object stubMonitor = new Object(); @@ -166,6 +168,18 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho this.refreshStubOnConnectFailure = refreshStubOnConnectFailure; } + /** + * Set whether to expose the JNDI environment context for all access to the target + * RMI stub, i.e. for all method invocations on the exposed object reference. + *

    Default is "false", i.e. to only expose the JNDI context for object lookup. + * Switch this flag to "true" in order to expose the JNDI environment (including + * the authorization context) for each RMI invocation, as needed by WebLogic + * for RMI stubs with authorization requirements. + */ + public void setExposeAccessContext(boolean exposeAccessContext) { + this.exposeAccessContext = exposeAccessContext; + } + @Override public void afterPropertiesSet() throws NamingException { @@ -190,8 +204,8 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho else if (getServiceInterface() != null) { boolean isImpl = getServiceInterface().isInstance(remoteObj); logger.debug("Using service interface [" + getServiceInterface().getName() + - "] for JNDI RMI object [" + getJndiName() + "] - " + - (!isImpl ? "not " : "") + "directly implemented"); + "] for JNDI RMI object [" + getJndiName() + "] - " + + (!isImpl ? "not " : "") + "directly implemented"); } } if (this.cacheStub) { @@ -268,13 +282,15 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho * @see java.rmi.NoSuchObjectException */ public Object invoke(MethodInvocation invocation) throws Throwable { - Object stub = null; + Object stub; try { stub = getStub(); } catch (NamingException ex) { throw new RemoteLookupFailureException("JNDI lookup for RMI service [" + getJndiName() + "] failed", ex); } + + Context ctx = (this.exposeAccessContext ? getJndiTemplate().getContext() : null); try { return doInvoke(invocation, stub); } @@ -297,6 +313,9 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho throw ex; } } + finally { + getJndiTemplate().releaseContext(ctx); + } } /** @@ -354,7 +373,7 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho * @see #invoke */ protected Object refreshAndRetry(MethodInvocation invocation) throws Throwable { - Object freshStub = null; + Object freshStub; synchronized (this.stubMonitor) { this.cachedStub = null; freshStub = lookupStub(); @@ -426,7 +445,7 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho * @see org.springframework.remoting.support.RemoteInvocation */ protected Object doInvoke(MethodInvocation methodInvocation, RmiInvocationHandler invocationHandler) - throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { + throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { if (AopUtils.isToStringMethod(methodInvocation.getMethod())) { return "RMI invoker proxy for service URL [" + getJndiName() + "]"; From 944e1c95e6e6cbe83dd709fa3fe608f79b2c8f36 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Jan 2013 17:58:24 +0100 Subject: [PATCH 081/143] EhCacheManagerFactoryBean applies cacheManagerName ahead of creation (for EHCache 2.5 compatibility) Issue: SPR-9171 --- .../ehcache/EhCacheManagerFactoryBean.java | 54 ++++++++++++++----- .../cache/ehcache/EhCacheSupportTests.java | 3 +- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java index 8ae68321dcd..a170eb84f85 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2013 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,12 @@ package org.springframework.cache.ehcache; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Method; import net.sf.ehcache.CacheException; import net.sf.ehcache.CacheManager; +import net.sf.ehcache.config.Configuration; +import net.sf.ehcache.config.ConfigurationFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -28,6 +31,8 @@ import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.io.Resource; +import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; /** * {@link FactoryBean} that exposes an EHCache {@link net.sf.ehcache.CacheManager} @@ -54,6 +59,10 @@ import org.springframework.core.io.Resource; */ public class EhCacheManagerFactoryBean implements FactoryBean, InitializingBean, DisposableBean { + // Check whether EHCache 2.1+ CacheManager.create(Configuration) method is available... + private static final Method createWithConfiguration = + ClassUtils.getMethodIfAvailable(CacheManager.class, "create", Configuration.class); + protected final Log logger = LogFactory.getLog(getClass()); private Resource configLocation; @@ -98,21 +107,42 @@ public class EhCacheManagerFactoryBean implements FactoryBean, Ini public void afterPropertiesSet() throws IOException, CacheException { logger.info("Initializing EHCache CacheManager"); - if (this.configLocation != null) { - InputStream is = this.configLocation.getInputStream(); - try { - this.cacheManager = (this.shared ? CacheManager.create(is) : new CacheManager(is)); + InputStream is = (this.configLocation != null ? this.configLocation.getInputStream() : null); + try { + // A bit convoluted for EHCache 1.x/2.0 compatibility. + // To be much simpler once we require EHCache 2.1+ + if (this.cacheManagerName != null) { + if (this.shared && createWithConfiguration == null) { + // No CacheManager.create(Configuration) method available before EHCache 2.1; + // can only set CacheManager name after creation. + this.cacheManager = (is != null ? CacheManager.create(is) : CacheManager.create()); + this.cacheManager.setName(this.cacheManagerName); + } + else { + Configuration configuration = (is != null ? ConfigurationFactory.parseConfiguration(is) : + ConfigurationFactory.parseConfiguration()); + configuration.setName(this.cacheManagerName); + if (this.shared) { + this.cacheManager = (CacheManager) ReflectionUtils.invokeMethod(createWithConfiguration, null, configuration); + } + else { + this.cacheManager = new CacheManager(configuration); + } + } } - finally { + // For strict backwards compatibility: use simplest possible constructors... + else if (this.shared) { + this.cacheManager = (is != null ? CacheManager.create(is) : CacheManager.create()); + } + else { + this.cacheManager = (is != null ? new CacheManager(is) : new CacheManager()); + } + } + finally { + if (is != null) { is.close(); } } - else { - this.cacheManager = (this.shared ? CacheManager.create() : new CacheManager()); - } - if (this.cacheManagerName != null) { - this.cacheManager.setName(this.cacheManagerName); - } } diff --git a/spring-context-support/src/test/java/org/springframework/cache/ehcache/EhCacheSupportTests.java b/spring-context-support/src/test/java/org/springframework/cache/ehcache/EhCacheSupportTests.java index 646a6797171..fcfe2216425 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/ehcache/EhCacheSupportTests.java +++ b/spring-context-support/src/test/java/org/springframework/cache/ehcache/EhCacheSupportTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -38,6 +38,7 @@ public class EhCacheSupportTests extends TestCase { public void testLoadingBlankCacheManager() throws Exception { EhCacheManagerFactoryBean cacheManagerFb = new EhCacheManagerFactoryBean(); + cacheManagerFb.setCacheManagerName("myCacheManager"); assertEquals(CacheManager.class, cacheManagerFb.getObjectType()); assertTrue("Singleton property", cacheManagerFb.isSingleton()); cacheManagerFb.afterPropertiesSet(); From 87968e5a71929a4cce6f1fd4c0e2e194a631715a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Jan 2013 18:01:33 +0100 Subject: [PATCH 082/143] Further preparations for 3.2.1 --- src/dist/changelog.txt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/dist/changelog.txt b/src/dist/changelog.txt index 6f53ecc7317..8c0d7fbfab3 100644 --- a/src/dist/changelog.txt +++ b/src/dist/changelog.txt @@ -9,19 +9,28 @@ Changes in version 3.2.1 (2013-01-24) * SpEL support for static finals on interfaces (SPR-10125) * AnnotationAwareOrderComparator is able to sort Class objects as well (SPR-10152) * added dedicated sort method to AnnotationAwareOrderComparator (SPR-9625) +* BridgeMethodResolver properly handles bridge methods in interfaces (SPR-9330) +* LocalVariableTableParameterNameDiscoverer works for bridge methods as well (SPR-9429) +* CachedIntrospectionResults.clearClassLoader(null) removes cached classes for the system class loader (SPR-9189) * fixed QualifierAnnotationAutowireCandidateResolver's detection of custom qualifier annotations (SPR-10107) * fixed AbstractAutoProxyCreator to accept null bean names again (SPR-10108) * AbstractAdvisingBeanPostProcessor caches per bean target class, working for null bean names as well (SPR-10144) * MessageSourceResourceBundle overrides JDK 1.6 containsKey method, avoiding NPE in getKeys (SPR-10136) +* SpringBeanAutowiringInterceptor eagerly releases BeanFactory if post-construction fails (SPR-10013) +* added "exposeAccessContext" flag JndiRmiClientInterceptor/ProxyFactoryBean (for WebLogic; SPR-9428) +* MBeanExporter does not log warnings for manually unregistered MBeans (SPR-9451) * AbstractCacheManager accepts no caches defined, allowing for EHCache default cache setup (SPR-7955) -* spring-task-3.2.xsd allows for SpEL expressions in initial-delay attribute (SPR-10102) -* reintroduced "mode" and "proxy-target-class" attributes in spring-task-3.1/3.2.xsd (SPR-10177) +* EhCacheManagerFactoryBean applies cacheManagerName ahead of creation (for EHCache 2.5 compatibility; SPR-9171) * JDBC parameter binding uses JDBC 3.0 ParameterMetaData (if available) for type determination (SPR-10084) +* reintroduced "mode" and "proxy-target-class" attributes in spring-task-3.1/3.2.xsd (SPR-10177) +* spring-task-3.2.xsd allows for SpEL expressions in initial-delay attribute (SPR-10102) +* spring-jms-3.2.xsd allows for SpEL expressions in prefetch and receive-timeout attributes (SPR-9553) * JmsTemplate uses configured receiveTimeout if shorter than remaining transaction timeout (SPR-10109) * added MappingJackson2MessageConverter for JMS (SPR-10099) * MimeMessageHelper encodes attachment filename if not ASCII compliant (SPR-9258) * FreeMarkerConfigurationFactory properly supports TemplateLoaders when recreating Configurations (SPR-9389) * SpringContextResourceAdapter implements equals/hashCode according to the JCA 1.5 contract (SPR-9162) +* ContextLoader properly detects pre-refreshed WebApplicationContext (SPR-9996) Changes in version 3.2 GA (2012-12-13) From 5d9ad5b1d1ceb5e8d0ede2a7ede5203d94964ec2 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Jan 2013 19:07:30 +0100 Subject: [PATCH 083/143] AbstractDriverBasedDataSource does not rely on Properties chaining anymore Issue: SPR-9461 --- .../datasource/AbstractDriverBasedDataSource.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/AbstractDriverBasedDataSource.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/AbstractDriverBasedDataSource.java index 6df3d53e571..c7e7e19918d 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/AbstractDriverBasedDataSource.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/AbstractDriverBasedDataSource.java @@ -139,15 +139,18 @@ public abstract class AbstractDriverBasedDataSource extends AbstractDataSource { * @see java.sql.Driver#connect(String, java.util.Properties) */ protected Connection getConnectionFromDriver(String username, String password) throws SQLException { - Properties props = new Properties(); - props.putAll(getConnectionProperties()); + Properties mergedProps = new Properties(); + Properties connProps = getConnectionProperties(); + if (connProps != null) { + mergedProps.putAll(connProps); + } if (username != null) { - props.setProperty("user", username); + mergedProps.setProperty("user", username); } if (password != null) { - props.setProperty("password", password); + mergedProps.setProperty("password", password); } - return getConnectionFromDriver(props); + return getConnectionFromDriver(mergedProps); } /** From 24cc33306def7aff4aa2c797cd4aea320a4f8523 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Mon, 21 Jan 2013 11:05:50 +0100 Subject: [PATCH 084/143] Fix typo in reference documentation Issue: SPR-10171 --- src/reference/docbook/overview.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reference/docbook/overview.xml b/src/reference/docbook/overview.xml index a4de366ef8c..9bef8ee488e 100644 --- a/src/reference/docbook/overview.xml +++ b/src/reference/docbook/overview.xml @@ -362,7 +362,7 @@ TR: OK. Added to diagram.--> places: On the community download site http://www.springsource.org/downloads/community. + xl:href="http://www.springsource.org/download/community">http://www.springsource.org/download/community. Here you find all the Spring jars bundled together into a zip file for easy download. The names of the jars here since version 3.0 are in the form From d40c8cfc58298f3c5f0fea5870d0aed495ba3fb9 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Mon, 21 Jan 2013 11:21:44 +0100 Subject: [PATCH 085/143] Fix broken Castor URLs in ref docs and mapping XML Issue: SPR-10189 --- .../java/org/springframework/oxm/castor/package-info.java | 2 +- .../test/resources/org/springframework/oxm/castor/mapping.xml | 4 ++-- src/reference/docbook/oxm.xml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spring-oxm/src/main/java/org/springframework/oxm/castor/package-info.java b/spring-oxm/src/main/java/org/springframework/oxm/castor/package-info.java index 42f8cdade28..1f1167ab09e 100644 --- a/spring-oxm/src/main/java/org/springframework/oxm/castor/package-info.java +++ b/spring-oxm/src/main/java/org/springframework/oxm/castor/package-info.java @@ -1,7 +1,7 @@ /** * - * Package providing integration of Castor within Springs O/X Mapping + * Package providing integration of Castor within Springs O/X Mapping * support * */ diff --git a/spring-oxm/src/test/resources/org/springframework/oxm/castor/mapping.xml b/spring-oxm/src/test/resources/org/springframework/oxm/castor/mapping.xml index e85971251c2..f54e293fdf6 100644 --- a/spring-oxm/src/test/resources/org/springframework/oxm/castor/mapping.xml +++ b/spring-oxm/src/test/resources/org/springframework/oxm/castor/mapping.xml @@ -1,5 +1,5 @@ - + Castor generated mapping file @@ -28,4 +28,4 @@ xmlns:tns="http://samples.springframework.org/flight"/> - \ No newline at end of file + diff --git a/src/reference/docbook/oxm.xml b/src/reference/docbook/oxm.xml index f5797b2c4b8..00ae3966209 100644 --- a/src/reference/docbook/oxm.xml +++ b/src/reference/docbook/oxm.xml @@ -438,7 +438,7 @@ public class Application { though a mapping file can be used to have more control over the behavior of Castor. - For more information on Castor, refer to the + For more information on Castor, refer to the Castor web site. The Spring integration classes reside in the org.springframework.oxm.castor package. @@ -462,7 +462,7 @@ public class Application { Although it is possible to rely on Castor's default marshalling behavior, it might be necessary to have more control over it. This can be accomplished using a Castor mapping file. For more information, refer - to Castor XML Mapping. + to Castor XML Mapping. The mapping can be set using the mappingLocation resource property, indicated From fc6377cc533df9fe719b5092cb7760c31645f633 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Mon, 21 Jan 2013 12:41:43 +0100 Subject: [PATCH 086/143] Add TimedSpringRunnerTests to performance test group Issue: SPR-9984 --- .../test/context/junit4/TimedSpringRunnerTests.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java index a3679ba75e1..5594d3206cf 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java @@ -25,6 +25,8 @@ import org.junit.runner.notification.RunNotifier; import org.junit.runners.JUnit4; import org.springframework.test.annotation.Timed; import org.springframework.test.context.TestExecutionListeners; +import org.springframework.tests.Assume; +import org.springframework.tests.TestGroup; /** * Verifies proper handling of the following in conjunction with the @@ -42,6 +44,7 @@ public class TimedSpringRunnerTests { @Test public void timedTests() throws Exception { + Assume.group(TestGroup.PERFORMANCE); Class testClass = TimedSpringRunnerTestCase.class; TrackingRunListener listener = new TrackingRunListener(); RunNotifier notifier = new RunNotifier(); From 4dc3fcecbd9b274398b73614e31d794b72799adf Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Mon, 21 Jan 2013 13:57:32 +0100 Subject: [PATCH 087/143] Ensure -PtestGroups is passed through to unit tests Issue: SPR-9984 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 84c2ce62862..a6359fcc490 100644 --- a/build.gradle +++ b/build.gradle @@ -67,7 +67,7 @@ configure(allprojects) { project -> test { systemProperty("java.awt.headless", "true") - systemProperty("testGroups", properties.get("testGroups")) + systemProperty("testGroups", project.properties.get("testGroups")) } repositories { From cb8dc73fbb326a82324cf797a0116786d54e99b0 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Mon, 21 Jan 2013 14:03:36 +0100 Subject: [PATCH 088/143] Attempt to repro ReflectionUtils performance issue Issue: SPR-10197 --- .../util/ReflectionUtilsTests.java | 54 +++++++++++++++---- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/spring-core/src/test/java/org/springframework/util/ReflectionUtilsTests.java b/spring-core/src/test/java/org/springframework/util/ReflectionUtilsTests.java index 6b791a154be..7d555076ba4 100644 --- a/spring-core/src/test/java/org/springframework/util/ReflectionUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/util/ReflectionUtilsTests.java @@ -16,15 +16,6 @@ package org.springframework.util; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -33,10 +24,18 @@ import java.rmi.RemoteException; import java.util.LinkedList; import java.util.List; +import org.hamcrest.Matchers; + import org.junit.Ignore; import org.junit.Test; + +import org.springframework.tests.Assume; +import org.springframework.tests.TestGroup; import org.springframework.tests.sample.objects.TestObject; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; + /** * @author Rob Harrop * @author Juergen Hoeller @@ -347,6 +346,43 @@ public class ReflectionUtilsTests { assertFalse(ObjectUtils.containsElement(methods, Parent.class.getMethod("m1"))); } + @Test + public void getUniqueDeclaredMethods_isFastEnough() { + Assume.group(TestGroup.PERFORMANCE); + + @SuppressWarnings("unused") + class C { + void m00() { } void m01() { } void m02() { } void m03() { } void m04() { } + void m05() { } void m06() { } void m07() { } void m08() { } void m09() { } + void m10() { } void m11() { } void m12() { } void m13() { } void m14() { } + void m15() { } void m16() { } void m17() { } void m18() { } void m19() { } + void m20() { } void m21() { } void m22() { } void m23() { } void m24() { } + void m25() { } void m26() { } void m27() { } void m28() { } void m29() { } + void m30() { } void m31() { } void m32() { } void m33() { } void m34() { } + void m35() { } void m36() { } void m37() { } void m38() { } void m39() { } + void m40() { } void m41() { } void m42() { } void m43() { } void m44() { } + void m45() { } void m46() { } void m47() { } void m48() { } void m49() { } + void m50() { } void m51() { } void m52() { } void m53() { } void m54() { } + void m55() { } void m56() { } void m57() { } void m58() { } void m59() { } + void m60() { } void m61() { } void m62() { } void m63() { } void m64() { } + void m65() { } void m66() { } void m67() { } void m68() { } void m69() { } + void m70() { } void m71() { } void m72() { } void m73() { } void m74() { } + void m75() { } void m76() { } void m77() { } void m78() { } void m79() { } + void m80() { } void m81() { } void m82() { } void m83() { } void m84() { } + void m85() { } void m86() { } void m87() { } void m88() { } void m89() { } + void m90() { } void m91() { } void m92() { } void m93() { } void m94() { } + void m95() { } void m96() { } void m97() { } void m98() { } void m99() { } + } + + StopWatch sw = new StopWatch(); + sw.start(); + Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(C.class); + sw.stop(); + long totalMs = sw.getTotalTimeMillis(); + assertThat(methods.length, Matchers.greaterThan(100)); + assertThat(totalMs, Matchers.lessThan(10L)); + } + private static class ListSavingMethodCallback implements ReflectionUtils.MethodCallback { private List methodNames = new LinkedList(); From 8e1685caefae49ccd796ef9a8f707bc94d54680a Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 21 Jan 2013 11:55:31 -0500 Subject: [PATCH 089/143] Fix ClassCastException in TilesConfigurer Issue: SPR-10195 --- build.gradle | 3 + .../servlet/view/tiles3/TilesConfigurer.java | 76 +++++++++++-------- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/build.gradle b/build.gradle index 7d147c14438..813816bdfd9 100644 --- a/build.gradle +++ b/build.gradle @@ -613,6 +613,9 @@ project("spring-webmvc-tiles3") { optional("org.apache.tiles:tiles-jsp:3.0.1") { exclude group: "org.slf4j", module: "jcl-over-slf4j" } + optional("org.apache.tiles:tiles-extras:3.0.1") { + exclude group: "org.slf4j", module: "jcl-over-slf4j" + } optional("org.apache.tiles:tiles-el:3.0.1") { exclude group: "org.slf4j", module: "jcl-over-slf4j" } diff --git a/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/TilesConfigurer.java b/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/TilesConfigurer.java index f01f25dd1ad..d23ac0033b2 100644 --- a/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/TilesConfigurer.java +++ b/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/TilesConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,19 +32,21 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.tiles.TilesContainer; import org.apache.tiles.TilesException; -import org.apache.tiles.access.TilesAccess; import org.apache.tiles.definition.DefinitionsFactory; import org.apache.tiles.definition.DefinitionsReader; import org.apache.tiles.definition.dao.BaseLocaleUrlDefinitionDAO; import org.apache.tiles.definition.dao.CachingLocaleUrlDefinitionDAO; import org.apache.tiles.definition.digester.DigesterDefinitionsReader; import org.apache.tiles.el.ELAttributeEvaluator; +import org.apache.tiles.el.ScopeELResolver; import org.apache.tiles.el.TilesContextBeanELResolver; import org.apache.tiles.el.TilesContextELResolver; import org.apache.tiles.evaluator.AttributeEvaluator; import org.apache.tiles.evaluator.AttributeEvaluatorFactory; import org.apache.tiles.evaluator.BasicAttributeEvaluatorFactory; import org.apache.tiles.evaluator.impl.DirectAttributeEvaluator; +import org.apache.tiles.extras.complete.CompleteAutoloadTilesContainerFactory; +import org.apache.tiles.extras.complete.CompleteAutoloadTilesInitializer; import org.apache.tiles.factory.AbstractTilesContainerFactory; import org.apache.tiles.factory.BasicTilesContainerFactory; import org.apache.tiles.impl.BasicTilesContainer; @@ -72,7 +74,9 @@ import org.springframework.web.context.ServletContextAware; *

    The TilesConfigurer simply configures a TilesContainer using a set of files * containing definitions, to be accessed by {@link TilesView} instances. This is a * Spring-based alternative (for usage in Spring configuration) to the Tiles-provided - * {@link org.apache.tiles.web.startup.TilesListener} (for usage in {@code web.xml}). + * {@code ServletContextListener} + * (e.g. {@link org.apache.tiles.extras.complete.CompleteAutoloadTilesListener} + * for usage in {@code web.xml}. * *

    TilesViews can be managed by any {@link org.springframework.web.servlet.ViewResolver}. * For simple convention-based view resolution, consider using {@link TilesViewResolver}. @@ -110,8 +114,6 @@ public class TilesConfigurer implements ServletContextAware, InitializingBean, D private TilesInitializer tilesInitializer; - private boolean completeAutoload = false; - private String[] definitions; private boolean checkRefresh = false; @@ -154,17 +156,14 @@ public class TilesConfigurer implements ServletContextAware, InitializingBean, D public void setCompleteAutoload(boolean completeAutoload) { if (completeAutoload) { try { - Class clazz = getClass().getClassLoader().loadClass( - "org.apache.tiles.extras.complete.CompleteAutoloadTilesInitializer"); - this.tilesInitializer = (TilesInitializer) clazz.newInstance(); + this.tilesInitializer = new SpringCompleteAutoloadTilesInitializer(); } catch (Exception ex) { - throw new IllegalStateException("Tiles-Extras 3.0 not available", ex); + throw new IllegalStateException("tiles-extras 3.x not available", ex); } } else { this.tilesInitializer = null; } - this.completeAutoload = completeAutoload; } /** @@ -192,7 +191,7 @@ public class TilesConfigurer implements ServletContextAware, InitializingBean, D /** * Set the {@link org.apache.tiles.definition.DefinitionsFactory} implementation to use. - * Default is {@link org.apache.tiles.definition.UrlDefinitionsFactory}, + * Default is {@link org.apache.tiles.definition.UnresolvingLocaleDefinitionsFactory}, * operating on definition resource URLs. *

    Specify a custom DefinitionsFactory, e.g. a UrlDefinitionsFactory subclass, * to customize the creation of Tiles Definition objects. Note that such a @@ -204,8 +203,8 @@ public class TilesConfigurer implements ServletContextAware, InitializingBean, D } /** - * Set the {@link org.apache.tiles.preparer.PreparerFactory} implementation to use. - * Default is {@link org.apache.tiles.preparer.BasicPreparerFactory}, creating + * Set the {@link org.apache.tiles.preparer.factory.PreparerFactory} implementation to use. + * Default is {@link org.apache.tiles.preparer.factory.BasicPreparerFactory}, creating * shared instances for specified preparer classes. *

    Specify {@link SimpleSpringPreparerFactory} to autowire * {@link org.apache.tiles.preparer.ViewPreparer} instances based on specified @@ -230,7 +229,7 @@ public class TilesConfigurer implements ServletContextAware, InitializingBean, D * Set whether to use a MutableTilesContainer (typically the CachingTilesContainer * implementation) for this application. Default is "false". * @see org.apache.tiles.mgmt.MutableTilesContainer - * @see org.apache.tiles.mgmt.CachingTilesContainer + * @see org.apache.tiles.impl.mgmt.CachingTilesContainer */ public void setUseMutableTilesContainer(boolean useMutableTilesContainer) { this.useMutableTilesContainer = useMutableTilesContainer; @@ -246,26 +245,11 @@ public class TilesConfigurer implements ServletContextAware, InitializingBean, D * @throws TilesException in case of setup failure */ public void afterPropertiesSet() throws TilesException { - - SpringWildcardServletTilesApplicationContext preliminaryContext = - new SpringWildcardServletTilesApplicationContext(this.servletContext); - + ApplicationContext preliminaryContext = new SpringWildcardServletTilesApplicationContext(this.servletContext); if (this.tilesInitializer == null) { this.tilesInitializer = new SpringTilesInitializer(); } this.tilesInitializer.initialize(preliminaryContext); - - if (this.completeAutoload) { - // We need to do this after initialization simply because we're reusing the - // original CompleteAutoloadTilesInitializer above. We cannot subclass - // CompleteAutoloadTilesInitializer when compiling against Tiles 2.1... - logger.debug("Registering Tiles 3.0 SpringLocaleResolver for complete-autoload setup"); - BasicTilesContainer container = (BasicTilesContainer) TilesAccess.getContainer(preliminaryContext); - BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(container.getDefinitionsFactory()); - if (bw.isWritableProperty("localeResolver")) { - bw.setPropertyValue("localeResolver", new SpringLocaleResolver()); - } - } } /** @@ -332,8 +316,7 @@ public class TilesConfigurer implements ServletContextAware, InitializingBean, D if (definitionsFactoryClass != null) { DefinitionsFactory factory = BeanUtils.instantiate(definitionsFactoryClass); if (factory instanceof org.apache.tiles.request.ApplicationContextAware) { - ((org.apache.tiles.request.ApplicationContextAware) factory) - .setApplicationContext(applicationContext); + ((org.apache.tiles.request.ApplicationContextAware) factory).setApplicationContext(applicationContext); } BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(factory); if (bw.isWritableProperty("localeResolver")) { @@ -380,6 +363,34 @@ public class TilesConfigurer implements ServletContextAware, InitializingBean, D } } + private class SpringCompleteAutoloadTilesInitializer extends CompleteAutoloadTilesInitializer { + + @Override + protected AbstractTilesContainerFactory createContainerFactory(ApplicationContext context) { + return new SpringCompleteAutoloadTilesContainerFactory(); + } + } + + private class SpringCompleteAutoloadTilesContainerFactory extends CompleteAutoloadTilesContainerFactory { + + @Override + protected AttributeEvaluatorFactory createAttributeEvaluatorFactory( + ApplicationContext applicationContext, LocaleResolver resolver) { + return new BasicAttributeEvaluatorFactory(new DirectAttributeEvaluator()); + } + + @Override + public TilesContainer createContainer(ApplicationContext applicationContext) { + CachingTilesContainer cachingContainer = (CachingTilesContainer) super.createContainer(applicationContext); + BasicTilesContainer tilesContainer = (BasicTilesContainer) cachingContainer.getWrappedContainer(); + BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(tilesContainer.getDefinitionsFactory()); + if (bw.isWritableProperty("localeResolver")) { + bw.setPropertyValue("localeResolver", new SpringLocaleResolver()); + } + return tilesContainer; + } + } + private class TilesElActivator { @@ -405,6 +416,7 @@ public class TilesConfigurer implements ServletContextAware, InitializingBean, D private static class CompositeELResolverImpl extends CompositeELResolver { public CompositeELResolverImpl() { + add(new ScopeELResolver()); add(new TilesContextELResolver(new TilesContextBeanELResolver())); add(new TilesContextBeanELResolver()); add(new ArrayELResolver(false)); From e4fcad9f936ba492f28ec5f0421eea4b3f76f8aa Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 21 Jan 2013 12:34:56 -0500 Subject: [PATCH 090/143] Fix exception message about producible media types Issue: SPR-10175 --- .../annotation/AbstractMessageConverterMethodProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java index ccdf7d3703e..48cff20fe54 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java @@ -123,7 +123,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe } } if (compatibleMediaTypes.isEmpty()) { - throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes); + throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes); } List mediaTypes = new ArrayList(compatibleMediaTypes); From 21becef1bdd36aac41c519798a772dbadb5d6973 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 21 Jan 2013 10:51:21 -0800 Subject: [PATCH 091/143] Support Date to String in JodaTimeConverters Update JodaTimeConverters in include support for Date to String conversion. The JodaTimeFormattingTests and DateFormattingTests have been extended to ensure that Date to String conversion is supported with or without Joda. Issue: SPR-10198 --- .../format/datetime/DateFormatterRegistrar.java | 4 ++-- .../format/datetime/joda/JodaTimeConverters.java | 14 +++++++++++++- .../format/datetime/DateFormattingTests.java | 12 ++++++++++-- .../datetime/joda/JodaTimeFormattingTests.java | 12 +++++++++++- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/format/datetime/DateFormatterRegistrar.java b/spring-context/src/main/java/org/springframework/format/datetime/DateFormatterRegistrar.java index eb413a6ea7a..aedf1184373 100644 --- a/spring-context/src/main/java/org/springframework/format/datetime/DateFormatterRegistrar.java +++ b/spring-context/src/main/java/org/springframework/format/datetime/DateFormatterRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -57,7 +57,7 @@ public class DateFormatterRegistrar implements FormatterRegistrar { * @param dateFormatter the date formatter */ public void setFormatter(DateFormatter dateFormatter) { - Assert.notNull(dateFormatter,"DateFormatter must not be null"); + Assert.notNull(dateFormatter, "DateFormatter must not be null"); this.dateFormatter = dateFormatter; } diff --git a/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeConverters.java b/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeConverters.java index 6943ad4241f..b49b6072559 100644 --- a/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeConverters.java +++ b/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeConverters.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -35,6 +35,7 @@ import org.springframework.format.datetime.DateFormatterRegistrar; * Installs lower-level type converters required to integrate Joda Time support into Spring's field formatting system. * * @author Keith Donald + * @author Phillip Webb * @since 3.0 */ final class JodaTimeConverters { @@ -55,6 +56,7 @@ final class JodaTimeConverters { registry.addConverter(new DateTimeToCalendarConverter()); registry.addConverter(new DateTimeToLongConverter()); registry.addConverter(new CalendarToReadableInstantConverter()); + registry.addConverter(new DateToReadableInstantConverter()); } @@ -159,4 +161,14 @@ final class JodaTimeConverters { } } + /** + * Used when printing a java.util.Date field with a ReadableInstantPrinter. + * @see MillisecondInstantPrinter + * @see JodaDateTimeFormatAnnotationFormatterFactory + */ + private static class DateToReadableInstantConverter implements Converter { + public ReadableInstant convert(Date source) { + return new DateTime(source); + } + } } diff --git a/spring-context/src/test/java/org/springframework/format/datetime/DateFormattingTests.java b/spring-context/src/test/java/org/springframework/format/datetime/DateFormattingTests.java index 087e8e95214..5698ed06518 100644 --- a/spring-context/src/test/java/org/springframework/format/datetime/DateFormattingTests.java +++ b/spring-context/src/test/java/org/springframework/format/datetime/DateFormattingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,10 +30,10 @@ import org.junit.Ignore; import org.junit.Test; import org.springframework.beans.MutablePropertyValues; import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat.ISO; -import org.springframework.format.datetime.DateFormatterRegistrar; import org.springframework.format.support.FormattingConversionService; import org.springframework.validation.DataBinder; @@ -186,6 +186,14 @@ public class DateFormattingTests { assertEquals("10/31/09", binder.getBindingResult().getFieldValue("children[0].dateAnnotated")); } + @Test + public void dateToString() throws Exception { + Date date = new Date(); + Object actual = this.conversionService.convert(date, TypeDescriptor.valueOf(Date.class), TypeDescriptor.valueOf(String.class)); + String expected = new DateFormatter().print(date, Locale.US); + assertEquals(expected, actual); + } + @SuppressWarnings("unused") private static class SimpleDateBean { diff --git a/spring-context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java b/spring-context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java index 597a3820b50..237df0509f3 100644 --- a/spring-context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java +++ b/spring-context/src/test/java/org/springframework/format/datetime/joda/JodaTimeFormattingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -35,6 +35,7 @@ import org.junit.Test; import org.springframework.beans.MutablePropertyValues; import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat.ISO; @@ -46,6 +47,7 @@ import static org.junit.Assert.*; /** * @author Keith Donald * @author Juergen Hoeller + * @author Phillip Webb */ public class JodaTimeFormattingTests { @@ -456,6 +458,14 @@ public class JodaTimeFormattingTests { assertEquals("2009-10-31T07:00:00.000-05:00", binder.getBindingResult().getFieldValue("mutableDateTimeAnnotated")); } + @Test + public void dateToString() throws Exception { + Date date = new Date(); + Object actual = this.conversionService.convert(date, TypeDescriptor.valueOf(Date.class), TypeDescriptor.valueOf(String.class)); + String expected = JodaTimeContextHolder.getFormatter(org.joda.time.format.DateTimeFormat.shortDateTime(), Locale.US).print(new DateTime(date)); + assertEquals(expected, actual); + } + @SuppressWarnings("unused") private static class JodaTimeBean { From 0c56e86bfb7617b8a92ff187c9aec20c20db9da2 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 21 Jan 2013 13:23:59 -0500 Subject: [PATCH 092/143] Fix NPE in FormHttpMessageConverter Issue: SPR-10187 --- .../http/converter/FormHttpMessageConverter.java | 10 ++++++---- .../http/converter/FormHttpMessageConverterTests.java | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java index 0bbe671083f..f66a38bf192 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java @@ -265,10 +265,12 @@ public class FormHttpMessageConverter implements HttpMessageConverter> entry : parts.entrySet()) { String name = entry.getKey(); for (Object part : entry.getValue()) { - writeBoundary(boundary, os); - HttpEntity entity = getEntity(part); - writePart(name, entity, os); - writeNewLine(os); + if (part != null) { + writeBoundary(boundary, os); + HttpEntity entity = getEntity(part); + writePart(name, entity, os); + writeNewLine(os); + } } } } diff --git a/spring-web/src/test/java/org/springframework/http/converter/FormHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/FormHttpMessageConverterTests.java index 2344269b634..0792d6c6a8e 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/FormHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/FormHttpMessageConverterTests.java @@ -112,6 +112,7 @@ public class FormHttpMessageConverterTests { parts.add("name 1", "value 1"); parts.add("name 2", "value 2+1"); parts.add("name 2", "value 2+2"); + parts.add("name 3", null); Resource logo = new ClassPathResource("/org/springframework/http/converter/logo.jpg"); parts.add("logo", logo); From 902a136b6e7accf4aa4c8f6be1988e2cecb000ef Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 21 Jan 2013 14:41:31 -0500 Subject: [PATCH 093/143] Add helpful error message to DispServlet initializer Issue: SPR-10168 --- .../support/AbstractDispatcherServletInitializer.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/AbstractDispatcherServletInitializer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/AbstractDispatcherServletInitializer.java index ae16bdb9946..d6e6d010539 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/AbstractDispatcherServletInitializer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/AbstractDispatcherServletInitializer.java @@ -94,6 +94,11 @@ public abstract class AbstractDispatcherServletInitializer ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet); + + Assert.notNull(registration, + "Failed to register servlet with name '" + servletName + "'." + + "Check if there is another servlet registered under the same name."); + registration.setLoadOnStartup(1); registration.addMapping(getServletMappings()); registration.setAsyncSupported(isAsyncSupported()); From 660458a649e6158a9d772bba06326887995b60c4 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 21 Jan 2013 14:52:41 -0500 Subject: [PATCH 094/143] Fix init order issue in RequestMappingHandlerMapping Issue: SPR-10173 --- .../RequestMappingHandlerMapping.java | 2 +- .../RequestMappingHandlerMappingTests.java | 50 ++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index 5700b8def72..bb9ddd72b1f 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -157,10 +157,10 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @Override public void afterPropertiesSet() { - super.afterPropertiesSet(); if (this.useRegisteredSuffixPatternMatch) { this.fileExtensions.addAll(contentNegotiationManager.getAllFileExtensions()); } + super.afterPropertiesSet(); } /** diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java index 5c4d0776a49..63fce21fa7d 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java @@ -15,20 +15,28 @@ */ package org.springframework.web.servlet.mvc.method.annotation; -import static org.junit.Assert.*; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import org.junit.Before; import org.junit.Test; import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; import org.springframework.util.StringValueResolver; import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.accept.PathExtensionContentNegotiationStrategy; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.context.support.StaticWebApplicationContext; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; /** * Tests for {@link RequestMappingHandlerMapping}. @@ -63,6 +71,35 @@ public class RequestMappingHandlerMappingTests { assertEquals(Arrays.asList("json"), this.handlerMapping.getFileExtensions()); } + @Test + public void useRegsiteredSuffixPatternMatchInitialization() { + + Map fileExtensions = Collections.singletonMap("json", MediaType.APPLICATION_JSON); + PathExtensionContentNegotiationStrategy strategy = new PathExtensionContentNegotiationStrategy(fileExtensions); + ContentNegotiationManager manager = new ContentNegotiationManager(strategy); + + final Set extensions = new HashSet(); + + RequestMappingHandlerMapping hm = new RequestMappingHandlerMapping() { + @Override + protected RequestMappingInfo getMappingForMethod(Method method, Class handlerType) { + extensions.addAll(getFileExtensions()); + return super.getMappingForMethod(method, handlerType); + } + }; + + StaticWebApplicationContext wac = new StaticWebApplicationContext(); + wac.registerSingleton("testController", TestController.class); + wac.refresh(); + + hm.setContentNegotiationManager(manager); + hm.setUseRegisteredSuffixPatternMatch(true); + hm.setApplicationContext(wac); + hm.afterPropertiesSet(); + + assertEquals(Collections.singleton("json"), extensions); + } + @Test public void useSuffixPatternMatch() { assertTrue(this.handlerMapping.useSuffixPatternMatch()); @@ -93,4 +130,13 @@ public class RequestMappingHandlerMappingTests { assertArrayEquals(new String[] { "/foo", "/foo/bar" }, result); } + + @Controller + static class TestController { + + @RequestMapping + public void handle() { + } + } + } From d9a4fb410d6608260a597b1d72b9153c585caa5d Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Fri, 4 Jan 2013 15:56:08 +0100 Subject: [PATCH 095/143] Introduce "dummy" Environment implementation For testing purposes in which an Environment implementation is required but a ConfigurableEnvironment is not desirable. All methods are no-ops and return null, therefore NPEs are likely. --- .../core/env/DummyEnvironment.java | 75 +++++++++++++++++++ .../web/servlet/DispatcherServletTests.java | 17 ++--- 2 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 spring-core/src/test/java/org/springframework/core/env/DummyEnvironment.java diff --git a/spring-core/src/test/java/org/springframework/core/env/DummyEnvironment.java b/spring-core/src/test/java/org/springframework/core/env/DummyEnvironment.java new file mode 100644 index 00000000000..88d9347dfa1 --- /dev/null +++ b/spring-core/src/test/java/org/springframework/core/env/DummyEnvironment.java @@ -0,0 +1,75 @@ +/* + * Copyright 2002-2013 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.core.env; + +public class DummyEnvironment implements Environment { + + public boolean containsProperty(String key) { + return false; + } + + public String getProperty(String key) { + return null; + } + + public String getProperty(String key, String defaultValue) { + return null; + } + + public T getProperty(String key, Class targetType) { + return null; + } + + public T getProperty(String key, Class targetType, T defaultValue) { + return null; + } + + public Class getPropertyAsClass(String key, Class targetType) { + return null; + } + + public String getRequiredProperty(String key) throws IllegalStateException { + return null; + } + + public T getRequiredProperty(String key, Class targetType) + throws IllegalStateException { + return null; + } + + public String resolvePlaceholders(String text) { + return null; + } + + public String resolveRequiredPlaceholders(String text) + throws IllegalArgumentException { + return null; + } + + public String[] getActiveProfiles() { + return null; + } + + public String[] getDefaultProfiles() { + return null; + } + + public boolean acceptsProfiles(String... profiles) { + return false; + } + +} diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/DispatcherServletTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/DispatcherServletTests.java index 147875e4ca4..bb2d0a089b0 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/DispatcherServletTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/DispatcherServletTests.java @@ -16,12 +16,6 @@ package org.springframework.web.servlet; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.CoreMatchers.sameInstance; -import static org.junit.Assert.assertThat; - import java.io.IOException; import java.util.Locale; @@ -36,15 +30,15 @@ import junit.framework.TestCase; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyValue; -import org.springframework.tests.sample.beans.TestBean; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.DefaultMessageSourceResolvable; import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.StandardEnvironment; +import org.springframework.core.env.DummyEnvironment; import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.mock.web.test.MockHttpServletResponse; import org.springframework.mock.web.test.MockServletConfig; import org.springframework.mock.web.test.MockServletContext; +import org.springframework.tests.sample.beans.TestBean; import org.springframework.web.bind.EscapedErrors; import org.springframework.web.context.ConfigurableWebEnvironment; import org.springframework.web.context.ServletConfigAwareBean; @@ -65,6 +59,9 @@ import org.springframework.web.servlet.theme.AbstractThemeResolver; import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.util.WebUtils; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + /** * @author Rod Johnson * @author Juergen Hoeller @@ -842,8 +839,8 @@ public class DispatcherServletTests extends TestCase { servlet.setEnvironment(env1); // should succeed assertThat(servlet.getEnvironment(), sameInstance(env1)); try { - servlet.setEnvironment(new StandardEnvironment()); - fail("expected exception"); + servlet.setEnvironment(new DummyEnvironment()); + fail("expected IllegalArgumentException for non-configurable Environment"); } catch (IllegalArgumentException ex) { } From 3cdb866bda078a24d3fccb36b08a449a970a524f Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Fri, 4 Jan 2013 15:47:28 +0100 Subject: [PATCH 096/143] Relax ConfigurableWebEnvironment signatures ConfigurableWebEnvironment was introduced in 3.2.0.M1 with SPR-9439 in order to break a cyclic dependency. At the same time, certain signatures such as AbstractRefreshableWebApplicationContext#getEnviroment and GenericWebApplicationContext#getEnvironment were updated to take advantage of covariant return types and return this newer, more narrow type and providing cast-free calls to ConfigurableWebEnvironment methods where necessary. Similar changes were made to HttpServletBean in 3.2.0.M2 with SPR-9763. Narrowing #getEnvironment signatures in this fashion required enforcing at the #setEnvironment level that any Environment instance provided (explicitly or via the EnvironmentAware callback) must be an instance of ConfigurableWebEnvironment. This is a reasonable assertion in typical web application scenarios, but as SPR-10138 demonstrates, there are valid use cases in which one may want or need to inject a non-web ConfigurableEnvironment variant, e.g. during automated unit/integration testing. On review, it was never strictly necessary to narrow #getEnvironment signatures, although doing so did provided convenience and type safety. In order to maintain as flexible and backward-compatible an arrangement as possible, this commit relaxes these #getEnvironment signatures back to their original, pre-3.2 state. Namely, they now return ConfigurableEnvironment as opposed to ConfigurableWebEnvironment, and in accordance, all instanceof assertions have been removed or relaxed to ensure that injected Environment instances are of type ConfigurableEnvironment. These changes have been verified against David Winterfeldt's Spring by Example spring-rest-services project, as described at SPR-10138. Issue: SPR-10138, SPR-9763, SPR-9439 --- .../ConfigurableWebApplicationContext.java | 7 +------ .../web/context/ContextLoader.java | 6 +++++- ...tractRefreshableWebApplicationContext.java | 18 ++++++------------ .../support/GenericWebApplicationContext.java | 17 ++++++----------- .../support/StaticWebApplicationContext.java | 13 +------------ .../web/servlet/FrameworkServlet.java | 9 +++++++-- .../web/servlet/HttpServletBean.java | 19 +++++++++---------- .../web/servlet/DispatcherServletTests.java | 2 +- 8 files changed, 36 insertions(+), 55 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/context/ConfigurableWebApplicationContext.java b/spring-web/src/main/java/org/springframework/web/context/ConfigurableWebApplicationContext.java index a6185a81e8f..cba5e1dbc30 100644 --- a/spring-web/src/main/java/org/springframework/web/context/ConfigurableWebApplicationContext.java +++ b/spring-web/src/main/java/org/springframework/web/context/ConfigurableWebApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -71,11 +71,6 @@ public interface ConfigurableWebApplicationContext extends WebApplicationContext */ ServletConfig getServletConfig(); - /** - * Return the {@link ConfigurableWebEnvironment} used by this web application context. - */ - ConfigurableWebEnvironment getEnvironment(); - /** * Set the namespace for this web application context, * to be used for building a default context config location. diff --git a/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java b/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java index 9880e9875ee..69d812da378 100644 --- a/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java +++ b/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java @@ -38,6 +38,7 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.access.ContextSingletonBeanFactoryLocator; import org.springframework.core.GenericTypeResolver; import org.springframework.core.annotation.AnnotationAwareOrderComparator; +import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.util.Assert; @@ -490,7 +491,10 @@ public class ContextLoader { initializerInstances.add(BeanUtils.instantiateClass(initializerClass)); } - applicationContext.getEnvironment().initPropertySources(servletContext, null); + ConfigurableEnvironment env = applicationContext.getEnvironment(); + if (env instanceof ConfigurableWebEnvironment) { + ((ConfigurableWebEnvironment)env).initPropertySources(servletContext, null); + } Collections.sort(initializerInstances, new AnnotationAwareOrderComparator()); for (ApplicationContextInitializer initializer : initializerInstances) { diff --git a/spring-web/src/main/java/org/springframework/web/context/support/AbstractRefreshableWebApplicationContext.java b/spring-web/src/main/java/org/springframework/web/context/support/AbstractRefreshableWebApplicationContext.java index f36bddc3168..92b1f7907a7 100644 --- a/spring-web/src/main/java/org/springframework/web/context/support/AbstractRefreshableWebApplicationContext.java +++ b/spring-web/src/main/java/org/springframework/web/context/support/AbstractRefreshableWebApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -27,7 +27,6 @@ import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.ui.context.Theme; import org.springframework.ui.context.ThemeSource; import org.springframework.ui.context.support.UiApplicationContextUtils; -import org.springframework.util.Assert; import org.springframework.web.context.ConfigurableWebApplicationContext; import org.springframework.web.context.ConfigurableWebEnvironment; import org.springframework.web.context.ServletConfigAware; @@ -157,15 +156,6 @@ public abstract class AbstractRefreshableWebApplicationContext extends AbstractR return new StandardServletEnvironment(); } - @Override - public ConfigurableWebEnvironment getEnvironment() { - ConfigurableEnvironment env = super.getEnvironment(); - Assert.isInstanceOf(ConfigurableWebEnvironment.class, env, - "ConfigurableWebApplicationContext environment must be of type " + - "ConfigurableWebEnvironment"); - return (ConfigurableWebEnvironment) env; - } - /** * Register request/session scopes, a {@link ServletContextAwareProcessor}, etc. */ @@ -212,7 +202,11 @@ public abstract class AbstractRefreshableWebApplicationContext extends AbstractR @Override protected void initPropertySources() { super.initPropertySources(); - this.getEnvironment().initPropertySources(this.servletContext, this.servletConfig); + ConfigurableEnvironment env = this.getEnvironment(); + if (env instanceof ConfigurableWebEnvironment) { + ((ConfigurableWebEnvironment)env).initPropertySources( + this.servletContext, this.servletConfig); + } } public Theme getTheme(String themeName) { diff --git a/spring-web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java b/spring-web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java index ac87960cd00..d30d3aef841 100644 --- a/spring-web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java +++ b/spring-web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -147,15 +147,6 @@ public class GenericWebApplicationContext extends GenericApplicationContext return new StandardServletEnvironment(); } - @Override - public ConfigurableWebEnvironment getEnvironment() { - ConfigurableEnvironment env = super.getEnvironment(); - Assert.isInstanceOf(ConfigurableWebEnvironment.class, env, - "ConfigurableWebApplicationContext environment must be of type " + - "ConfigurableWebEnvironment"); - return (ConfigurableWebEnvironment) env; - } - /** * Register ServletContextAwareProcessor. * @see ServletContextAwareProcessor @@ -202,7 +193,11 @@ public class GenericWebApplicationContext extends GenericApplicationContext @Override protected void initPropertySources() { super.initPropertySources(); - this.getEnvironment().initPropertySources(this.servletContext, null); + ConfigurableEnvironment env = this.getEnvironment(); + if (env instanceof ConfigurableWebEnvironment) { + ((ConfigurableWebEnvironment)env).initPropertySources( + this.servletContext, null); + } } public Theme getTheme(String themeName) { diff --git a/spring-web/src/main/java/org/springframework/web/context/support/StaticWebApplicationContext.java b/spring-web/src/main/java/org/springframework/web/context/support/StaticWebApplicationContext.java index 5de71bda608..5d26a434e3c 100644 --- a/spring-web/src/main/java/org/springframework/web/context/support/StaticWebApplicationContext.java +++ b/spring-web/src/main/java/org/springframework/web/context/support/StaticWebApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -27,9 +27,7 @@ import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.ui.context.Theme; import org.springframework.ui.context.ThemeSource; import org.springframework.ui.context.support.UiApplicationContextUtils; -import org.springframework.util.Assert; import org.springframework.web.context.ConfigurableWebApplicationContext; -import org.springframework.web.context.ConfigurableWebEnvironment; import org.springframework.web.context.ServletConfigAware; import org.springframework.web.context.ServletContextAware; @@ -169,15 +167,6 @@ public class StaticWebApplicationContext extends StaticApplicationContext return new StandardServletEnvironment(); } - @Override - public ConfigurableWebEnvironment getEnvironment() { - ConfigurableEnvironment env = super.getEnvironment(); - Assert.isInstanceOf(ConfigurableWebEnvironment.class, env, - "ConfigurableWebApplication environment must be of type " + - "ConfigurableWebEnvironment"); - return (ConfigurableWebEnvironment) env; - } - /** * Initialize the theme capability. */ diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java index ca2a919043e..3dca4eda942 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,11 +39,13 @@ import org.springframework.context.i18n.LocaleContext; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.SimpleLocaleContext; import org.springframework.core.annotation.AnnotationAwareOrderComparator; +import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.context.ConfigurableWebApplicationContext; +import org.springframework.web.context.ConfigurableWebEnvironment; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.RequestAttributes; @@ -638,7 +640,10 @@ public abstract class FrameworkServlet extends HttpServletBean { // the context is refreshed; do it eagerly here to ensure servlet property sources // are in place for use in any post-processing or initialization that occurs // below prior to #refresh - wac.getEnvironment().initPropertySources(getServletContext(), getServletConfig()); + ConfigurableEnvironment env = wac.getEnvironment(); + if (env instanceof ConfigurableWebEnvironment) { + ((ConfigurableWebEnvironment)env).initPropertySources(getServletContext(), getServletConfig()); + } postProcessWebApplicationContext(wac); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/HttpServletBean.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/HttpServletBean.java index 044b56d2aea..bcd3a4d1606 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/HttpServletBean.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/HttpServletBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -27,7 +27,6 @@ import javax.servlet.http.HttpServlet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; @@ -35,6 +34,7 @@ import org.springframework.beans.PropertyAccessorFactory; import org.springframework.beans.PropertyValue; import org.springframework.beans.PropertyValues; import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.EnvironmentCapable; import org.springframework.core.io.Resource; @@ -42,9 +42,8 @@ import org.springframework.core.io.ResourceEditor; import org.springframework.core.io.ResourceLoader; import org.springframework.util.Assert; import org.springframework.util.StringUtils; -import org.springframework.web.context.ConfigurableWebEnvironment; -import org.springframework.web.context.support.StandardServletEnvironment; import org.springframework.web.context.support.ServletContextResourceLoader; +import org.springframework.web.context.support.StandardServletEnvironment; /** * Simple extension of {@link javax.servlet.http.HttpServlet} which treats @@ -91,7 +90,7 @@ public abstract class HttpServletBean extends HttpServlet */ private final Set requiredProperties = new HashSet(); - private ConfigurableWebEnvironment environment; + private ConfigurableEnvironment environment; /** @@ -187,11 +186,11 @@ public abstract class HttpServletBean extends HttpServlet /** * {@inheritDoc} * @throws IllegalArgumentException if environment is not assignable to - * {@code ConfigurableWebEnvironment}. + * {@code ConfigurableEnvironment}. */ public void setEnvironment(Environment environment) { - Assert.isInstanceOf(ConfigurableWebEnvironment.class, environment); - this.environment = (ConfigurableWebEnvironment)environment; + Assert.isInstanceOf(ConfigurableEnvironment.class, environment); + this.environment = (ConfigurableEnvironment) environment; } /** @@ -199,7 +198,7 @@ public abstract class HttpServletBean extends HttpServlet *

    If {@code null}, a new environment will be initialized via * {@link #createEnvironment()}. */ - public ConfigurableWebEnvironment getEnvironment() { + public ConfigurableEnvironment getEnvironment() { if (this.environment == null) { this.environment = this.createEnvironment(); } @@ -210,7 +209,7 @@ public abstract class HttpServletBean extends HttpServlet * Create and return a new {@link StandardServletEnvironment}. Subclasses may override * in order to configure the environment or specialize the environment type returned. */ - protected ConfigurableWebEnvironment createEnvironment() { + protected ConfigurableEnvironment createEnvironment() { return new StandardServletEnvironment(); } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/DispatcherServletTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/DispatcherServletTests.java index bb2d0a089b0..b73e69ae6f2 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/DispatcherServletTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/DispatcherServletTests.java @@ -833,7 +833,7 @@ public class DispatcherServletTests extends TestCase { public void testEnvironmentOperations() { DispatcherServlet servlet = new DispatcherServlet(); - ConfigurableWebEnvironment defaultEnv = servlet.getEnvironment(); + ConfigurableEnvironment defaultEnv = servlet.getEnvironment(); assertThat(defaultEnv, notNullValue()); ConfigurableEnvironment env1 = new StandardServletEnvironment(); servlet.setEnvironment(env1); // should succeed From 2a41de00e355ce1d4e277111b8c93d6fa2dc0020 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 22 Jan 2013 12:09:00 +0100 Subject: [PATCH 097/143] Polish Javadoc in Spring MVC async support This commit fixes some typographical and grammatical errors in various classes in Spring MVC's async support. --- .../web/context/request/async/DeferredResult.java | 6 +++--- .../web/context/request/async/WebAsyncManager.java | 10 +++++----- .../web/context/request/async/WebAsyncTask.java | 12 ++++++------ .../config/annotation/AsyncSupportConfigurer.java | 12 ++++++------ 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResult.java b/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResult.java index 4da20dd40b1..22ea83f4585 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResult.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,13 +32,13 @@ import org.springframework.web.context.request.NativeWebRequest; *

    Subclasses can extend this class to easily associate additional data or * behavior with the {@link DeferredResult}. For example, one might want to * associate the user used to create the {@link DeferredResult} by extending the - * class and adding an addition property for the user. In this way, the user + * class and adding an additional property for the user. In this way, the user * could easily be accessed later without the need to use a data structure to do * the mapping. * *

    An example of associating additional behavior to this class might be * realized by extending the class to implement an additional interface. For - * example, one might want to implement a {@link Comparable} so that when the + * example, one might want to implement {@link Comparable} so that when the * {@link DeferredResult} is added to a {@link PriorityQueue} it is handled in * the correct order. * diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java b/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java index 2e104d8e75b..8c79b4230f2 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -37,9 +37,9 @@ import org.springframework.web.util.UrlPathHelper; * as an SPI and not typically used directly by application classes. * *

    An async scenario starts with request processing as usual in a thread (T1). - * Concurrent request handling can be innitiated by calling - * {@linkplain #startCallableProcessing(Callable, Object...) startCallableProcessing} or - * {@linkplain #startDeferredResultProcessing(DeferredResult, Object...) startDeferredResultProcessing} + * Concurrent request handling can be initiated by calling + * {@link #startCallableProcessing(Callable, Object...) startCallableProcessing} or + * {@link #startDeferredResultProcessing(DeferredResult, Object...) startDeferredResultProcessing}, * both of which produce a result in a separate thread (T2). The result is saved * and the request dispatched to the container, to resume processing with the saved * result in a third thread (T3). Within the dispatched thread (T3), the saved @@ -263,7 +263,7 @@ public final class WebAsyncManager { * the timeout value of the {@code AsyncWebRequest} before delegating to * {@link #startCallableProcessing(Callable, Object...)}. * - * @param webAsyncTask an WebAsyncTask containing the target {@code Callable} + * @param webAsyncTask a WebAsyncTask containing the target {@code Callable} * @param processingContext additional context to save that can be accessed * via {@link #getConcurrentResultContext()} * @throws Exception If concurrent processing failed to start diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncTask.java b/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncTask.java index 15cfdc96eeb..59fa985acb7 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncTask.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -46,7 +46,7 @@ public class WebAsyncTask { /** - * Create an {@code WebAsyncTask} wrapping the given {@link Callable}. + * Create a {@code WebAsyncTask} wrapping the given {@link Callable}. * @param callable the callable for concurrent handling */ public WebAsyncTask(Callable callable) { @@ -54,7 +54,7 @@ public class WebAsyncTask { } /** - * Create an {@code WebAsyncTask} with a timeout value and a {@link Callable}. + * Create a {@code WebAsyncTask} with a timeout value and a {@link Callable}. * @param timeout timeout value in milliseconds * @param callable the callable for concurrent handling */ @@ -63,7 +63,7 @@ public class WebAsyncTask { } /** - * Create an {@code WebAsyncTask} with a timeout value, an executor name, and a {@link Callable}. + * Create a {@code WebAsyncTask} with a timeout value, an executor name, and a {@link Callable}. * @param timeout timeout value in milliseconds; ignored if {@code null} * @param callable the callable for concurrent handling */ @@ -73,7 +73,7 @@ public class WebAsyncTask { } /** - * Create an {@code WebAsyncTask} with a timeout value, an executor instance, and a Callable. + * Create a {@code WebAsyncTask} with a timeout value, an executor instance, and a Callable. * @param timeout timeout value in milliseconds; ignored if {@code null} * @param callable the callable for concurrent handling */ @@ -113,7 +113,7 @@ public class WebAsyncTask { return this.executor; } else if (this.executorName != null) { - Assert.state(this.beanFactory != null, "A BeanFactory is required to look up an task executor bean"); + Assert.state(this.beanFactory != null, "A BeanFactory is required to look up a task executor bean"); return this.beanFactory.getBean(this.executorName, AsyncTaskExecutor.class); } else { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/AsyncSupportConfigurer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/AsyncSupportConfigurer.java index 07d2b1a87e5..53e4893f99e 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/AsyncSupportConfigurer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/AsyncSupportConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,7 @@ import org.springframework.web.context.request.async.DeferredResult; import org.springframework.web.context.request.async.DeferredResultProcessingInterceptor; /** - * Helps with configuring a options for asynchronous request processing. + * Helps with configuring options for asynchronous request processing. * * @author Rossen Stoyanchev * @since 3.2 @@ -50,9 +50,9 @@ public class AsyncSupportConfigurer { /** * Set the default {@link AsyncTaskExecutor} to use when a controller method * returns a {@link Callable}. Controller methods can override this default on - * a per-request basis by returning an {@link WebAsyncTask}. + * a per-request basis by returning a {@link WebAsyncTask}. * - *

    By default a {@link SimpleAsyncTaskExecutor} instance is used and it's + *

    By default a {@link SimpleAsyncTaskExecutor} instance is used, and it's * highly recommended to change that default in production since the simple * executor does not re-use threads. * @@ -79,7 +79,7 @@ public class AsyncSupportConfigurer { } /** - * Configure lifecycle intercepters with callbacks around concurrent request + * Configure lifecycle interceptors with callbacks around concurrent request * execution that starts when a controller returns a * {@link java.util.concurrent.Callable}. * @@ -92,7 +92,7 @@ public class AsyncSupportConfigurer { } /** - * Configure lifecycle intercepters with callbacks around concurrent request + * Configure lifecycle interceptors with callbacks around concurrent request * execution that starts when a controller returns a {@link DeferredResult}. * * @param interceptors the interceptors to register From 3a626f93197d7f0fd4266d9877550a8d330017e5 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Tue, 22 Jan 2013 14:14:15 +0100 Subject: [PATCH 098/143] Support XML properties in ResourcePropertySource JDK 5 introduced an XML-based properties file syntax. This commit ensures that when such files are supplied as the underlying resource for a ResourcePropertySource instance, they are routed appropriately to Properties#loadFromXML as opposed to Properties#load. Issue: SPR-9896 --- .../core/io/support/ResourcePropertySource.java | 16 +++++++++++++--- .../java/org/springframework/core/io/example.xml | 6 ++++++ .../io/support/ResourcePropertySourceTests.java | 13 ++++++++++++- 3 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 spring-core/src/test/java/org/springframework/core/io/example.xml diff --git a/spring-core/src/main/java/org/springframework/core/io/support/ResourcePropertySource.java b/spring-core/src/main/java/org/springframework/core/io/support/ResourcePropertySource.java index f67a4579705..705d8d44e71 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/ResourcePropertySource.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/ResourcePropertySource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2013 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. @@ -28,7 +28,11 @@ import org.springframework.util.StringUtils; /** * Subclass of {@link PropertiesPropertySource} that loads a {@link Properties} * object from a given {@link org.springframework.core.io.Resource} or resource location such as - * {@code "classpath:/com/myco/foo.properties"} or {@code "file:/path/to/file.properties"}. + * {@code "classpath:/com/myco/foo.properties"} or {@code "file:/path/to/file.xml"}. + * Both traditional and XML-based properties file formats are supported, however in order + * for XML processing to take effect, the underlying {@code Resource}'s + * {@link org.springframework.core.io.Resource#getFilename() getFilename()} method must + * return non-{@code null} and end in ".xml". * * @author Chris Beams * @since 3.1 @@ -99,7 +103,13 @@ public class ResourcePropertySource extends PropertiesPropertySource { private static Properties loadPropertiesForResource(Resource resource) throws IOException { Properties props = new Properties(); InputStream is = resource.getInputStream(); - props.load(is); + String filename = resource.getFilename(); + if (filename != null && filename.endsWith(".xml")) { + props.loadFromXML(is); + } + else { + props.load(is); + } try { is.close(); } catch (IOException ex) { diff --git a/spring-core/src/test/java/org/springframework/core/io/example.xml b/spring-core/src/test/java/org/springframework/core/io/example.xml new file mode 100644 index 00000000000..1d638537e10 --- /dev/null +++ b/spring-core/src/test/java/org/springframework/core/io/example.xml @@ -0,0 +1,6 @@ + + + + bar + + diff --git a/spring-core/src/test/java/org/springframework/core/io/support/ResourcePropertySourceTests.java b/spring-core/src/test/java/org/springframework/core/io/support/ResourcePropertySourceTests.java index 1b77a9f542c..1bbc3cb2a1c 100644 --- a/spring-core/src/test/java/org/springframework/core/io/support/ResourcePropertySourceTests.java +++ b/spring-core/src/test/java/org/springframework/core/io/support/ResourcePropertySourceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2013 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,10 @@ public class ResourcePropertySourceTests { private static final String PROPERTIES_LOCATION = "classpath:" + PROPERTIES_PATH; private static final String PROPERTIES_RESOURCE_DESCRIPTION = "class path resource [" + PROPERTIES_PATH + "]"; + private static final String XML_PROPERTIES_PATH = "org/springframework/core/io/example.xml"; + private static final String XML_PROPERTIES_LOCATION = "classpath:" + XML_PROPERTIES_PATH; + private static final String XML_PROPERTIES_RESOURCE_DESCRIPTION = "class path resource [" + XML_PROPERTIES_PATH + "]"; + @Test public void withLocationAndGeneratedName() throws IOException { PropertySource ps = new ResourcePropertySource(PROPERTIES_LOCATION); @@ -46,6 +50,13 @@ public class ResourcePropertySourceTests { assertThat(ps.getName(), is(PROPERTIES_RESOURCE_DESCRIPTION)); } + @Test + public void xmlWithLocationAndGeneratedName() throws IOException { + PropertySource ps = new ResourcePropertySource(XML_PROPERTIES_LOCATION); + assertEquals(ps.getProperty("foo"), "bar"); + assertThat(ps.getName(), is(XML_PROPERTIES_RESOURCE_DESCRIPTION)); + } + @Test public void withLocationAndExplicitName() throws IOException { PropertySource ps = new ResourcePropertySource("ps1", PROPERTIES_LOCATION); From d3f4528905b2c6277d1b169f8570591418c57350 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Tue, 22 Jan 2013 16:02:31 +0100 Subject: [PATCH 099/143] Remove additional xsd versions from reference docs A few versioned xsd references still existed in xsd-configuration.xml; this commit removes them, completing the work for SPR-10010 Issue: SPR-10010 --- src/reference/docbook/xsd-configuration.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/reference/docbook/xsd-configuration.xml b/src/reference/docbook/xsd-configuration.xml index f57e766e067..a78556a27c8 100644 --- a/src/reference/docbook/xsd-configuration.xml +++ b/src/reference/docbook/xsd-configuration.xml @@ -679,7 +679,7 @@ http://www.springframework.org/schema/beans http://www.springframework.org/schem covered in the chapter entitled . You are strongly encouraged to look at the - 'spring-tx-3.0.xsd' file that ships with the Spring + 'spring-tx.xsd' file that ships with the Spring distribution. This file is (of course), the XML Schema for Spring's transaction configuration, and covers all of the various tags in the tx namespace, including attribute defaults and @@ -812,7 +812,7 @@ http://www.springframework.org/schema/beans http://www.springframework.org/schem Spring as they are currently undergoing review. If you are a third party tool vendor and you would like to contribute to this review process, then do mail the Spring mailing list. The currently supported tool - tags can be found in the file 'spring-tool-3.0.xsd' in the + tags can be found in the file 'spring-tool.xsd' in the 'src/org/springframework/beans/factory/xml' directory of the Spring source distribution.

@@ -931,8 +931,8 @@ http://www.springframework.org/schema/beans http://www.springframework.org/schem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation=" -http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd -http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"> +http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd +http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> ]]> @@ -1053,8 +1053,8 @@ http://www.springframework.org/schema/util http://www.springframework.org/schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation=" - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd - http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"> + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> ]]> @@ -1138,7 +1138,7 @@ http://www.springframework.org/schema/util http://www.springframework.org/schema relevant XSD file. As can be seen in the following screenshot, the 'http://www.springframework.org/schema/util' namespace is being associated with the file resource - 'C:\bench\spring\src\org\springframework\beans\factory\xml\spring-util-3.0.xsd'. + 'C:\bench\spring\src\org\springframework\beans\factory\xml\spring-util.xsd'. From 40ed4e78bee40dbf2afc12b0ac2fa8d12bb60b35 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 22 Jan 2013 11:54:31 -0500 Subject: [PATCH 100/143] Make UriComponents Serializable Issue: SPR-10186 --- .../java/org/springframework/web/util/UriComponents.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/util/UriComponents.java b/spring-web/src/main/java/org/springframework/web/util/UriComponents.java index f04c5f695bd..2a2b300bfa6 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriComponents.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriComponents.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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.web.util; +import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URI; import java.util.Arrays; @@ -38,7 +39,7 @@ import org.springframework.util.MultiValueMap; * @since 3.1 * @see UriComponentsBuilder */ -public abstract class UriComponents { +public abstract class UriComponents implements Serializable { private static final String DEFAULT_ENCODING = "UTF-8"; From 692ced8046f40facaa15fe9ad5a8d2cf56852794 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 22 Jan 2013 14:15:22 -0500 Subject: [PATCH 101/143] Fix typo in reference docs Issue: SPR-10204 --- src/reference/docbook/mvc.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reference/docbook/mvc.xml b/src/reference/docbook/mvc.xml index f4d3e3e802e..c8aebb4f8a4 100644 --- a/src/reference/docbook/mvc.xml +++ b/src/reference/docbook/mvc.xml @@ -5110,7 +5110,7 @@ public class WebConfig extends WebMvcConfigurerAdapter { @Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { - configurer.setFavorPathExtension(false).setFavorParameter(true); + configurer.favorPathExtension(false).favorParameter(true); } } From c1a4f5c0fe5bf9defb252b30197c08d138be42da Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 21 Jan 2013 11:31:28 +0100 Subject: [PATCH 102/143] MockHttpServletRequest's getParameter(Values) returns null for null parameter name Issue: SPR-10192 --- .../mock/web/MockHttpServletRequest.java | 43 ++++++++++-------- .../mock/web/MockHttpServletRequestTests.java | 12 +++-- .../mock/web/test/MockHttpServletRequest.java | 44 +++++++++---------- 3 files changed, 54 insertions(+), 45 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java index 441a53e4ce4..d609f39e8fc 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -35,7 +35,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; - import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletException; @@ -97,8 +96,10 @@ public class MockHttpServletRequest implements HttpServletRequest { private static final String CHARSET_PREFIX = "charset="; + private boolean active = true; + // --------------------------------------------------------------------- // ServletRequest properties // --------------------------------------------------------------------- @@ -140,6 +141,7 @@ public class MockHttpServletRequest implements HttpServletRequest { private int localPort = DEFAULT_SERVER_PORT; + // --------------------------------------------------------------------- // HttpServletRequest properties // --------------------------------------------------------------------- @@ -235,6 +237,7 @@ public class MockHttpServletRequest implements HttpServletRequest { this.locales.add(Locale.ENGLISH); } + // --------------------------------------------------------------------- // Lifecycle methods // --------------------------------------------------------------------- @@ -279,6 +282,7 @@ public class MockHttpServletRequest implements HttpServletRequest { } } + // --------------------------------------------------------------------- // ServletRequest interface // --------------------------------------------------------------------- @@ -351,7 +355,7 @@ public class MockHttpServletRequest implements HttpServletRequest { * parameter name, they will be replaced. */ public void setParameter(String name, String value) { - setParameter(name, new String[] { value }); + setParameter(name, new String[] {value}); } /** @@ -373,7 +377,8 @@ public class MockHttpServletRequest implements HttpServletRequest { public void setParameters(Map params) { Assert.notNull(params, "Parameter map must not be null"); for (Object key : params.keySet()) { - Assert.isInstanceOf(String.class, key, "Parameter map key must be of type [" + String.class.getName() + "]"); + Assert.isInstanceOf(String.class, key, + "Parameter map key must be of type [" + String.class.getName() + "]"); Object value = params.get(key); if (value instanceof String) { this.setParameter((String) key, (String) value); @@ -382,8 +387,8 @@ public class MockHttpServletRequest implements HttpServletRequest { this.setParameter((String) key, (String[]) value); } else { - throw new IllegalArgumentException("Parameter map value must be single value " + " or array of type [" - + String.class.getName() + "]"); + throw new IllegalArgumentException( + "Parameter map value must be single value " + " or array of type [" + String.class.getName() + "]"); } } } @@ -394,7 +399,7 @@ public class MockHttpServletRequest implements HttpServletRequest { * parameter name, the given value will be added to the end of the list. */ public void addParameter(String name, String value) { - addParameter(name, new String[] { value }); + addParameter(name, new String[] {value}); } /** @@ -425,7 +430,8 @@ public class MockHttpServletRequest implements HttpServletRequest { public void addParameters(Map params) { Assert.notNull(params, "Parameter map must not be null"); for (Object key : params.keySet()) { - Assert.isInstanceOf(String.class, key, "Parameter map key must be of type [" + String.class.getName() + "]"); + Assert.isInstanceOf(String.class, key, + "Parameter map key must be of type [" + String.class.getName() + "]"); Object value = params.get(key); if (value instanceof String) { this.addParameter((String) key, (String) value); @@ -434,8 +440,8 @@ public class MockHttpServletRequest implements HttpServletRequest { this.addParameter((String) key, (String[]) value); } else { - throw new IllegalArgumentException("Parameter map value must be single value " + " or array of type [" - + String.class.getName() + "]"); + throw new IllegalArgumentException("Parameter map value must be single value " + + " or array of type [" + String.class.getName() + "]"); } } } @@ -456,8 +462,7 @@ public class MockHttpServletRequest implements HttpServletRequest { } public String getParameter(String name) { - Assert.notNull(name, "Parameter name must not be null"); - String[] arr = this.parameters.get(name); + String[] arr = (name != null ? this.parameters.get(name) : null); return (arr != null && arr.length > 0 ? arr[0] : null); } @@ -466,8 +471,7 @@ public class MockHttpServletRequest implements HttpServletRequest { } public String[] getParameterValues(String name) { - Assert.notNull(name, "Parameter name must not be null"); - return this.parameters.get(name); + return (name != null ? this.parameters.get(name) : null); } public Map getParameterMap() { @@ -509,8 +513,8 @@ public class MockHttpServletRequest implements HttpServletRequest { public BufferedReader getReader() throws UnsupportedEncodingException { if (this.content != null) { InputStream sourceStream = new ByteArrayInputStream(this.content); - Reader sourceReader = (this.characterEncoding != null) ? new InputStreamReader(sourceStream, - this.characterEncoding) : new InputStreamReader(sourceStream); + Reader sourceReader = (this.characterEncoding != null) ? + new InputStreamReader(sourceStream, this.characterEncoding) : new InputStreamReader(sourceStream); return new BufferedReader(sourceReader); } else { @@ -574,7 +578,7 @@ public class MockHttpServletRequest implements HttpServletRequest { * @since 3.2 */ public void setPreferredLocales(List locales) { - Assert.notEmpty(locales, "preferred locales list must not be empty"); + Assert.notEmpty(locales, "Locale list must not be empty"); this.locales.clear(); this.locales.addAll(locales); } @@ -635,6 +639,7 @@ public class MockHttpServletRequest implements HttpServletRequest { return this.localPort; } + // --------------------------------------------------------------------- // HttpServletRequest interface // --------------------------------------------------------------------- @@ -797,8 +802,8 @@ public class MockHttpServletRequest implements HttpServletRequest { } public boolean isUserInRole(String role) { - return (this.userRoles.contains(role) || (this.servletContext instanceof MockServletContext && ((MockServletContext) this.servletContext).getDeclaredRoles().contains( - role))); + return (this.userRoles.contains(role) || (this.servletContext instanceof MockServletContext && + ((MockServletContext) this.servletContext).getDeclaredRoles().contains(role))); } public void setUserPrincipal(Principal userPrincipal) { diff --git a/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletRequestTests.java b/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletRequestTests.java index 085bbb8b7db..a237bb00d1e 100644 --- a/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletRequestTests.java +++ b/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletRequestTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,8 +16,6 @@ package org.springframework.mock.web; -import static org.junit.Assert.*; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -29,6 +27,8 @@ import java.util.Map; import org.junit.Test; +import static org.junit.Assert.*; + /** * Unit tests for {@link MockHttpServletRequest}. * @@ -105,6 +105,12 @@ public class MockHttpServletRequestTests { assertEquals("HTTP header casing not being preserved", headerName, requestHeaders.nextElement()); } + @Test + public void nullParameterName() { + assertNull(request.getParameter(null)); + assertNull(request.getParameterValues(null)); + } + @Test public void setMultipleParameters() { request.setParameter("key1", "value1"); diff --git a/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletRequest.java b/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletRequest.java index 5694b666105..c5dc6d76010 100644 --- a/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletRequest.java +++ b/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -36,7 +36,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; - import javax.servlet.AsyncContext; import javax.servlet.DispatcherType; import javax.servlet.RequestDispatcher; @@ -110,9 +109,9 @@ public class MockHttpServletRequest implements HttpServletRequest { private boolean active = true; - //--------------------------------------------------------------------- + // --------------------------------------------------------------------- // ServletRequest properties - //--------------------------------------------------------------------- + // --------------------------------------------------------------------- private final Map attributes = new LinkedHashMap(); @@ -151,11 +150,12 @@ public class MockHttpServletRequest implements HttpServletRequest { private int localPort = DEFAULT_SERVER_PORT; - private Map parts = new HashMap(); + private final Map parts = new HashMap(); - //--------------------------------------------------------------------- + + // --------------------------------------------------------------------- // HttpServletRequest properties - //--------------------------------------------------------------------- + // --------------------------------------------------------------------- private String authType; @@ -200,9 +200,9 @@ public class MockHttpServletRequest implements HttpServletRequest { private DispatcherType dispatcherType = DispatcherType.REQUEST; - //--------------------------------------------------------------------- + // --------------------------------------------------------------------- // Constructors - //--------------------------------------------------------------------- + // --------------------------------------------------------------------- /** * Create a new {@code MockHttpServletRequest} with a default @@ -256,9 +256,10 @@ public class MockHttpServletRequest implements HttpServletRequest { this.locales.add(Locale.ENGLISH); } - //--------------------------------------------------------------------- + + // --------------------------------------------------------------------- // Lifecycle methods - //--------------------------------------------------------------------- + // --------------------------------------------------------------------- /** * Return the ServletContext that this request is associated with. (Not @@ -302,9 +303,9 @@ public class MockHttpServletRequest implements HttpServletRequest { } - //--------------------------------------------------------------------- + // --------------------------------------------------------------------- // ServletRequest interface - //--------------------------------------------------------------------- + // --------------------------------------------------------------------- @Override public Object getAttribute(String name) { @@ -414,8 +415,7 @@ public class MockHttpServletRequest implements HttpServletRequest { } else { throw new IllegalArgumentException( - "Parameter map value must be single value " + " or array of type [" + String.class.getName() + - "]"); + "Parameter map value must be single value " + " or array of type [" + String.class.getName() + "]"); } } } @@ -490,8 +490,7 @@ public class MockHttpServletRequest implements HttpServletRequest { @Override public String getParameter(String name) { - Assert.notNull(name, "Parameter name must not be null"); - String[] arr = this.parameters.get(name); + String[] arr = (name != null ? this.parameters.get(name) : null); return (arr != null && arr.length > 0 ? arr[0] : null); } @@ -502,8 +501,7 @@ public class MockHttpServletRequest implements HttpServletRequest { @Override public String[] getParameterValues(String name) { - Assert.notNull(name, "Parameter name must not be null"); - return this.parameters.get(name); + return (name != null ? this.parameters.get(name) : null); } @Override @@ -620,7 +618,7 @@ public class MockHttpServletRequest implements HttpServletRequest { * @since 3.2 */ public void setPreferredLocales(List locales) { - Assert.notEmpty(locales, "preferred locales list must not be empty"); + Assert.notEmpty(locales, "Locale list must not be empty"); this.locales.clear(); this.locales.addAll(locales); } @@ -779,7 +777,7 @@ public class MockHttpServletRequest implements HttpServletRequest { @Override public String getHeader(String name) { HeaderValueHolder header = HeaderValueHolder.getByName(this.headers, name); - return (header != null ? header.getValue().toString() : null); + return (header != null ? header.getStringValue() : null); } @Override @@ -867,8 +865,8 @@ public class MockHttpServletRequest implements HttpServletRequest { @Override public boolean isUserInRole(String role) { - return (this.userRoles.contains(role) || (this.servletContext instanceof MockServletContext && ((MockServletContext) this.servletContext).getDeclaredRoles().contains( - role))); + return (this.userRoles.contains(role) || (this.servletContext instanceof MockServletContext && + ((MockServletContext) this.servletContext).getDeclaredRoles().contains(role))); } public void setUserPrincipal(Principal userPrincipal) { From 5a773b771d2b300888df33128f6c3993e2bd50b9 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 21 Jan 2013 11:33:13 +0100 Subject: [PATCH 103/143] MockHttpServletResponse's getHeaderNames declares Collection instead of Set for Servlet 3.0 compatibility Issue: SPR-9885 --- .../mock/web/MockHttpServletResponse.java | 9 +++++---- .../mock/web/MockHttpServletResponseTests.java | 11 +++++------ .../mock/web/test/MockHttpServletResponse.java | 15 +++++++-------- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletResponse.java b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletResponse.java index c86e95a93a7..75a7a1fc3ba 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletResponse.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,11 +24,11 @@ import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Set; import javax.servlet.ServletOutputStream; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; @@ -56,6 +56,7 @@ public class MockHttpServletResponse implements HttpServletResponse { private static final String LOCATION_HEADER = "Location"; + //--------------------------------------------------------------------- // ServletResponse properties //--------------------------------------------------------------------- @@ -145,7 +146,7 @@ public class MockHttpServletResponse implements HttpServletResponse { private void updateContentTypeHeader() { if (this.contentType != null) { StringBuilder sb = new StringBuilder(this.contentType); - if (this.contentType.toLowerCase().indexOf(CHARSET_PREFIX) == -1 && this.charset) { + if (!this.contentType.toLowerCase().contains(CHARSET_PREFIX) && this.charset) { sb.append(";").append(CHARSET_PREFIX).append(this.characterEncoding); } doAddHeaderValue(CONTENT_TYPE_HEADER, sb.toString(), true); @@ -299,7 +300,7 @@ public class MockHttpServletResponse implements HttpServletResponse { *

As of Servlet 3.0, this method is also defined HttpServletResponse. * @return the {@code Set} of header name {@code Strings}, or an empty {@code Set} if none */ - public Set getHeaderNames() { + public Collection getHeaderNames() { return this.headers.keySet(); } diff --git a/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletResponseTests.java b/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletResponseTests.java index c16f7cab9e3..867b86ad90a 100644 --- a/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletResponseTests.java +++ b/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletResponseTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,18 +16,17 @@ package org.springframework.mock.web; -import static org.junit.Assert.*; - import java.io.IOException; import java.util.Arrays; -import java.util.Set; - +import java.util.Collection; import javax.servlet.http.HttpServletResponse; import org.junit.Test; import org.springframework.web.util.WebUtils; +import static org.junit.Assert.*; + /** * Unit tests for {@link MockHttpServletResponse}. * @@ -127,7 +126,7 @@ public class MockHttpServletResponseTests { public void httpHeaderNameCasingIsPreserved() throws Exception { final String headerName = "Header1"; response.addHeader(headerName, "value1"); - Set responseHeaders = response.getHeaderNames(); + Collection responseHeaders = response.getHeaderNames(); assertNotNull(responseHeaders); assertEquals(1, responseHeaders.size()); assertEquals("HTTP header casing not being preserved", headerName, responseHeaders.iterator().next()); diff --git a/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletResponse.java b/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletResponse.java index 6f52ab8e3e8..990026770db 100644 --- a/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletResponse.java +++ b/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,11 +24,11 @@ import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Set; import javax.servlet.ServletOutputStream; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; @@ -38,11 +38,9 @@ import org.springframework.util.LinkedCaseInsensitiveMap; import org.springframework.web.util.WebUtils; /** - * Mock implementation of the {@link javax.servlet.http.HttpServletResponse} - * interface. Supports the Servlet 3.0 API level + * Mock implementation of the {@link javax.servlet.http.HttpServletResponse} interface. * - *

Used for testing the web framework; also useful for testing - * application controllers. + *

Compatible with Servlet 2.5 as well as Servlet 3.0. * * @author Juergen Hoeller * @author Rod Johnson @@ -58,6 +56,7 @@ public class MockHttpServletResponse implements HttpServletResponse { private static final String LOCATION_HEADER = "Location"; + //--------------------------------------------------------------------- // ServletResponse properties //--------------------------------------------------------------------- @@ -148,7 +147,7 @@ public class MockHttpServletResponse implements HttpServletResponse { private void updateContentTypeHeader() { if (this.contentType != null) { StringBuilder sb = new StringBuilder(this.contentType); - if (this.contentType.toLowerCase().indexOf(CHARSET_PREFIX) == -1 && this.charset) { + if (!this.contentType.toLowerCase().contains(CHARSET_PREFIX) && this.charset) { sb.append(";").append(CHARSET_PREFIX).append(this.characterEncoding); } doAddHeaderValue(CONTENT_TYPE_HEADER, sb.toString(), true); @@ -319,7 +318,7 @@ public class MockHttpServletResponse implements HttpServletResponse { * @return the {@code Set} of header name {@code Strings}, or an empty {@code Set} if none */ @Override - public Set getHeaderNames() { + public Collection getHeaderNames() { return this.headers.keySet(); } From 8e75eee9b2b35f807fefe9575e59be5e2f4768eb Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 22 Jan 2013 16:46:01 +0100 Subject: [PATCH 104/143] SpringValidationAdapter properly detects invalid value for JSR-303 field-level bean constraints Issue: SPR-9332 --- .../SpringValidatorAdapter.java | 17 +++-- .../beanvalidation/ValidatorFactoryTests.java | 68 +++++++++++++++++-- 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java index 1a51c37bb9b..7565e82099c 100644 --- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java +++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -99,7 +99,8 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation. } } } - processConstraintViolations(this.targetValidator.validate(target, groups.toArray(new Class[groups.size()])), errors); + processConstraintViolations( + this.targetValidator.validate(target, groups.toArray(new Class[groups.size()])), errors); } /** @@ -114,10 +115,11 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation. FieldError fieldError = errors.getFieldError(field); if (fieldError == null || !fieldError.isBindingFailure()) { try { - String errorCode = violation.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName(); - Object[] errorArgs = getArgumentsForConstraint(errors.getObjectName(), field, violation.getConstraintDescriptor()); + ConstraintDescriptor cd = violation.getConstraintDescriptor(); + String errorCode = cd.getAnnotation().annotationType().getSimpleName(); + Object[] errorArgs = getArgumentsForConstraint(errors.getObjectName(), field, cd); if (errors instanceof BindingResult) { - // can do custom FieldError registration with invalid value from ConstraintViolation, + // Can do custom FieldError registration with invalid value from ConstraintViolation, // as necessary for Hibernate Validator compatibility (non-indexed set path in field) BindingResult bindingResult = (BindingResult) errors; String nestedField = bindingResult.getNestedPath() + field; @@ -128,8 +130,9 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation. } else { Object invalidValue = violation.getInvalidValue(); - if (!"".equals(field) && invalidValue == violation.getLeafBean()) { - // bean constraint with property path: retrieve the actual property value + if (field.contains(".") && !field.contains("[]")) { + // Possibly a bean constraint with property path: retrieve the actual property value. + // However, explicitly avoid this for "address[]" style paths that we can't handle. invalidValue = bindingResult.getRawFieldValue(field); } String[] errorCodes = bindingResult.resolveMessageCodes(errorCode, field); diff --git a/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java b/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java index 5c2dad22a4b..3e6cac57598 100644 --- a/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java +++ b/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,6 +30,7 @@ import javax.validation.Constraint; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import javax.validation.ConstraintViolation; +import javax.validation.Payload; import javax.validation.Valid; import javax.validation.constraints.NotNull; @@ -37,6 +38,7 @@ import org.hibernate.validator.HibernateValidator; import org.junit.Test; import org.springframework.validation.BeanPropertyBindingResult; +import org.springframework.validation.Errors; import org.springframework.validation.FieldError; import org.springframework.validation.ObjectError; @@ -193,6 +195,18 @@ public class ValidatorFactoryTests { System.out.println(fieldError.getDefaultMessage()); } + @Test + public void testInnerBeanValidation() throws Exception { + LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); + validator.afterPropertiesSet(); + + MainBean mainBean = new MainBean(); + Errors errors = new BeanPropertyBindingResult(mainBean, "mainBean"); + validator.validate(mainBean, errors); + Object rejected = errors.getFieldValue("inner.value"); + assertNull(rejected); + } + @NameAddressValid public static class ValidPerson { @@ -242,7 +256,6 @@ public class ValidatorFactoryTests { } } - public static class ValidAddress { @NotNull @@ -257,7 +270,6 @@ public class ValidatorFactoryTests { } } - @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = NameAddressValidator.class) @@ -270,7 +282,6 @@ public class ValidatorFactoryTests { Class[] payload() default {}; } - public static class NameAddressValidator implements ConstraintValidator { @Override @@ -283,4 +294,53 @@ public class ValidatorFactoryTests { } } + + public static class MainBean { + + @InnerValid + private InnerBean inner = new InnerBean(); + + public InnerBean getInner() { + return inner; + } + } + + public static class InnerBean { + + private String value; + + public String getValue() { + return value; + } + public void setValue(String value) { + this.value = value; + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + @Constraint(validatedBy=InnerValidator.class) + public static @interface InnerValid { + String message() default "NOT VALID"; + Class[] groups() default { }; + Class[] payload() default {}; + } + + public static class InnerValidator implements ConstraintValidator { + + @Override + public void initialize(InnerValid constraintAnnotation) { + } + + @Override + public boolean isValid(InnerBean bean, ConstraintValidatorContext context) { + context.disableDefaultConstraintViolation(); + if (bean.getValue() == null) { + context.buildConstraintViolationWithTemplate("NULL"). addNode("value").addConstraintViolation(); + return false; + } + return true; + } + } + } From 1a929f22e0e8d136a11605efccf211a19ec63894 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 22 Jan 2013 20:38:03 +0100 Subject: [PATCH 105/143] EntityManagerFactoryUtils finds default EntityManagerFactory in parent contexts as well Also introduces consistent use of getBean(Class) for similar use cases across the framework, accepting a locally unique target bean even if further matching beans would be available in parent contexts (in contrast to BeanFactoryUtils.beanOfType's behavior). Issue: SPR-10160 --- .../factory/config/ServiceLocatorFactoryBean.java | 9 ++++----- .../orm/jpa/EntityManagerFactoryUtils.java | 9 ++++++--- .../interceptor/TransactionAspectSupport.java | 10 ++-------- .../interceptor/TransactionProxyFactoryBean.java | 6 +++--- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.java index e8f2b72b6c7..1ded4154aa2 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -27,7 +27,6 @@ import org.springframework.beans.BeansException; import org.springframework.beans.FatalBeanException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.ListableBeanFactory; @@ -364,12 +363,12 @@ public class ServiceLocatorFactoryBean implements FactoryBean, BeanFacto try { String beanName = tryGetBeanName(args); if (StringUtils.hasLength(beanName)) { - // Service locator for a specific bean name. + // Service locator for a specific bean name return beanFactory.getBean(beanName, serviceLocatorMethodReturnType); } else { - // Service locator for a bean type. - return BeanFactoryUtils.beanOfTypeIncludingAncestors(beanFactory, serviceLocatorMethodReturnType); + // Service locator for a bean type + return beanFactory.getBean(serviceLocatorMethodReturnType); } } catch (BeansException ex) { diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/EntityManagerFactoryUtils.java b/spring-orm/src/main/java/org/springframework/orm/jpa/EntityManagerFactoryUtils.java index 9419950001e..044cb72be38 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/EntityManagerFactoryUtils.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/EntityManagerFactoryUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -76,10 +76,12 @@ public abstract class EntityManagerFactoryUtils { * Find an EntityManagerFactory with the given name in the given * Spring application context (represented as ListableBeanFactory). *

The specified unit name will be matched against the configured - * peristence unit, provided that a discovered EntityManagerFactory + * persistence unit, provided that a discovered EntityManagerFactory * implements the {@link EntityManagerFactoryInfo} interface. If not, * the persistence unit name will be matched against the Spring bean name, * assuming that the EntityManagerFactory bean names follow that convention. + *

If no unit name has been given, this method will search for a default + * EntityManagerFactory through {@link ListableBeanFactory#getBean(Class)}. * @param beanFactory the ListableBeanFactory to search * @param unitName the name of the persistence unit (may be {@code null} or empty, * in which case a single bean of type EntityManagerFactory will be searched for) @@ -108,7 +110,8 @@ public abstract class EntityManagerFactoryUtils { return beanFactory.getBean(unitName, EntityManagerFactory.class); } else { - return BeanFactoryUtils.beanOfType(beanFactory, EntityManagerFactory.class); + // Find unique EntityManagerFactory bean in the context, falling back to parent contexts. + return beanFactory.getBean(EntityManagerFactory.class); } } diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java index 8d15342c02d..9f9b9503ddf 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,9 +24,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; import org.springframework.core.NamedThreadLocal; import org.springframework.transaction.NoTransactionException; @@ -247,12 +245,8 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init else if (this.transactionManagerBeanName != null) { return this.beanFactory.getBean(this.transactionManagerBeanName, PlatformTransactionManager.class); } - else if (this.beanFactory instanceof ListableBeanFactory) { - return BeanFactoryUtils.beanOfTypeIncludingAncestors(((ListableBeanFactory) this.beanFactory), PlatformTransactionManager.class); - } else { - throw new IllegalStateException( - "Cannot retrieve PlatformTransactionManager beans from non-listable BeanFactory: " + this.beanFactory); + return this.beanFactory.getBean(PlatformTransactionManager.class); } } diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionProxyFactoryBean.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionProxyFactoryBean.java index eaf963cc397..62b408e155c 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionProxyFactoryBean.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionProxyFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -176,8 +176,8 @@ public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBe /** * This callback is optional: If running in a BeanFactory and no transaction * manager has been set explicitly, a single matching bean of type - * PlatformTransactionManager will be fetched from the BeanFactory. - * @see org.springframework.beans.factory.BeanFactoryUtils#beanOfTypeIncludingAncestors + * {@link PlatformTransactionManager} will be fetched from the BeanFactory. + * @see org.springframework.beans.factory.BeanFactory#getBean(Class) * @see org.springframework.transaction.PlatformTransactionManager */ public void setBeanFactory(BeanFactory beanFactory) { From 8f103c2ea195210ac84cfc9fd56b9e21f32ecc85 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 22 Jan 2013 21:10:08 +0100 Subject: [PATCH 106/143] Introduced NoUniqueBeanDefinitionException as a dedicated subclass of NoSuchBeanDefinitionException Issue: SPR-10194 --- .../beans/factory/BeanFactory.java | 9 ++- .../beans/factory/BeanFactoryUtils.java | 61 +++++++------- .../NoSuchBeanDefinitionException.java | 29 ++++--- .../NoUniqueBeanDefinitionException.java | 79 +++++++++++++++++++ .../support/DefaultListableBeanFactory.java | 16 ++-- .../support/StaticListableBeanFactory.java | 8 +- .../DefaultListableBeanFactoryTests.java | 9 ++- ...ersistenceAnnotationBeanPostProcessor.java | 11 +-- 8 files changed, 163 insertions(+), 59 deletions(-) create mode 100644 spring-beans/src/main/java/org/springframework/beans/factory/NoUniqueBeanDefinitionException.java diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java index 2d18e67b1ac..8c73a16fd7a 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -143,7 +143,7 @@ public interface BeanFactory { * is {@code Object.class}, this method will succeed whatever the class of the * returned instance. * @return an instance of the bean - * @throws NoSuchBeanDefinitionException if there's no such bean definition + * @throws NoSuchBeanDefinitionException if there is no such bean definition * @throws BeanNotOfRequiredTypeException if the bean is not of the required type * @throws BeansException if the bean could not be created */ @@ -158,7 +158,8 @@ public interface BeanFactory { * of the given type. For more extensive retrieval operations across sets of beans, * use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}. * @return an instance of the single bean matching the required type - * @throws NoSuchBeanDefinitionException if there is not exactly one matching bean found + * @throws NoSuchBeanDefinitionException if no bean of the given type was found + * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found * @since 3.0 * @see ListableBeanFactory */ @@ -172,7 +173,7 @@ public interface BeanFactory { * @param args arguments to use if creating a prototype using explicit arguments to a * static factory method. It is invalid to use a non-null args value in any other case. * @return an instance of the bean - * @throws NoSuchBeanDefinitionException if there's no such bean definition + * @throws NoSuchBeanDefinitionException if there is no such bean definition * @throws BeanDefinitionStoreException if arguments have been given but * the affected bean isn't a prototype * @throws BeansException if the bean could not be created diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java index 2a4f577a1c0..657bb1ebd1d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -310,20 +310,15 @@ public abstract class BeanFactoryUtils { * @param lbf the bean factory * @param type type of bean to match * @return the matching bean instance - * @throws NoSuchBeanDefinitionException - * if 0 or more than 1 beans of the given type were found + * @throws NoSuchBeanDefinitionException if no bean of the given type was found + * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found * @throws BeansException if the bean could not be created */ public static T beanOfTypeIncludingAncestors(ListableBeanFactory lbf, Class type) throws BeansException { Map beansOfType = beansOfTypeIncludingAncestors(lbf, type); - if (beansOfType.size() == 1) { - return beansOfType.values().iterator().next(); - } - else { - throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size()); - } + return uniqueBean(type, beansOfType); } /** @@ -351,8 +346,8 @@ public abstract class BeanFactoryUtils { * eagerly initialized to determine their type: So be aware that passing in "true" * for this flag will initialize FactoryBeans and "factory-bean" references. * @return the matching bean instance - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException - * if 0 or more than 1 beans of the given type were found + * @throws NoSuchBeanDefinitionException if no bean of the given type was found + * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found * @throws BeansException if the bean could not be created */ public static T beanOfTypeIncludingAncestors( @@ -360,12 +355,7 @@ public abstract class BeanFactoryUtils { throws BeansException { Map beansOfType = beansOfTypeIncludingAncestors(lbf, type, includeNonSingletons, allowEagerInit); - if (beansOfType.size() == 1) { - return beansOfType.values().iterator().next(); - } - else { - throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size()); - } + return uniqueBean(type, beansOfType); } /** @@ -380,19 +370,14 @@ public abstract class BeanFactoryUtils { * @param lbf the bean factory * @param type type of bean to match * @return the matching bean instance - * @throws NoSuchBeanDefinitionException - * if 0 or more than 1 beans of the given type were found + * @throws NoSuchBeanDefinitionException if no bean of the given type was found + * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found * @throws BeansException if the bean could not be created */ public static T beanOfType(ListableBeanFactory lbf, Class type) throws BeansException { Assert.notNull(lbf, "ListableBeanFactory must not be null"); Map beansOfType = lbf.getBeansOfType(type); - if (beansOfType.size() == 1) { - return beansOfType.values().iterator().next(); - } - else { - throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size()); - } + return uniqueBean(type, beansOfType); } /** @@ -415,8 +400,8 @@ public abstract class BeanFactoryUtils { * eagerly initialized to determine their type: So be aware that passing in "true" * for this flag will initialize FactoryBeans and "factory-bean" references. * @return the matching bean instance - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException - * if 0 or more than 1 beans of the given type were found + * @throws NoSuchBeanDefinitionException if no bean of the given type was found + * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found * @throws BeansException if the bean could not be created */ public static T beanOfType( @@ -425,11 +410,27 @@ public abstract class BeanFactoryUtils { Assert.notNull(lbf, "ListableBeanFactory must not be null"); Map beansOfType = lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit); - if (beansOfType.size() == 1) { - return beansOfType.values().iterator().next(); + return uniqueBean(type, beansOfType); + } + + /** + * Extract a unique bean for the given type from the given Map of matching beans. + * @param type type of bean to match + * @param matchingBeans all matching beans found + * @return the unique bean instance + * @throws NoSuchBeanDefinitionException if no bean of the given type was found + * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found + */ + private static T uniqueBean(Class type, Map matchingBeans) { + int nrFound = matchingBeans.size(); + if (nrFound == 1) { + return matchingBeans.values().iterator().next(); + } + else if (nrFound > 1) { + throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet()); } else { - throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size()); + throw new NoSuchBeanDefinitionException(type); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java b/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java index 0157d086506..5bd8ea7a9f1 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,11 +20,13 @@ import org.springframework.beans.BeansException; import org.springframework.util.StringUtils; /** - * Exception thrown when a {@code BeanFactory} is asked for a bean - * instance for which it cannot find a definition. + * Exception thrown when a {@code BeanFactory} is asked for a bean instance + * for which it cannot find a definition. * * @author Rod Johnson * @author Juergen Hoeller + * @see BeanFactory#getBean(String) + * @see BeanFactory#getBean(Class) */ @SuppressWarnings("serial") public class NoSuchBeanDefinitionException extends BeansException { @@ -60,7 +62,7 @@ public class NoSuchBeanDefinitionException extends BeansException { * @param type required type of the missing bean */ public NoSuchBeanDefinitionException(Class type) { - super("No unique bean of type [" + type.getName() + "] is defined"); + super("No qualifying bean of type [" + type.getName() + "] is defined"); this.beanType = type; } @@ -70,7 +72,7 @@ public class NoSuchBeanDefinitionException extends BeansException { * @param message detailed message describing the problem */ public NoSuchBeanDefinitionException(Class type, String message) { - super("No unique bean of type [" + type.getName() + "] is defined: " + message); + super("No qualifying bean of type [" + type.getName() + "] is defined: " + message); this.beanType = type; } @@ -81,7 +83,7 @@ public class NoSuchBeanDefinitionException extends BeansException { * @param message detailed message describing the problem */ public NoSuchBeanDefinitionException(Class type, String dependencyDescription, String message) { - super("No matching bean of type [" + type.getName() + "] found for dependency" + + super("No qualifying bean of type [" + type.getName() + "] found for dependency" + (StringUtils.hasLength(dependencyDescription) ? " [" + dependencyDescription + "]" : "") + ": " + message); this.beanType = type; @@ -89,19 +91,26 @@ public class NoSuchBeanDefinitionException extends BeansException { /** - * Return the name of the missing bean, if it was a lookup by name - * that failed. + * Return the name of the missing bean, if it was a lookup by name that failed. */ public String getBeanName() { return this.beanName; } /** - * Return the required type of the missing bean, if it was a lookup - * by type that failed. + * Return the required type of the missing bean, if it was a lookup by type that failed. */ public Class getBeanType() { return this.beanType; } + /** + * Return the number of beans found when only one matching bean was expected. + * For a regular NoSuchBeanDefinitionException, this will always be 0. + * @see NoUniqueBeanDefinitionException + */ + public int getNumberOfBeansFound() { + return 0; + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/NoUniqueBeanDefinitionException.java b/spring-beans/src/main/java/org/springframework/beans/factory/NoUniqueBeanDefinitionException.java new file mode 100644 index 00000000000..7db0bae6e12 --- /dev/null +++ b/spring-beans/src/main/java/org/springframework/beans/factory/NoUniqueBeanDefinitionException.java @@ -0,0 +1,79 @@ +/* + * Copyright 2002-2013 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.beans.factory; + +import java.util.Arrays; +import java.util.Collection; + +import org.springframework.util.StringUtils; + +/** + * Exception thrown when a {@code BeanFactory} is asked for a bean instance for which + * multiple matching candidates have been found when only one matching bean was expected. + * + * @author Juergen Hoeller + * @since 3.2.1 + * @see BeanFactory#getBean(Class) + */ +@SuppressWarnings("serial") +public class NoUniqueBeanDefinitionException extends NoSuchBeanDefinitionException { + + private int numberOfBeansFound; + + + /** + * Create a new {@code NoUniqueBeanDefinitionException}. + * @param type required type of the non-unique bean + * @param numberOfBeansFound the number of matching beans + * @param message detailed message describing the problem + */ + public NoUniqueBeanDefinitionException(Class type, int numberOfBeansFound, String message) { + super(type, message); + this.numberOfBeansFound = numberOfBeansFound; + } + + /** + * Create a new {@code NoUniqueBeanDefinitionException}. + * @param type required type of the non-unique bean + * @param beanNamesFound the names of all matching beans (as a Collection) + */ + public NoUniqueBeanDefinitionException(Class type, Collection beanNamesFound) { + this(type, beanNamesFound.size(), "expected single matching bean but found " + beanNamesFound.size() + ": " + + StringUtils.collectionToCommaDelimitedString(beanNamesFound)); + } + + /** + * Create a new {@code NoUniqueBeanDefinitionException}. + * @param type required type of the non-unique bean + * @param beanNamesFound the names of all matching beans (as an array) + */ + public NoUniqueBeanDefinitionException(Class type, String... beanNamesFound) { + this(type, Arrays.asList(beanNamesFound)); + } + + + /** + * Return the number of beans found when only one matching bean was expected. + * For a NoUniqueBeanDefinitionException, this will usually be higher than 1. + * @see #getBeanType() + */ + @Override + public int getNumberOfBeansFound() { + return this.numberOfBeansFound; + } + +} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 824caaaec5e..432663ab793 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,6 +50,7 @@ import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.CannotLoadBeanClassException; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.SmartFactoryBean; import org.springframework.beans.factory.config.BeanDefinition; @@ -270,12 +271,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto if (beanNames.length == 1) { return getBean(beanNames[0], requiredType); } - else if (beanNames.length == 0 && getParentBeanFactory() != null) { + else if (beanNames.length > 1) { + throw new NoUniqueBeanDefinitionException(requiredType, beanNames); + } + else if (getParentBeanFactory() != null) { return getParentBeanFactory().getBean(requiredType); } else { - throw new NoSuchBeanDefinitionException(requiredType, "expected single bean but found " + - beanNames.length + ": " + StringUtils.arrayToCommaDelimitedString(beanNames)); + throw new NoSuchBeanDefinitionException(requiredType); } } @@ -823,8 +826,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto if (matchingBeans.size() > 1) { String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor); if (primaryBeanName == null) { - throw new NoSuchBeanDefinitionException(type, "expected single matching bean but found " + - matchingBeans.size() + ": " + matchingBeans.keySet()); + throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet()); } if (autowiredBeanNames != null) { autowiredBeanNames.add(primaryBeanName); @@ -895,7 +897,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto boolean candidateLocal = containsBeanDefinition(candidateBeanName); boolean primaryLocal = containsBeanDefinition(primaryBeanName); if (candidateLocal == primaryLocal) { - throw new NoSuchBeanDefinitionException(descriptor.getDependencyType(), + throw new NoUniqueBeanDefinitionException(descriptor.getDependencyType(), candidateBeans.size(), "more than one 'primary' bean found among candidates: " + candidateBeans.keySet()); } else if (candidateLocal && !primaryLocal) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java index 3e8784b1817..4eef1b4751f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2013 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,6 +31,7 @@ import org.springframework.beans.factory.BeanNotOfRequiredTypeException; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.SmartFactoryBean; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.StringUtils; @@ -117,8 +118,11 @@ public class StaticListableBeanFactory implements ListableBeanFactory { if (beanNames.length == 1) { return getBean(beanNames[0], requiredType); } + else if (beanNames.length > 1) { + throw new NoUniqueBeanDefinitionException(requiredType, beanNames); + } else { - throw new NoSuchBeanDefinitionException(requiredType, "expected single bean but found " + beanNames.length); + throw new NoSuchBeanDefinitionException(requiredType); } } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index acf9f65a05a..e93e95973d6 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -1270,6 +1270,12 @@ public class DefaultListableBeanFactoryTests { } @Test(expected=NoSuchBeanDefinitionException.class) + public void testGetBeanByTypeWithNoneFound() { + DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); + lbf.getBean(TestBean.class); + } + + @Test(expected=NoUniqueBeanDefinitionException.class) public void testGetBeanByTypeWithAmbiguity() { DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class); @@ -1296,7 +1302,8 @@ public class DefaultListableBeanFactoryTests { try { lbf.getBean(TestBean.class); fail("Should have thrown NoSuchBeanDefinitionException"); - } catch (NoSuchBeanDefinitionException ex) { + } + catch (NoSuchBeanDefinitionException ex) { // expected } } diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java b/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java index ac9c408f02c..39c437e8cdf 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.annotation.InjectionMetadata; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor; @@ -60,7 +61,6 @@ import org.springframework.orm.jpa.ExtendedEntityManagerCreator; import org.springframework.orm.jpa.SharedEntityManagerCreator; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; /** * BeanPostProcessor that processes {@link javax.persistence.PersistenceUnit} @@ -534,10 +534,11 @@ public class PersistenceAnnotationBeanPostProcessor } return emf; } + else if (beanNames.length > 1) { + throw new NoUniqueBeanDefinitionException(EntityManagerFactory.class, beanNames); + } else { - throw new NoSuchBeanDefinitionException( - EntityManagerFactory.class, "expected single bean but found " + beanNames.length + ": " + - StringUtils.arrayToCommaDelimitedString(beanNames)); + throw new NoSuchBeanDefinitionException(EntityManagerFactory.class); } } From 0a09da7534395acc53dcb423a7b42f64d8403dec Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 22 Jan 2013 21:10:47 +0100 Subject: [PATCH 107/143] Final preparations for 3.2.1 --- src/dist/changelog.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/dist/changelog.txt b/src/dist/changelog.txt index 8c0d7fbfab3..771470cc1d3 100644 --- a/src/dist/changelog.txt +++ b/src/dist/changelog.txt @@ -12,25 +12,30 @@ Changes in version 3.2.1 (2013-01-24) * BridgeMethodResolver properly handles bridge methods in interfaces (SPR-9330) * LocalVariableTableParameterNameDiscoverer works for bridge methods as well (SPR-9429) * CachedIntrospectionResults.clearClassLoader(null) removes cached classes for the system class loader (SPR-9189) +* introduced NoUniqueBeanDefinitionException as a dedicated subclass of NoSuchBeanDefinitionException (SPR-10194) * fixed QualifierAnnotationAutowireCandidateResolver's detection of custom qualifier annotations (SPR-10107) * fixed AbstractAutoProxyCreator to accept null bean names again (SPR-10108) * AbstractAdvisingBeanPostProcessor caches per bean target class, working for null bean names as well (SPR-10144) * MessageSourceResourceBundle overrides JDK 1.6 containsKey method, avoiding NPE in getKeys (SPR-10136) +* SpringValidationAdapter properly detects invalid value for JSR-303 field-level bean constraints (SPR-9332) * SpringBeanAutowiringInterceptor eagerly releases BeanFactory if post-construction fails (SPR-10013) * added "exposeAccessContext" flag JndiRmiClientInterceptor/ProxyFactoryBean (for WebLogic; SPR-9428) * MBeanExporter does not log warnings for manually unregistered MBeans (SPR-9451) * AbstractCacheManager accepts no caches defined, allowing for EHCache default cache setup (SPR-7955) * EhCacheManagerFactoryBean applies cacheManagerName ahead of creation (for EHCache 2.5 compatibility; SPR-9171) -* JDBC parameter binding uses JDBC 3.0 ParameterMetaData (if available) for type determination (SPR-10084) * reintroduced "mode" and "proxy-target-class" attributes in spring-task-3.1/3.2.xsd (SPR-10177) * spring-task-3.2.xsd allows for SpEL expressions in initial-delay attribute (SPR-10102) * spring-jms-3.2.xsd allows for SpEL expressions in prefetch and receive-timeout attributes (SPR-9553) * JmsTemplate uses configured receiveTimeout if shorter than remaining transaction timeout (SPR-10109) * added MappingJackson2MessageConverter for JMS (SPR-10099) +* JDBC parameter binding uses JDBC 3.0 ParameterMetaData (if available) for type determination (SPR-10084) +* JpaTransactionManager etc finds default EntityManagerFactory in parent context as well (SPR-10160) * MimeMessageHelper encodes attachment filename if not ASCII compliant (SPR-9258) * FreeMarkerConfigurationFactory properly supports TemplateLoaders when recreating Configurations (SPR-9389) * SpringContextResourceAdapter implements equals/hashCode according to the JCA 1.5 contract (SPR-9162) * ContextLoader properly detects pre-refreshed WebApplicationContext (SPR-9996) +* MockHttpServletRequest's getParameter(Values) returns null for null parameter name (SPR-10192) +* MockHttpServletResponse's getHeaderNames declares Collection instead of Set for Servlet 3.0 compatibility (SPR-9885) Changes in version 3.2 GA (2012-12-13) From a3e190e1bf80b88ce049f540a05bdf9706e140a4 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 22 Jan 2013 17:20:24 -0500 Subject: [PATCH 108/143] Support sub-types of ResponseEntity Issue: SPR-10207 --- .../mvc/method/annotation/HttpEntityMethodProcessor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java index e93029aa640..edb113e6079 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,7 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro public boolean supportsReturnType(MethodParameter returnType) { Class parameterType = returnType.getParameterType(); - return HttpEntity.class.equals(parameterType) || ResponseEntity.class.equals(parameterType); + return HttpEntity.class.isAssignableFrom(parameterType) || ResponseEntity.class.isAssignableFrom(parameterType); } public Object resolveArgument( From 0efdd3d5668115b5463c119392e93dc830c9de60 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 22 Jan 2013 22:10:29 +0100 Subject: [PATCH 109/143] Aligned XML scheduled-task elements with @Scheduled in terms of kicking in after context refresh Issue: SPR-9231 --- ...ontextLifecycleScheduledTaskRegistrar.java | 65 +++++++++++++++++++ .../config/ScheduledTaskRegistrar.java | 12 +++- .../ScheduledTasksBeanDefinitionParser.java | 6 +- 3 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 spring-context/src/main/java/org/springframework/scheduling/config/ContextLifecycleScheduledTaskRegistrar.java diff --git a/spring-context/src/main/java/org/springframework/scheduling/config/ContextLifecycleScheduledTaskRegistrar.java b/spring-context/src/main/java/org/springframework/scheduling/config/ContextLifecycleScheduledTaskRegistrar.java new file mode 100644 index 00000000000..eacf7ed25c1 --- /dev/null +++ b/spring-context/src/main/java/org/springframework/scheduling/config/ContextLifecycleScheduledTaskRegistrar.java @@ -0,0 +1,65 @@ +/* + * Copyright 2002-2013 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.scheduling.config; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; + +/** + * {@link ScheduledTaskRegistrar} subclass that redirect the actual scheduling + * of tasks to the {@link ContextRefreshedEvent} callback. Falls back to regular + * {@link ScheduledTaskRegistrar} behavior when not running within an ApplicationContext. + * + * @author Juergen Hoeller + * @since 3.2.1 + */ +public class ContextLifecycleScheduledTaskRegistrar extends ScheduledTaskRegistrar + implements ApplicationContextAware, ApplicationListener { + + private ApplicationContext applicationContext; + + + public void setApplicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + + /** + * If we're running within an ApplicationContext, don't schedule the tasks + * right here; wait for this context's ContextRefreshedEvent instead. + */ + @Override + public void afterPropertiesSet() { + if (this.applicationContext == null) { + scheduleTasks(); + } + } + + /** + * Actually schedule the tasks at the right time of the context lifecycle, + * if we're running within an ApplicationContext. + */ + public void onApplicationEvent(ContextRefreshedEvent event) { + if (event.getApplicationContext() != this.applicationContext) { + return; + } + scheduleTasks(); + } + +} diff --git a/spring-context/src/main/java/org/springframework/scheduling/config/ScheduledTaskRegistrar.java b/spring-context/src/main/java/org/springframework/scheduling/config/ScheduledTaskRegistrar.java index 592fd033c13..2efd26cea9e 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/config/ScheduledTaskRegistrar.java +++ b/spring-context/src/main/java/org/springframework/scheduling/config/ScheduledTaskRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -274,11 +274,19 @@ public class ScheduledTaskRegistrar implements InitializingBean, DisposableBean (this.triggerTasks != null && !this.triggerTasks.isEmpty()); } + + /** + * Calls {@link #scheduleTasks()} at bean construction time. + */ + public void afterPropertiesSet() { + scheduleTasks(); + } + /** * Schedule all registered tasks against the underlying {@linkplain * #setTaskScheduler(TaskScheduler) task scheduler}. */ - public void afterPropertiesSet() { + protected void scheduleTasks() { long now = System.currentTimeMillis(); if (this.taskScheduler == null) { diff --git a/spring-context/src/main/java/org/springframework/scheduling/config/ScheduledTasksBeanDefinitionParser.java b/spring-context/src/main/java/org/springframework/scheduling/config/ScheduledTasksBeanDefinitionParser.java index a892eb414b1..8f6963614f4 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/config/ScheduledTasksBeanDefinitionParser.java +++ b/spring-context/src/main/java/org/springframework/scheduling/config/ScheduledTasksBeanDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -38,8 +38,10 @@ import org.w3c.dom.NodeList; public class ScheduledTasksBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { private static final String ELEMENT_SCHEDULED = "scheduled"; + private static final long ZERO_INITIAL_DELAY = 0; + @Override protected boolean shouldGenerateId() { return true; @@ -47,7 +49,7 @@ public class ScheduledTasksBeanDefinitionParser extends AbstractSingleBeanDefini @Override protected String getBeanClassName(Element element) { - return "org.springframework.scheduling.config.ScheduledTaskRegistrar"; + return "org.springframework.scheduling.config.ContextLifecycleScheduledTaskRegistrar"; } @Override From ece727bf57dac1b764cbdad94a10660c15a41c48 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 22 Jan 2013 23:28:38 +0100 Subject: [PATCH 110/143] Introduced NoUniqueBeanDefinitionException as a dedicated subclass of NoSuchBeanDefinitionException Issue: SPR-10194 --- ...nnotationConfigApplicationContextTests.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java b/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java index 23973400039..5906ea485dc 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,8 +17,10 @@ package org.springframework.context.annotation; import java.util.Map; +import java.util.regex.Pattern; import org.junit.Test; + import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; @@ -27,7 +29,7 @@ import org.springframework.context.annotation6.ComponentForScanning; import org.springframework.context.annotation6.ConfigForScanning; import org.springframework.context.annotation6.Jsr330NamedForScanning; -import static java.lang.String.*; +import static java.lang.String.format; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.springframework.util.StringUtils.*; @@ -120,13 +122,14 @@ public class AnnotationConfigApplicationContextTests { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class); // attempt to retrieve a bean that does not exist - Class targetType = java.util.regex.Pattern.class; + Class targetType = Pattern.class; try { Object bean = context.getBean(targetType); fail("should have thrown NoSuchBeanDefinitionException, instead got: " + bean); - } catch (NoSuchBeanDefinitionException ex) { + } + catch (NoSuchBeanDefinitionException ex) { assertThat(ex.getMessage(), containsString( - format("No unique bean of type [%s] is defined", targetType.getName()))); + format("No qualifying bean of type [%s] is defined", targetType.getName()))); } } @@ -137,10 +140,11 @@ public class AnnotationConfigApplicationContextTests { try { context.getBean(TestBean.class); - } catch (RuntimeException ex) { + } + catch (NoSuchBeanDefinitionException ex) { assertThat(ex.getMessage(), allOf( - containsString("No unique bean of type [" + TestBean.class.getName() + "] is defined"), + containsString("No qualifying bean of type [" + TestBean.class.getName() + "] is defined"), containsString("tb1"), containsString("tb2") ) From 3eec27a7230f65698d9afd04e27ce0db6ac57cab Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 22 Jan 2013 13:47:25 -0800 Subject: [PATCH 111/143] Filter build folder resources in eclipse Apply eclipse project 'filter' to hide generated build artifacts from the workspace. --- gradle/ide.gradle | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/gradle/ide.gradle b/gradle/ide.gradle index af43e6bc9d9..bb73f9f456f 100644 --- a/gradle/ide.gradle +++ b/gradle/ide.gradle @@ -78,3 +78,21 @@ task cleanEclipseJdtUi(type: Delete) { tasks["eclipseJdt"].dependsOn(eclipseJdtPrepare) tasks["cleanEclipse"].dependsOn(cleanEclipseJdtUi) tasks["eclipse"].dependsOn(eclipseSettings, eclipseWstComponent) + + +// Filter 'build' folder + +eclipse.project.file.withXml { + def node = it.asNode() + + def filteredResources = node.get("filteredResources") + if(filteredResources) { + node.remove(filteredResources) + } + def filterNode = node.appendNode("filteredResources").appendNode("filter") + filterNode.appendNode("name", "build") + filterNode.appendNode("type", "26") + def matcherNode = filterNode.appendNode("matcher") + matcherNode.appendNode("id", "org.eclipse.ui.ide.multiFilter") + matcherNode.appendNode("arguments", "1.0-projectRelativePath-matches-false-true-build\\/((?!eclipse).)*") +} From 05ba366edc4a8c3b426f54aa313466e7954f3b9b Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 22 Jan 2013 13:49:14 -0800 Subject: [PATCH 112/143] Polish AbstractContextLoader Javadoc Fix unnecessary HTML escaping from {@code} Javadoc. --- .../test/context/support/AbstractContextLoader.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java index 0928ff4c9ec..0fc6c6012c7 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java @@ -185,8 +185,8 @@ public abstract class AbstractContextLoader implements SmartContextLoader { * *

For example, if the supplied class is {@code com.example.MyTest}, * the generated locations will contain a single string with a value of - * "classpath:/com/example/MyTest{@code <suffix>}", - * where {@code <suffix>} is the value of the + * "classpath:/com/example/MyTest{@code }", + * where {@code } is the value of the * {@link #getResourceSuffix() resource suffix} string. * *

As of Spring 3.1, the implementation of this method adheres to the From 4d01d43c191564ade610437533e34b4e77ba385f Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 22 Jan 2013 14:11:32 -0800 Subject: [PATCH 113/143] Test String to char[] conversion Issue: SPR-9793 --- .../support/DefaultConversionTests.java | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/spring-core/src/test/java/org/springframework/core/convert/support/DefaultConversionTests.java b/spring-core/src/test/java/org/springframework/core/convert/support/DefaultConversionTests.java index 3dde4907053..7b2ff0490a1 100644 --- a/spring-core/src/test/java/org/springframework/core/convert/support/DefaultConversionTests.java +++ b/spring-core/src/test/java/org/springframework/core/convert/support/DefaultConversionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,14 @@ package org.springframework.core.convert.support; -import static org.junit.Assert.*; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import java.awt.Color; import java.math.BigDecimal; @@ -773,6 +780,30 @@ public class DefaultConversionTests { assertEquals(new Long(1), e.getId()); } + @Test + public void convertCharArrayToString() throws Exception { + String converted = conversionService.convert(new char[] { 'a', 'b', 'c' }, String.class); + assertThat(converted, equalTo("a,b,c")); + } + + @Test + public void convertStringToCharArray() throws Exception { + char[] converted = conversionService.convert("a,b,c", char[].class); + assertThat(converted, equalTo(new char[] { 'a', 'b', 'c' })); + } + + @Test + public void convertStringToCustomCharArray() throws Exception { + conversionService.addConverter(new Converter() { + @Override + public char[] convert(String source) { + return source.toCharArray(); + } + }); + char[] converted = conversionService.convert("abc", char[].class); + assertThat(converted, equalTo(new char[] { 'a', 'b', 'c' })); + } + public static class TestEntity { private Long id; From 3c09b07652ed3c7a2e4cee1295a450aee387f99b Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 22 Jan 2013 18:06:41 -0500 Subject: [PATCH 114/143] Raise exception on missing request parameters Issue: SPR-10193 --- .../RequestMappingInfoHandlerMapping.java | 25 +++++++++++++++++++ ...RequestMappingInfoHandlerMappingTests.java | 19 ++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java index e55ec1a3cad..cc242bf756a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java @@ -28,15 +28,19 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import org.springframework.http.MediaType; +import org.springframework.util.CollectionUtils; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.UnsatisfiedServletRequestParameterException; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping; +import org.springframework.web.servlet.mvc.condition.NameValueExpression; +import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition; import org.springframework.web.util.WebUtils; /** @@ -185,14 +189,17 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe Set consumableMediaTypes; Set producibleMediaTypes; + Set paramConditions; if (patternAndMethodMatches.isEmpty()) { consumableMediaTypes = getConsumableMediaTypes(request, patternMatches); producibleMediaTypes = getProdicubleMediaTypes(request, patternMatches); + paramConditions = getRequestParams(request, patternMatches); } else { consumableMediaTypes = getConsumableMediaTypes(request, patternAndMethodMatches); producibleMediaTypes = getProdicubleMediaTypes(request, patternAndMethodMatches); + paramConditions = getRequestParams(request, patternAndMethodMatches); } if (!consumableMediaTypes.isEmpty()) { @@ -205,6 +212,10 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe else if (!producibleMediaTypes.isEmpty()) { throw new HttpMediaTypeNotAcceptableException(new ArrayList(producibleMediaTypes)); } + else if (!CollectionUtils.isEmpty(paramConditions)) { + String[] params = paramConditions.toArray(new String[paramConditions.size()]); + throw new UnsatisfiedServletRequestParameterException(params, request.getParameterMap()); + } else { return null; } @@ -230,4 +241,18 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe return result; } + private Set getRequestParams(HttpServletRequest request, Set partialMatches) { + for (RequestMappingInfo partialMatch : partialMatches) { + ParamsRequestCondition condition = partialMatch.getParamsCondition(); + if (!CollectionUtils.isEmpty(condition.getExpressions()) && (condition.getMatchingCondition(request) == null)) { + Set expressions = new HashSet(); + for (NameValueExpression expr : condition.getExpressions()) { + expressions.add(expr.toString()); + } + return expressions; + } + } + return null; + } + } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java index 1d9168926b8..2db8412a827 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java @@ -42,6 +42,7 @@ import org.springframework.util.MultiValueMap; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.UnsatisfiedServletRequestParameterException; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -202,6 +203,19 @@ public class RequestMappingInfoHandlerMappingTests { } } + @Test + public void testUnsatisfiedServletRequestParameterException() throws Exception { + try { + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/params"); + this.handlerMapping.getHandler(request); + fail("UnsatisfiedServletRequestParameterException expected"); + } + catch (UnsatisfiedServletRequestParameterException ex) { + assertArrayEquals("Invalid request parameter conditions", + new String[] { "foo=bar" }, ex.getParamConditions()); + } + } + @Test public void uriTemplateVariables() { PatternsRequestCondition patterns = new PatternsRequestCondition("/{path1}/{path2}"); @@ -414,6 +428,11 @@ public class RequestMappingInfoHandlerMappingTests { return ""; } + @RequestMapping(value = "/params", params="foo=bar") + public String param() { + return ""; + } + @RequestMapping(value = "/content", produces="application/xml") public String xmlContent() { return ""; From 9c032d52d40b232c4e12ff94a5edb7d4851d7251 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 22 Jan 2013 15:32:09 -0800 Subject: [PATCH 115/143] Allow TypeDescriptor array construction Add a static factory method that can be used to create an array TypeDescriptor with a specific element type. Allows array types with generic elements to be constructed. Issue: SPR-9792 --- .../core/convert/TypeDescriptor.java | 20 +++++++++++++++- .../core/convert/TypeDescriptorTests.java | 23 ++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index f320400e6a8..59a7d09ace5 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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.core.convert; import java.lang.annotation.Annotation; +import java.lang.reflect.Array; import java.lang.reflect.Field; import java.util.Collection; import java.util.HashMap; @@ -33,6 +34,7 @@ import org.springframework.util.ObjectUtils; * @author Keith Donald * @author Andy Clement * @author Juergen Hoeller + * @author Phillip Webb * @since 3.0 */ public class TypeDescriptor { @@ -146,6 +148,22 @@ public class TypeDescriptor { return new TypeDescriptor(mapType, keyTypeDescriptor, valueTypeDescriptor); } + /** + * Create a new type descriptor as an array of the specified type. For example to + * create a {@code Map[]} use + * {@code TypeDescriptor.array(TypeDescriptor.map(Map.class, TypeDescriptor.value(String.class), TypeDescriptor.value(String.class)))}. + * @param elementTypeDescriptor the {@link TypeDescriptor} of the array element or {@code null} + * @return an array {@link TypeDescriptor} or {@code null} if {@code elementTypeDescriptor} is {@code null} + * @since 3.2 + */ + public static TypeDescriptor array(TypeDescriptor elementTypeDescriptor) { + if(elementTypeDescriptor == null) { + return null; + } + Class type = Array.newInstance(elementTypeDescriptor.getType(), 0).getClass(); + return new TypeDescriptor(type, elementTypeDescriptor, null, null, elementTypeDescriptor.getAnnotations()); + } + /** * Creates a type descriptor for a nested type declared within the method parameter. * For example, if the methodParameter is a List<String> and the nestingLevel is 1, the nested type descriptor will be String.class. diff --git a/spring-core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java b/spring-core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java index 90e2bf893b6..6597ab46a78 100644 --- a/spring-core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java +++ b/spring-core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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 java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -36,6 +37,7 @@ import static org.junit.Assert.*; /** * @author Keith Donald * @author Andy Clement + * @author Phillip Webb */ @SuppressWarnings("rawtypes") public class TypeDescriptorTests { @@ -849,4 +851,23 @@ public class TypeDescriptorTests { assertEquals(TypeDescriptor.forObject(new CustomMap()).getMapValueTypeDescriptor(), TypeDescriptor.valueOf(Integer.class)); } + @Test + public void createMapArray() throws Exception { + TypeDescriptor mapType = TypeDescriptor.map(LinkedHashMap.class, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)); + TypeDescriptor arrayType = TypeDescriptor.array(mapType); + assertEquals(arrayType.getType(), LinkedHashMap[].class); + assertEquals(arrayType.getElementTypeDescriptor(), mapType); + } + + + @Test + public void createStringArray() throws Exception { + TypeDescriptor arrayType = TypeDescriptor.array(TypeDescriptor.valueOf(String.class)); + assertEquals(arrayType, TypeDescriptor.valueOf(String[].class)); + } + + @Test + public void createNullArray() throws Exception { + assertNull(TypeDescriptor.array(null)); + } } From 89db04ec75225834dee988769a339ba2a5ee52b3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 23 Jan 2013 01:29:02 +0100 Subject: [PATCH 116/143] Added "awaitTerminationSeconds" property to ThreadPoolTaskExecutor/ThreadPoolTaskScheduler Issue: SPR-5387 --- .../ExecutorConfigurationSupport.java | 73 ++++++++++++++++++- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.java index 964872e1d6e..91764b33286 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.java +++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -54,6 +55,8 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac private boolean waitForTasksToCompleteOnShutdown = false; + private int awaitTerminationSeconds = 0; + private String beanName; private ExecutorService executor; @@ -85,9 +88,17 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac } /** - * Set whether to wait for scheduled tasks to complete on shutdown. - *

Default is "false". Switch this to "true" if you prefer - * fully completed tasks at the expense of a longer shutdown phase. + * Set whether to wait for scheduled tasks to complete on shutdown, + * not interrupting running tasks and executing all tasks in the queue. + *

Default is "false", shutting down immediately through interrupting + * ongoing tasks and clearing the queue. Switch this flag to "true" if you + * prefer fully completed tasks at the expense of a longer shutdown phase. + *

Note that Spring's container shutdown continues while ongoing tasks + * are being completed. If you want this executor to block and wait for the + * termination of tasks before the rest of the container continues to shut + * down - e.g. in order to keep up other resources that your tasks may need -, + * set the {@link #setAwaitTerminationSeconds "awaitTerminationSeconds"} + * property instead of or in addition to this property. * @see java.util.concurrent.ExecutorService#shutdown() * @see java.util.concurrent.ExecutorService#shutdownNow() */ @@ -95,6 +106,33 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac this.waitForTasksToCompleteOnShutdown = waitForJobsToCompleteOnShutdown; } + /** + * Set the maximum number of seconds that this executor is supposed to block + * on shutdown in order to wait for remaining tasks to complete their execution + * before the rest of the container continues to shut down. This is particularly + * useful if your remaining tasks are likely to need access to other resources + * that are also managed by the container. + *

By default, this executor won't wait for the termination of tasks at all. + * It will either shut down immediately, interrupting ongoing tasks and clearing + * the remaining task queue - or, if the + * {@link #setWaitForTasksToCompleteOnShutdown "waitForTasksToCompleteOnShutdown"} + * flag has been set to {@code true}, it will continue to fully execute all + * ongoing tasks as well as all remaining tasks in the queue, in parallel to + * the rest of the container shutting down. + *

In either case, if you specify an await-termination period using this property, + * this executor will wait for the given time (max) for the termination of tasks. + * As a rule of thumb, specify a significantly higher timeout here if you set + * "waitForTasksToCompleteOnShutdown" to {@code true} at the same time, + * since all remaining tasks in the queue will still get executed - in contrast + * to the default shutdown behavior where it's just about waiting for currently + * executing tasks that aren't reacting to thread interruption. + * @see java.util.concurrent.ExecutorService#shutdown() + * @see java.util.concurrent.ExecutorService#awaitTermination + */ + public void setAwaitTerminationSeconds(int awaitTerminationSeconds) { + this.awaitTerminationSeconds = awaitTerminationSeconds; + } + public void setBeanName(String name) { this.beanName = name; } @@ -145,6 +183,8 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac /** * Perform a shutdown on the ThreadPoolExecutor. * @see java.util.concurrent.ExecutorService#shutdown() + * @see java.util.concurrent.ExecutorService#shutdownNow() + * @see #awaitTerminationIfNecessary() */ public void shutdown() { if (logger.isInfoEnabled()) { @@ -156,6 +196,31 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac else { this.executor.shutdownNow(); } + awaitTerminationIfNecessary(); + } + + /** + * Wait for the executor to terminate, according to the value of the + * {@link #setAwaitTerminationSeconds "awaitTerminationSeconds"} property. + */ + private void awaitTerminationIfNecessary() { + if (this.awaitTerminationSeconds > 0) { + try { + if (!this.executor.awaitTermination(this.awaitTerminationSeconds, TimeUnit.SECONDS)) { + if (logger.isWarnEnabled()) { + logger.warn("Timed out while waiting for executor" + + (this.beanName != null ? " '" + this.beanName + "'" : "") + " to terminate"); + } + } + } + catch (InterruptedException ex) { + if (logger.isWarnEnabled()) { + logger.warn("Interrupted while waiting for executor" + + (this.beanName != null ? " '" + this.beanName + "'" : "") + " to terminate"); + } + Thread.currentThread().interrupt(); + } + } } } From d5af9dc0a56214aa006c37190ba0110a7b4a5d89 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 23 Jan 2013 01:29:56 +0100 Subject: [PATCH 117/143] Polishing --- .../scheduling/support/PeriodicTrigger.java | 18 ++++++++---------- .../ThreadPoolTaskSchedulerTests.java | 16 +++++++++------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/support/PeriodicTrigger.java b/spring-context/src/main/java/org/springframework/scheduling/support/PeriodicTrigger.java index b494e4c5ff0..e25fdd3f254 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/support/PeriodicTrigger.java +++ b/spring-context/src/main/java/org/springframework/scheduling/support/PeriodicTrigger.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,8 +31,8 @@ import org.springframework.util.Assert; * completion time). To measure the interval between the * scheduled start time of each execution instead, set the * 'fixedRate' property to {@code true}. - *

- * Note that the TaskScheduler interface already defines methods for scheduling + * + *

Note that the TaskScheduler interface already defines methods for scheduling * tasks at fixed-rate or with fixed-delay. Both also support an optional value * for the initial delay. Those methods should be used directly whenever * possible. The value of this Trigger implementation is that it can be used @@ -68,7 +68,7 @@ public class PeriodicTrigger implements Trigger { */ public PeriodicTrigger(long period, TimeUnit timeUnit) { Assert.isTrue(period >= 0, "period must not be negative"); - this.timeUnit = (timeUnit != null) ? timeUnit : TimeUnit.MILLISECONDS; + this.timeUnit = (timeUnit != null ? timeUnit : TimeUnit.MILLISECONDS); this.period = this.timeUnit.toMillis(period); } @@ -91,6 +91,7 @@ public class PeriodicTrigger implements Trigger { this.fixedRate = fixedRate; } + /** * Returns the time after which a task should run again. */ @@ -104,6 +105,7 @@ public class PeriodicTrigger implements Trigger { return new Date(triggerContext.lastCompletionTime().getTime() + this.period); } + @Override public boolean equals(Object obj) { if (this == obj) { @@ -113,16 +115,12 @@ public class PeriodicTrigger implements Trigger { return false; } PeriodicTrigger other = (PeriodicTrigger) obj; - return this.fixedRate == other.fixedRate - && this.initialDelay == other.initialDelay - && this.period == other.period; + return (this.fixedRate == other.fixedRate && this.initialDelay == other.initialDelay && this.period == other.period); } @Override public int hashCode() { - return (this.fixedRate ? 17 : 29) + - (int) (37 * this.period) + - (int) (41 * this.initialDelay); + return (this.fixedRate ? 17 : 29) + (int) (37 * this.period) + (int) (41 * this.initialDelay); } } diff --git a/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolTaskSchedulerTests.java b/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolTaskSchedulerTests.java index 7320cea1d19..ddaa2b0fcea 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolTaskSchedulerTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/concurrent/ThreadPoolTaskSchedulerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,11 +16,6 @@ package org.springframework.scheduling.concurrent; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - import java.util.Date; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; @@ -29,6 +24,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -36,6 +32,8 @@ import org.springframework.scheduling.Trigger; import org.springframework.scheduling.TriggerContext; import org.springframework.util.ErrorHandler; +import static org.junit.Assert.*; + /** * @author Mark Fisher * @since 3.0 @@ -44,7 +42,6 @@ public class ThreadPoolTaskSchedulerTests { private static final String THREAD_NAME_PREFIX = "test-"; - private final ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); @@ -54,6 +51,11 @@ public class ThreadPoolTaskSchedulerTests { scheduler.afterPropertiesSet(); } + @After + public void shutdownScheduler() { + scheduler.destroy(); + } + // test methods From 5fb75304eb6571bcf838e6af4814ac09449e9e1c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 23 Jan 2013 01:31:28 +0100 Subject: [PATCH 118/143] Final preparations for 3.2.1 --- src/dist/changelog.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dist/changelog.txt b/src/dist/changelog.txt index 771470cc1d3..af25c8c883e 100644 --- a/src/dist/changelog.txt +++ b/src/dist/changelog.txt @@ -23,6 +23,8 @@ Changes in version 3.2.1 (2013-01-24) * MBeanExporter does not log warnings for manually unregistered MBeans (SPR-9451) * AbstractCacheManager accepts no caches defined, allowing for EHCache default cache setup (SPR-7955) * EhCacheManagerFactoryBean applies cacheManagerName ahead of creation (for EHCache 2.5 compatibility; SPR-9171) +* added "awaitTerminationSeconds" property to ThreadPoolTaskExecutor/ThreadPoolTaskScheduler (SPR-5387) +* aligned XML scheduled-task elements with @Scheduled in terms of kicking in after context refresh (SPR-9231) * reintroduced "mode" and "proxy-target-class" attributes in spring-task-3.1/3.2.xsd (SPR-10177) * spring-task-3.2.xsd allows for SpEL expressions in initial-delay attribute (SPR-10102) * spring-jms-3.2.xsd allows for SpEL expressions in prefetch and receive-timeout attributes (SPR-9553) From 7e74fd2b7fb30e6206d40bb7235d82433682386a Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 22 Jan 2013 17:53:30 -0800 Subject: [PATCH 119/143] Consider primary attribute with getBean(Class) Update DefaultListableBeanFactory.getBean(Class beanClass) to consider the 'primary' attribute of bean definitions. This makes getBean() behave in the same way as autowiring. Issue: SPR-7854 --- .../support/DefaultListableBeanFactory.java | 15 +++++++++ .../DefaultListableBeanFactoryTests.java | 33 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 432663ab793..23512f9e232 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -90,6 +90,7 @@ import org.springframework.util.StringUtils; * @author Sam Brannen * @author Costin Leau * @author Chris Beams + * @author Phillip Webb * @since 16 April 2001 * @see StaticListableBeanFactory * @see PropertiesBeanDefinitionReader @@ -272,6 +273,20 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto return getBean(beanNames[0], requiredType); } else if (beanNames.length > 1) { + T primaryBean = null; + for (String beanName : beanNames) { + T beanInstance = getBean(beanName, requiredType); + if (isPrimary(beanName, beanInstance)) { + if(primaryBean != null) { + throw new NoUniqueBeanDefinitionException(requiredType, beanNames.length, + "more than one 'primary' bean found of required type: " + Arrays.asList(beanNames)); + } + primaryBean = beanInstance; + } + } + if(primaryBean != null) { + return primaryBean; + } throw new NoUniqueBeanDefinitionException(requiredType, beanNames); } else if (getParentBeanFactory() != null) { diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index e93e95973d6..fd134fcc3dd 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -17,6 +17,8 @@ package org.springframework.beans.factory; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -49,7 +51,9 @@ import javax.security.auth.Subject; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.NotWritablePropertyException; @@ -102,11 +106,14 @@ import org.springframework.util.StopWatch; * @author Rick Evans * @author Sam Brannen * @author Chris Beams + * @author Phillip Webb */ public class DefaultListableBeanFactoryTests { private static final Log factoryLog = LogFactory.getLog(DefaultListableBeanFactory.class); + @Rule + public ExpectedException thrown = ExpectedException.none(); @Test public void testUnreferencedSingletonWasInstantiated() { @@ -1285,6 +1292,32 @@ public class DefaultListableBeanFactoryTests { lbf.getBean(TestBean.class); } + @Test + public void testGetBeanByTypeWithPrimary() throws Exception { + DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); + RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class); + RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class); + bd2.setPrimary(true); + lbf.registerBeanDefinition("bd1", bd1); + lbf.registerBeanDefinition("bd2", bd2); + TestBean bean = lbf.getBean(TestBean.class); + assertThat(bean.getBeanName(), equalTo("bd2")); + } + + @Test + public void testGetBeanByTypeWithMultiplePrimary() throws Exception { + DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); + RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class); + bd1.setPrimary(true); + RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class); + bd2.setPrimary(true); + lbf.registerBeanDefinition("bd1", bd1); + lbf.registerBeanDefinition("bd2", bd2); + thrown.expect(NoUniqueBeanDefinitionException.class); + thrown.expectMessage(containsString("more than one 'primary'")); + lbf.getBean(TestBean.class); + } + @Test public void testGetBeanByTypeFiltersOutNonAutowireCandidates() { DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); From a1aba23aa96595d1795db1ddc3ce7ef35fb4ec19 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 23 Jan 2013 16:47:23 +0100 Subject: [PATCH 120/143] Polish Javadoc in TypeDescriptor - fix @since version in array() - format method-level Javadoc - unescape HTML-escaped angle brackets with {@code} blocks --- .../core/convert/TypeDescriptor.java | 148 +++++++++++------- 1 file changed, 90 insertions(+), 58 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index 59a7d09ace5..79c134e7cf0 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -35,6 +35,7 @@ import org.springframework.util.ObjectUtils; * @author Andy Clement * @author Juergen Hoeller * @author Phillip Webb + * @author Sam Brannen * @since 3.0 */ public class TypeDescriptor { @@ -77,7 +78,8 @@ public class TypeDescriptor { /** * Create a new type descriptor from a {@link MethodParameter}. - * Use this constructor when a source or target conversion point is a constructor parameter, method parameter, or method return value. + *

Use this constructor when a source or target conversion point is a + * constructor parameter, method parameter, or method return value. * @param methodParameter the method parameter */ public TypeDescriptor(MethodParameter methodParameter) { @@ -86,7 +88,7 @@ public class TypeDescriptor { /** * Create a new type descriptor from a {@link Field}. - * Use this constructor when source or target conversion point is a field. + *

Use this constructor when a source or target conversion point is a field. * @param field the field */ public TypeDescriptor(Field field) { @@ -95,7 +97,8 @@ public class TypeDescriptor { /** * Create a new type descriptor from a {@link Property}. - * Use this constructor when a source or target conversion point is a property on a Java class. + *

Use this constructor when a source or target conversion point is a + * property on a Java class. * @param property the property */ public TypeDescriptor(Property property) { @@ -105,8 +108,11 @@ public class TypeDescriptor { /** * Create a new type descriptor from the given type. - * Use this to instruct the conversion system to convert an object to a specific target type, when no type location such as a method parameter or field is available to provide additional conversion context. - * Generally prefer use of {@link #forObject(Object)} for constructing type descriptors from source objects, as it handles the null object case. + *

Use this to instruct the conversion system to convert an object to a + * specific target type, when no type location such as a method parameter or + * field is available to provide additional conversion context. + *

Generally prefer use of {@link #forObject(Object)} for constructing type + * descriptors from source objects, as it handles the {@code null} object case. * @param type the class * @return the type descriptor */ @@ -116,12 +122,15 @@ public class TypeDescriptor { } /** - * Create a new type descriptor from a java.util.Collection type. - * Useful for converting to typed Collections. - * For example, a List<String> could be converted to a List<EmailAddress> by converting to a targetType built with this method. - * The method call to construct such a TypeDescriptor would look something like: collection(List.class, TypeDescriptor.valueOf(EmailAddress.class)); + * Create a new type descriptor from a {@link java.util.Collection} type. + *

Useful for converting to typed Collections. + *

For example, a {@code List} could be converted to a + * {@code List} by converting to a targetType built with this method. + * The method call to construct such a {@code TypeDescriptor} would look something + * like: {@code collection(List.class, TypeDescriptor.valueOf(EmailAddress.class));} * @param collectionType the collection type, which must implement {@link Collection}. - * @param elementTypeDescriptor a descriptor for the collection's element type, used to convert collection elements + * @param elementTypeDescriptor a descriptor for the collection's element type, + * used to convert collection elements * @return the collection type descriptor */ public static TypeDescriptor collection(Class collectionType, TypeDescriptor elementTypeDescriptor) { @@ -132,9 +141,9 @@ public class TypeDescriptor { } /** - * Create a new type descriptor from a java.util.Map type. - * Useful for Converting to typed Maps. - * For example, a Map<String, String> could be converted to a Map<Id, EmailAddress> by converting to a targetType built with this method: + * Create a new type descriptor from a {@link java.util.Map} type. + *

Useful for converting to typed Maps. + *

For example, a Map<String, String> could be converted to a Map<Id, EmailAddress> by converting to a targetType built with this method: * The method call to construct such a TypeDescriptor would look something like: map(Map.class, TypeDescriptor.valueOf(Id.class), TypeDescriptor.valueOf(EmailAddress.class)); * @param mapType the map type, which must implement {@link Map} * @param keyTypeDescriptor a descriptor for the map's key type, used to convert map keys @@ -149,12 +158,12 @@ public class TypeDescriptor { } /** - * Create a new type descriptor as an array of the specified type. For example to - * create a {@code Map[]} use + * Create a new type descriptor as an array of the specified type. + *

For example to create a {@code Map[]} use * {@code TypeDescriptor.array(TypeDescriptor.map(Map.class, TypeDescriptor.value(String.class), TypeDescriptor.value(String.class)))}. * @param elementTypeDescriptor the {@link TypeDescriptor} of the array element or {@code null} * @return an array {@link TypeDescriptor} or {@code null} if {@code elementTypeDescriptor} is {@code null} - * @since 3.2 + * @since 3.2.1 */ public static TypeDescriptor array(TypeDescriptor elementTypeDescriptor) { if(elementTypeDescriptor == null) { @@ -166,17 +175,26 @@ public class TypeDescriptor { /** * Creates a type descriptor for a nested type declared within the method parameter. - * For example, if the methodParameter is a List<String> and the nestingLevel is 1, the nested type descriptor will be String.class. - * If the methodParameter is a List> and the nestingLevel is 2, the nested type descriptor will also be a String.class. - * If the methodParameter is a Map and the nesting level is 1, the nested type descriptor will be String, derived from the map value. - * If the methodParameter is a List> and the nesting level is 2, the nested type descriptor will be String, derived from the map value. - * Returns null if a nested type cannot be obtained because it was not declared. - * For example, if the method parameter is a List<?>, the nested type descriptor returned will be null. + *

For example, if the methodParameter is a {@code List} and the + * nesting level is 1, the nested type descriptor will be String.class. + *

If the methodParameter is a {@code List>} and the nesting + * level is 2, the nested type descriptor will also be a String.class. + *

If the methodParameter is a {@code Map} and the nesting + * level is 1, the nested type descriptor will be String, derived from the map value. + *

If the methodParameter is a {@code List>} and the + * nesting level is 2, the nested type descriptor will be String, derived from the map value. + *

Returns {@code null} if a nested type cannot be obtained because it was not declared. + * For example, if the method parameter is a {@code List}, the nested type + * descriptor returned will be {@code null}. * @param methodParameter the method parameter with a nestingLevel of 1 - * @param nestingLevel the nesting level of the collection/array element or map key/value declaration within the method parameter - * @return the nested type descriptor at the specified nesting level, or null if it could not be obtained - * @throws IllegalArgumentException if the nesting level of the input {@link MethodParameter} argument is not 1 - * @throws IllegalArgumentException if the types up to the specified nesting level are not of collection, array, or map types + * @param nestingLevel the nesting level of the collection/array element or + * map key/value declaration within the method parameter + * @return the nested type descriptor at the specified nesting level, or null + * if it could not be obtained + * @throws IllegalArgumentException if the nesting level of the input + * {@link MethodParameter} argument is not 1 + * @throws IllegalArgumentException if the types up to the specified nesting + * level are not of collection, array, or map types */ public static TypeDescriptor nested(MethodParameter methodParameter, int nestingLevel) { if (methodParameter.getNestingLevel() != 1) { @@ -187,16 +205,23 @@ public class TypeDescriptor { /** * Creates a type descriptor for a nested type declared within the field. - *

For example, if the field is a {@code List<String>} and the nestingLevel is 1, the nested type descriptor will be {@code String.class}. - * If the field is a {@code List<List<String>>} and the nestingLevel is 2, the nested type descriptor will also be a {@code String.class}. - * If the field is a {@code Map<Integer, String>} and the nestingLevel is 1, the nested type descriptor will be String, derived from the map value. - * If the field is a {@code List<Map<Integer, String>>} and the nestingLevel is 2, the nested type descriptor will be String, derived from the map value. - * Returns {@code null} if a nested type cannot be obtained because it was not declared. - * For example, if the field is a {@code List<?>}, the nested type descriptor returned will be {@code null}. + *

For example, if the field is a {@code List} and the nesting + * level is 1, the nested type descriptor will be {@code String.class}. + *

If the field is a {@code List>} and the nesting level is + * 2, the nested type descriptor will also be a {@code String.class}. + *

If the field is a {@code Map} and the nesting level + * is 1, the nested type descriptor will be String, derived from the map value. + *

If the field is a {@code List>} and the nesting + * level is 2, the nested type descriptor will be String, derived from the map value. + *

Returns {@code null} if a nested type cannot be obtained because it was not declared. + * For example, if the field is a {@code List}, the nested type descriptor returned will be {@code null}. * @param field the field - * @param nestingLevel the nesting level of the collection/array element or map key/value declaration within the field - * @return the nested type descriptor at the specified nestingLevel, or null if it could not be obtained - * @throws IllegalArgumentException if the types up to the specified nesting level are not of collection, array, or map types + * @param nestingLevel the nesting level of the collection/array element or + * map key/value declaration within the field + * @return the nested type descriptor at the specified nesting level, or null + * if it could not be obtained + * @throws IllegalArgumentException if the types up to the specified nesting + * level are not of collection, array, or map types */ public static TypeDescriptor nested(Field field, int nestingLevel) { return nested(new FieldDescriptor(field), nestingLevel); @@ -204,16 +229,24 @@ public class TypeDescriptor { /** * Creates a type descriptor for a nested type declared within the property. - *

For example, if the property is a {@code List<String>} and the nestingLevel is 1, the nested type descriptor will be {@code String.class}. - * If the property is a {@code List<List<String>>} and the nestingLevel is 2, the nested type descriptor will also be a {@code String.class}. - * If the property is a {@code Map<Integer, String>} and the nestingLevel is 1, the nested type descriptor will be String, derived from the map value. - * If the property is a {@code List<Map<Integer, String>>} and the nestingLevel is 2, the nested type descriptor will be String, derived from the map value. - * Returns {@code null} if a nested type cannot be obtained because it was not declared. - * For example, if the property is a {@code List<?>}, the nested type descriptor returned will be {@code null}. + *

For example, if the property is a {@code List} and the nesting + * level is 1, the nested type descriptor will be {@code String.class}. + *

If the property is a {@code List>} and the nesting level + * is 2, the nested type descriptor will also be a {@code String.class}. + *

If the property is a {@code Map} and the nesting level + * is 1, the nested type descriptor will be String, derived from the map value. + *

If the property is a {@code List>} and the nesting + * level is 2, the nested type descriptor will be String, derived from the map value. + *

Returns {@code null} if a nested type cannot be obtained because it was not declared. + * For example, if the property is a {@code List}, the nested type descriptor + * returned will be {@code null}. * @param property the property - * @param nestingLevel the nesting level of the collection/array element or map key/value declaration within the property - * @return the nested type descriptor at the specified nestingLevel, or {@code null} if it could not be obtained - * @throws IllegalArgumentException if the types up to the specified nesting level are not of collection, array, or map types + * @param nestingLevel the nesting level of the collection/array element or + * map key/value declaration within the property + * @return the nested type descriptor at the specified nesting level, or + * {@code null} if it could not be obtained + * @throws IllegalArgumentException if the types up to the specified nesting + * level are not of collection, array, or map types */ public static TypeDescriptor nested(Property property, int nestingLevel) { return nested(new BeanPropertyDescriptor(property), nestingLevel); @@ -221,8 +254,8 @@ public class TypeDescriptor { /** * Create a new type descriptor for an object. - * Use this factory method to introspect a source object before asking the conversion system to convert it to some another type. - * If the provided object is null, returns null, else calls {@link #valueOf(Class)} to build a TypeDescriptor from the object's class. + *

Use this factory method to introspect a source object before asking the conversion system to convert it to some another type. + *

If the provided object is null, returns null, else calls {@link #valueOf(Class)} to build a TypeDescriptor from the object's class. * @param source the source object * @return the type descriptor */ @@ -232,7 +265,7 @@ public class TypeDescriptor { /** * The type of the backing class, method parameter, field, or property described by this TypeDescriptor. - * Returns primitive types as-is. + *

Returns primitive types as-is. *

See {@link #getObjectType()} for a variation of this operation that resolves primitive types * to their corresponding Object types if necessary. * @return the type, or {@code null} @@ -253,7 +286,7 @@ public class TypeDescriptor { /** * Narrows this {@link TypeDescriptor} by setting its type to the class of the provided value. - * If the value is {@code null}, no narrowing is performed and this TypeDescriptor is returned unchanged. + *

If the value is {@code null}, no narrowing is performed and this TypeDescriptor is returned unchanged. *

Designed to be called by binding frameworks when they read property, field, or method return values. * Allows such frameworks to narrow a TypeDescriptor built from a declared property, field, or method return value type. * For example, a field declared as {@code java.lang.Object} would be narrowed to {@code java.util.HashMap} @@ -273,7 +306,6 @@ public class TypeDescriptor { /** * Cast this {@link TypeDescriptor} to a superclass or implemented interface * preserving annotations and nested type context. - * * @param superType the super type to cast to (can be {@code null} * @return a new TypeDescriptor for the up-cast type * @throws IllegalArgumentException if this type is not assignable to the super-type @@ -342,7 +374,7 @@ public class TypeDescriptor { /** * Returns true if an object of this type descriptor can be assigned to the location described by the given type descriptor. - * For example, valueOf(String.class).isAssignableTo(valueOf(CharSequence.class)) returns true because a String value can be assigned to a CharSequence variable. + *

For example, valueOf(String.class).isAssignableTo(valueOf(CharSequence.class)) returns true because a String value can be assigned to a CharSequence variable. * On the other hand, valueOf(Number.class).isAssignableTo(valueOf(Integer.class)) returns false because, while all Integers are Numbers, not all Numbers are Integers. *

* For arrays, collections, and maps, element and key/value types are checked if declared. @@ -400,10 +432,10 @@ public class TypeDescriptor { /** * If this type is a {@link Collection} or an Array, creates a element TypeDescriptor from the provided collection or array element. - * Narrows the {@link #getElementTypeDescriptor() elementType} property to the class of the provided collection or array element. + *

Narrows the {@link #getElementTypeDescriptor() elementType} property to the class of the provided collection or array element. * For example, if this describes a java.util.List<java.lang.Number< and the element argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer. * If this describes a java.util.List<?> and the element argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer as well. - * Annotation and nested type context will be preserved in the narrowed TypeDescriptor that is returned. + *

Annotation and nested type context will be preserved in the narrowed TypeDescriptor that is returned. * @param element the collection or array element * @return a element type descriptor, narrowed to the type of the provided element * @throws IllegalStateException if this type is not a java.util.Collection or Array type @@ -435,10 +467,10 @@ public class TypeDescriptor { /** * If this type is a {@link Map}, creates a mapKey {@link TypeDescriptor} from the provided map key. - * Narrows the {@link #getMapKeyTypeDescriptor() mapKeyType} property to the class of the provided map key. + *

Narrows the {@link #getMapKeyTypeDescriptor() mapKeyType} property to the class of the provided map key. * For example, if this describes a java.util.Map<java.lang.Number, java.lang.String< and the key argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer. - * If this describes a java.util.Map<?, ?> and the key argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer as well. - * Annotation and nested type context will be preserved in the narrowed TypeDescriptor that is returned. + *

If this describes a java.util.Map<?, ?> and the key argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer as well. + *

Annotation and nested type context will be preserved in the narrowed TypeDescriptor that is returned. * @param mapKey the map key * @return the map key type descriptor * @throws IllegalStateException if this type is not a java.util.Map @@ -450,7 +482,7 @@ public class TypeDescriptor { /** * If this type is a {@link Map} and its value type is parameterized, returns the map's value type. - * If the Map's value type is not parameterized, returns null indicating the value type is not declared. + *

If the Map's value type is not parameterized, returns null indicating the value type is not declared. * @return the Map value type, or {@code null} if this type is a Map but its value type is not parameterized * @throws IllegalStateException if this type is not a java.util.Map */ @@ -461,10 +493,10 @@ public class TypeDescriptor { /** * If this type is a {@link Map}, creates a mapValue {@link TypeDescriptor} from the provided map value. - * Narrows the {@link #getMapValueTypeDescriptor() mapValueType} property to the class of the provided map value. + *

Narrows the {@link #getMapValueTypeDescriptor() mapValueType} property to the class of the provided map value. * For example, if this describes a java.util.Map<java.lang.String, java.lang.Number< and the value argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer. * If this describes a java.util.Map<?, ?> and the value argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer as well. - * Annotation and nested type context will be preserved in the narrowed TypeDescriptor that is returned. + *

Annotation and nested type context will be preserved in the narrowed TypeDescriptor that is returned. * @param mapValue the map value * @return the map value type descriptor * @throws IllegalStateException if this type is not a java.util.Map From 3b8aba9ccd0ca0a437a9425471644b1d01a99795 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 23 Jan 2013 16:48:08 +0100 Subject: [PATCH 121/143] Fix typo in ContextLifecycleScheduledTaskRegistrar --- .../config/ContextLifecycleScheduledTaskRegistrar.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/config/ContextLifecycleScheduledTaskRegistrar.java b/spring-context/src/main/java/org/springframework/scheduling/config/ContextLifecycleScheduledTaskRegistrar.java index eacf7ed25c1..8a0fba8c3ba 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/config/ContextLifecycleScheduledTaskRegistrar.java +++ b/spring-context/src/main/java/org/springframework/scheduling/config/ContextLifecycleScheduledTaskRegistrar.java @@ -22,9 +22,9 @@ import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; /** - * {@link ScheduledTaskRegistrar} subclass that redirect the actual scheduling + * {@link ScheduledTaskRegistrar} subclass that redirects the actual scheduling * of tasks to the {@link ContextRefreshedEvent} callback. Falls back to regular - * {@link ScheduledTaskRegistrar} behavior when not running within an ApplicationContext. + * {@code ScheduledTaskRegistrar} behavior when not running within an ApplicationContext. * * @author Juergen Hoeller * @since 3.2.1 From e4f1f682e2a7eb8e7589f1281d96132453d01523 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 23 Jan 2013 17:09:45 +0100 Subject: [PATCH 122/143] Update distribution README regarding paths - Corrected path to 'docs/spring-framework-reference' - Minor formatting changes --- src/dist/readme.txt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/dist/readme.txt b/src/dist/readme.txt index e55fefae193..c58486a587d 100644 --- a/src/dist/readme.txt +++ b/src/dist/readme.txt @@ -1,12 +1,11 @@ Spring Framework version ${version} -=============================================================================== +================================================================================ -To find out what has changed since any earlier releases, see -'docs/changelog.txt'. +To find out what has changed since any earlier releases, see 'docs/changelog.txt'. -Please consult the documentation located within the 'docs/reference' directory -of this release and also visit the official Spring Framework home at -http://www.springsource.org/spring-framework +Please consult the documentation located within the +'docs/spring-framework-reference' directory of this release and also visit +the official Spring Framework home at http://www.springsource.org/spring-framework There you will find links to the forum, issue tracker, and other resources. From 9982b4c01a8c7be0961e58b58ed83731c40449ff Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 23 Jan 2013 13:35:14 -0500 Subject: [PATCH 123/143] Add BS and VT char escape sequences to JavaScriptUtils Issue: SPR-9983 --- .../web/util/JavaScriptUtils.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/util/JavaScriptUtils.java b/spring-web/src/main/java/org/springframework/web/util/JavaScriptUtils.java index b28d398687f..0ee697f6d9a 100644 --- a/spring-web/src/main/java/org/springframework/web/util/JavaScriptUtils.java +++ b/spring-web/src/main/java/org/springframework/web/util/JavaScriptUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2013 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,21 +21,21 @@ package org.springframework.web.util; * Escapes based on the JavaScript 1.5 recommendation. * *

Reference: - * - * Core JavaScript 1.5 Guide - * + * + * JavaScript Guide on Mozilla Developer Network. * * @author Juergen Hoeller * @author Rob Harrop + * @author Rossen Stoyanchev * @since 1.1.1 */ public class JavaScriptUtils { /** - * Turn special characters into escaped characters conforming to JavaScript. - * Handles complete character set defined in HTML 4.01 recommendation. + * Turn JavaScript special characters into escaped characters. + * * @param input the input string - * @return the escaped string + * @return the string with escaped characters */ public static String javaScriptEscape(String input) { if (input == null) { @@ -73,6 +73,13 @@ public class JavaScriptUtils { else if (c == '\f') { filtered.append("\\f"); } + else if (c == '\b') { + filtered.append("\\b"); + } + // No '\v' in Java, use octal value for VT ascii char + else if (c == '\013') { + filtered.append("\\v"); + } else { filtered.append(c); } From f3ff98d86275e19e6828f9c3a0c432de0587bbbe Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 23 Jan 2013 11:02:15 -0800 Subject: [PATCH 124/143] Allow nulls with multiple embedded value resolvers Allow an embedded value resolver added to an AbstractBeanFactory to return null without adversely effecting any subsequent resolvers. Issue: SPR-8565 --- .../factory/support/AbstractBeanFactory.java | 2 +- .../DefaultListableBeanFactoryTests.java | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index 8db728ae579..f298c6522ff 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -746,7 +746,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp public String resolveEmbeddedValue(String value) { String result = value; for (StringValueResolver resolver : this.embeddedValueResolvers) { - result = resolver.resolveStringValue(result); + result = (result == null ? null : resolver.resolveStringValue(result)); } return result; } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index fd134fcc3dd..fc4ac5d55c6 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -28,6 +28,11 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import java.io.Closeable; import java.lang.reflect.Field; @@ -97,6 +102,7 @@ import org.springframework.tests.sample.beans.SideEffectBean; import org.springframework.tests.sample.beans.TestBean; import org.springframework.tests.sample.beans.factory.DummyFactory; import org.springframework.util.StopWatch; +import org.springframework.util.StringValueResolver; /** * Tests properties population and autowire behavior. @@ -2261,6 +2267,26 @@ public class DefaultListableBeanFactoryTests { assertThat(bf.containsBean("bogus"), is(false)); } + @Test + public void resolveEmbeddedValue() throws Exception { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + StringValueResolver r1 = mock(StringValueResolver.class); + StringValueResolver r2 = mock(StringValueResolver.class); + StringValueResolver r3 = mock(StringValueResolver.class); + bf.addEmbeddedValueResolver(r1); + bf.addEmbeddedValueResolver(r2); + bf.addEmbeddedValueResolver(r3); + given(r1.resolveStringValue("A")).willReturn("B"); + given(r2.resolveStringValue("B")).willReturn(null); + given(r3.resolveStringValue(isNull(String.class))).willThrow(new IllegalArgumentException()); + + bf.resolveEmbeddedValue("A"); + + verify(r1).resolveStringValue("A"); + verify(r2).resolveStringValue("B"); + verify(r3, never()).resolveStringValue(isNull(String.class)); + } + static class A { } static class B { } From 2ee70d683da7971ed2fca41ffbc440d61dd61d00 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 23 Jan 2013 14:59:15 -0500 Subject: [PATCH 125/143] Suppress serialization warning --- .../org/springframework/web/util/HierarchicalUriComponents.java | 1 + .../java/org/springframework/web/util/OpaqueUriComponents.java | 1 + 2 files changed, 2 insertions(+) diff --git a/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java b/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java index f3a5ec9013b..103784e9c29 100644 --- a/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java +++ b/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java @@ -41,6 +41,7 @@ import org.springframework.util.StringUtils; * @since 3.1.3 * @see Hierarchical URIs */ +@SuppressWarnings("serial") final class HierarchicalUriComponents extends UriComponents { private static final char PATH_DELIMITER = '/'; diff --git a/spring-web/src/main/java/org/springframework/web/util/OpaqueUriComponents.java b/spring-web/src/main/java/org/springframework/web/util/OpaqueUriComponents.java index d896518b29a..6e51c77dffd 100644 --- a/spring-web/src/main/java/org/springframework/web/util/OpaqueUriComponents.java +++ b/spring-web/src/main/java/org/springframework/web/util/OpaqueUriComponents.java @@ -33,6 +33,7 @@ import org.springframework.util.ObjectUtils; * @since 3.2 * @see Hierarchical vs Opaque URIs */ +@SuppressWarnings("serial") final class OpaqueUriComponents extends UriComponents { private static final MultiValueMap QUERY_PARAMS_NONE = new LinkedMultiValueMap(0); From 4dde7c4c160a5512dc474a230e3093e8d32354c9 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 23 Jan 2013 13:40:22 +0100 Subject: [PATCH 126/143] MBeanInfoAssembler impls expose actual method parameter names if possible Issue: SPR-9985 --- .../AbstractReflectiveMBeanInfoAssembler.java | 46 ++++++++++++++++--- .../assembler/MetadataMBeanInfoAssembler.java | 9 ++-- ...ethodNameBasedMBeanInfoAssemblerTests.java | 11 ++++- 3 files changed, 54 insertions(+), 12 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/jmx/export/assembler/AbstractReflectiveMBeanInfoAssembler.java b/spring-context/src/main/java/org/springframework/jmx/export/assembler/AbstractReflectiveMBeanInfoAssembler.java index d38e5be9483..f953d9465cf 100644 --- a/spring-context/src/main/java/org/springframework/jmx/export/assembler/AbstractReflectiveMBeanInfoAssembler.java +++ b/spring-context/src/main/java/org/springframework/jmx/export/assembler/AbstractReflectiveMBeanInfoAssembler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,6 +30,8 @@ import javax.management.modelmbean.ModelMBeanOperationInfo; import org.springframework.aop.framework.AopProxyUtils; import org.springframework.aop.support.AopUtils; import org.springframework.beans.BeanUtils; +import org.springframework.core.LocalVariableTableParameterNameDiscoverer; +import org.springframework.core.ParameterNameDiscoverer; import org.springframework.jmx.support.JmxUtils; /** @@ -49,6 +51,7 @@ import org.springframework.jmx.support.JmxUtils; * * @author Rob Harrop * @author Juergen Hoeller + * @author David Boden * @since 1.2 * @see #includeOperation * @see #includeReadAttribute @@ -177,6 +180,8 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean private boolean exposeClassDescriptor = false; + private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); + /** * Set the default for the JMX field "currencyTimeLimit". @@ -254,6 +259,23 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean return this.exposeClassDescriptor; } + /** + * Set the ParameterNameDiscoverer to use for resolving method parameter + * names if needed (e.g. for parameter names of MBean operation methods). + *

The default is {@link LocalVariableTableParameterNameDiscoverer}. + */ + public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) { + this.parameterNameDiscoverer = parameterNameDiscoverer; + } + + /** + * Return the ParameterNameDiscoverer to use for resolving method parameter + * names if needed (may be {@code null} in order to skip parameter detection). + */ + protected ParameterNameDiscoverer getParameterNameDiscoverer() { + return this.parameterNameDiscoverer; + } + /** * Iterate through all properties on the MBean class and gives subclasses @@ -381,7 +403,8 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean * Creates an instance of {@code ModelMBeanOperationInfo} for the * given method. Populates the parameter info for the operation. * @param method the {@code Method} to create a {@code ModelMBeanOperationInfo} for - * @param name the name for the operation info + * @param name the logical name for the operation (method name or property name); + * not used by the default implementation but possibly by subclasses * @param beanKey the key associated with the MBean in the beans map * of the {@code MBeanExporter} * @return the {@code ModelMBeanOperationInfo} @@ -392,7 +415,7 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean return new ModelMBeanOperationInfo(getOperationDescription(method, beanKey), method); } else { - return new ModelMBeanOperationInfo(name, + return new ModelMBeanOperationInfo(method.getName(), getOperationDescription(method, beanKey), getOperationParameters(method, beanKey), method.getReturnType().getName(), @@ -476,16 +499,27 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean /** * Create parameter info for the given method. - *

The default implementation returns an empty arry of {@code MBeanParameterInfo}. + *

The default implementation returns an empty array of {@code MBeanParameterInfo}. * @param method the {@code Method} to get the parameter information for * @param beanKey the key associated with the MBean in the beans map * of the {@code MBeanExporter} * @return the {@code MBeanParameterInfo} array */ protected MBeanParameterInfo[] getOperationParameters(Method method, String beanKey) { - return new MBeanParameterInfo[0]; - } + ParameterNameDiscoverer paramNameDiscoverer = getParameterNameDiscoverer(); + String[] paramNames = (paramNameDiscoverer != null ? paramNameDiscoverer.getParameterNames(method) : null); + if (paramNames == null) { + return new MBeanParameterInfo[0]; + } + MBeanParameterInfo[] info = new MBeanParameterInfo[paramNames.length]; + Class[] typeParameters = method.getParameterTypes(); + for(int i = 0; i < info.length; i++) { + info[i] = new MBeanParameterInfo(paramNames[i], typeParameters[i].getName(), paramNames[i]); + } + + return info; + } /** * Allows subclasses to add extra fields to the {@code Descriptor} for an MBean. diff --git a/spring-context/src/main/java/org/springframework/jmx/export/assembler/MetadataMBeanInfoAssembler.java b/spring-context/src/main/java/org/springframework/jmx/export/assembler/MetadataMBeanInfoAssembler.java index 320a64790e1..b75e956dab9 100644 --- a/spring-context/src/main/java/org/springframework/jmx/export/assembler/MetadataMBeanInfoAssembler.java +++ b/spring-context/src/main/java/org/springframework/jmx/export/assembler/MetadataMBeanInfoAssembler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -36,6 +36,7 @@ import org.springframework.jmx.export.metadata.ManagedOperationParameter; import org.springframework.jmx.export.metadata.ManagedResource; import org.springframework.jmx.support.MetricType; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** @@ -255,19 +256,17 @@ public class MetadataMBeanInfoAssembler extends AbstractReflectiveMBeanInfoAssem @Override protected MBeanParameterInfo[] getOperationParameters(Method method, String beanKey) { ManagedOperationParameter[] params = this.attributeSource.getManagedOperationParameters(method); - if (params == null || params.length == 0) { - return new MBeanParameterInfo[0]; + if (ObjectUtils.isEmpty(params)) { + return super.getOperationParameters(method, beanKey); } MBeanParameterInfo[] parameterInfo = new MBeanParameterInfo[params.length]; Class[] methodParameters = method.getParameterTypes(); - for (int i = 0; i < params.length; i++) { ManagedOperationParameter param = params[i]; parameterInfo[i] = new MBeanParameterInfo(param.getName(), methodParameters[i].getName(), param.getDescription()); } - return parameterInfo; } diff --git a/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerTests.java b/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerTests.java index 2b0bbaeefcf..72da54f6f36 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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 @@ -16,11 +16,13 @@ package org.springframework.jmx.export.assembler; +import javax.management.MBeanOperationInfo; import javax.management.modelmbean.ModelMBeanAttributeInfo; import javax.management.modelmbean.ModelMBeanInfo; /** * @author Rob Harrop + * @author David Boden */ public class MethodNameBasedMBeanInfoAssemblerTests extends AbstractJmxAssemblerTests { @@ -56,6 +58,13 @@ public class MethodNameBasedMBeanInfoAssemblerTests extends AbstractJmxAssembler assertFalse(attr.isWritable()); } + public void testSetNameParameterIsNamed() throws Exception { + ModelMBeanInfo info = getMBeanInfoFromAssembler(); + + MBeanOperationInfo operationSetAge = info.getOperation("setName"); + assertEquals("name", operationSetAge.getSignature()[0].getName()); + } + @Override protected String getApplicationContextPath() { return "org/springframework/jmx/export/assembler/methodNameAssembler.xml"; From a425d717b798356e14dd92ee7c88b13739f6e158 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 23 Jan 2013 15:25:07 +0100 Subject: [PATCH 127/143] ThreadPoolExecutorFactoryBean exposes "createExecutor" method for custom ThreadPoolExecutor subclasses Issue: SPR-9435 --- .../ThreadPoolExecutorFactoryBean.java | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java index 19cd880bbb7..3e04248ea86 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java +++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -126,7 +126,7 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport *

Default is "false", exposing the raw executor as bean reference. * Switch this flag to "true" to strictly prevent clients from * modifying the executor's configuration. - * @see java.util.concurrent.Executors#unconfigurableScheduledExecutorService + * @see java.util.concurrent.Executors#unconfigurableExecutorService */ public void setExposeUnconfigurableExecutor(boolean exposeUnconfigurableExecutor) { this.exposeUnconfigurableExecutor = exposeUnconfigurableExecutor; @@ -137,9 +137,8 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { BlockingQueue queue = createQueue(this.queueCapacity); - ThreadPoolExecutor executor = new ThreadPoolExecutor( - this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS, - queue, threadFactory, rejectedExecutionHandler); + ThreadPoolExecutor executor = createExecutor(this.corePoolSize, this.maxPoolSize, + this.keepAliveSeconds, queue, threadFactory, rejectedExecutionHandler); if (this.allowCoreThreadTimeOut) { executor.allowCoreThreadTimeOut(true); } @@ -151,6 +150,27 @@ public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport return executor; } + /** + * Create a new instance of {@link ThreadPoolExecutor} or a subclass thereof. + *

The default implementation creates a standard {@link ThreadPoolExecutor}. + * Can be overridden to provide custom {@link ThreadPoolExecutor} subclasses. + * @param corePoolSize the specified core pool size + * @param maxPoolSize the specified maximum pool size + * @param keepAliveSeconds the specified keep-alive time in seconds + * @param queue the BlockingQueue to use + * @param threadFactory the ThreadFactory to use + * @param rejectedExecutionHandler the RejectedExecutionHandler to use + * @return a new ThreadPoolExecutor instance + * @see #afterPropertiesSet() + */ + protected ThreadPoolExecutor createExecutor( + int corePoolSize, int maxPoolSize, int keepAliveSeconds, BlockingQueue queue, + ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { + + return new ThreadPoolExecutor(corePoolSize, maxPoolSize, + keepAliveSeconds, TimeUnit.SECONDS, queue, threadFactory, rejectedExecutionHandler); + } + /** * Create the BlockingQueue to use for the ThreadPoolExecutor. *

A LinkedBlockingQueue instance will be created for a positive From 0dcc0f2227c2c5442124c7b58d17bcfa993821b5 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 23 Jan 2013 15:26:16 +0100 Subject: [PATCH 128/143] Added further MySQL error code for DataIntegrityViolationException Issue: SPR-9237 --- .../org/springframework/jdbc/support/sql-error-codes.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-jdbc/src/main/resources/org/springframework/jdbc/support/sql-error-codes.xml b/spring-jdbc/src/main/resources/org/springframework/jdbc/support/sql-error-codes.xml index 50a5167aedd..8b06d1eb627 100644 --- a/spring-jdbc/src/main/resources/org/springframework/jdbc/support/sql-error-codes.xml +++ b/spring-jdbc/src/main/resources/org/springframework/jdbc/support/sql-error-codes.xml @@ -151,7 +151,7 @@ 1062 - 630,839,840,893,1169,1215,1216,1217,1451,1452,1557 + 630,839,840,893,1169,1215,1216,1217,1364,1451,1452,1557 1 From 4c823a3f9bf9413eea6b61a8c217eb1efff3e853 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 23 Jan 2013 16:02:42 +0100 Subject: [PATCH 129/143] DisposableBeanAdapter detects "shutdown" as a destroy method as well (for EHCache CacheManager setup) Issue: SPR-9713 --- .../support/DisposableBeanAdapter.java | 11 +++++-- .../DestroyMethodInferenceTests.java | 29 ++++++++++++++++--- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java index 990438597f6..6a498344a92 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -61,6 +61,8 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable { private static final String CLOSE_METHOD_NAME = "close"; + private static final String SHUTDOWN_METHOD_NAME = "shutdown"; + private static final Log logger = LogFactory.getLog(DisposableBeanAdapter.class); private static Class closeableInterface; @@ -176,7 +178,12 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable { return bean.getClass().getMethod(CLOSE_METHOD_NAME).getName(); } catch (NoSuchMethodException ex) { - // no candidate destroy method found + try { + return bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName(); + } + catch (NoSuchMethodException ex2) { + // no candidate destroy method found + } } } return null; diff --git a/spring-context/src/test/java/org/springframework/context/annotation/DestroyMethodInferenceTests.java b/spring-context/src/test/java/org/springframework/context/annotation/DestroyMethodInferenceTests.java index 7baf82681eb..87999283a31 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/DestroyMethodInferenceTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/DestroyMethodInferenceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,21 @@ package org.springframework.context.annotation; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; - import java.io.Closeable; import java.io.IOException; import org.junit.Test; + import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +/** + * @author Chris Beams + * @author Juergen Hoeller + */ public class DestroyMethodInferenceTests { @Test @@ -39,6 +44,7 @@ public class DestroyMethodInferenceTests { WithInheritedCloseMethod c4 = ctx.getBean("c4", WithInheritedCloseMethod.class); WithInheritedCloseMethod c5 = ctx.getBean("c5", WithInheritedCloseMethod.class); WithNoCloseMethod c6 = ctx.getBean("c6", WithNoCloseMethod.class); + WithLocalShutdownMethod c7 = ctx.getBean("c7", WithLocalShutdownMethod.class); assertThat(c0.closed, is(false)); assertThat(c1.closed, is(false)); @@ -47,6 +53,7 @@ public class DestroyMethodInferenceTests { assertThat(c4.closed, is(false)); assertThat(c5.closed, is(false)); assertThat(c6.closed, is(false)); + assertThat(c7.closed, is(false)); ctx.close(); assertThat("c0", c0.closed, is(true)); assertThat("c1", c1.closed, is(true)); @@ -55,6 +62,7 @@ public class DestroyMethodInferenceTests { assertThat("c4", c4.closed, is(true)); assertThat("c5", c5.closed, is(true)); assertThat("c6", c6.closed, is(false)); + assertThat("c7", c7.closed, is(true)); } @Test @@ -121,6 +129,11 @@ public class DestroyMethodInferenceTests { public WithNoCloseMethod c6() { return new WithNoCloseMethod(); } + + @Bean + public WithLocalShutdownMethod c7() { + return new WithLocalShutdownMethod(); + } } @@ -149,4 +162,12 @@ public class DestroyMethodInferenceTests { static class WithNoCloseMethod { boolean closed = false; } + + static class WithLocalShutdownMethod { + boolean closed = false; + public void shutdown() { + closed = true; + } + } + } From ede9d535ea89b15e58efb11dfa4136659e6adf3c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 23 Jan 2013 16:09:37 +0100 Subject: [PATCH 130/143] Updated resolvePath javadoc to reflect Environment-based placeholder resolution Issue: SPR-9455 --- .../AbstractRefreshableConfigApplicationContext.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractRefreshableConfigApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractRefreshableConfigApplicationContext.java index 138f45526cd..66aff5979be 100644 --- a/spring-context/src/main/java/org/springframework/context/support/AbstractRefreshableConfigApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/AbstractRefreshableConfigApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,7 +17,6 @@ package org.springframework.context.support; import org.springframework.beans.factory.BeanNameAware; - import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.util.Assert; @@ -114,12 +113,13 @@ public abstract class AbstractRefreshableConfigApplicationContext extends Abstra /** * Resolve the given path, replacing placeholders with corresponding - * system property values if necessary. Applied to config locations. + * environment property values if necessary. Applied to config locations. * @param path the original file path * @return the resolved file path + * @see org.springframework.core.env.Environment#resolveRequiredPlaceholders(String) */ protected String resolvePath(String path) { - return this.getEnvironment().resolveRequiredPlaceholders(path); + return getEnvironment().resolveRequiredPlaceholders(path); } From 049169d19fac59887bb4edbe36f85c3c3ab341b3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 23 Jan 2013 22:16:35 +0100 Subject: [PATCH 131/143] ResourcePropertyResource accepts EncodedResource for properties files with a specific encoding Also added constructor with Charset argument to EncodedResource. Issue: SPR-10096 --- .../core/io/support/EncodedResource.java | 58 +++++++++++++- .../io/support/PropertiesLoaderSupport.java | 30 +------- .../io/support/PropertiesLoaderUtils.java | 56 +++++++++++++- .../io/support/ResourcePropertySource.java | 75 +++++++++---------- 4 files changed, 148 insertions(+), 71 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/io/support/EncodedResource.java b/spring-core/src/main/java/org/springframework/core/io/support/EncodedResource.java index 405a16e2a88..04a173836c1 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/EncodedResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/EncodedResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,8 +17,10 @@ package org.springframework.core.io.support; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; +import java.nio.charset.Charset; import org.springframework.core.io.Resource; import org.springframework.util.Assert; @@ -39,7 +41,9 @@ public class EncodedResource { private final Resource resource; - private final String encoding; + private String encoding; + + private Charset charset; /** @@ -48,7 +52,8 @@ public class EncodedResource { * @param resource the Resource to hold */ public EncodedResource(Resource resource) { - this(resource, null); + Assert.notNull(resource, "Resource must not be null"); + this.resource = resource; } /** @@ -63,6 +68,18 @@ public class EncodedResource { this.encoding = encoding; } + /** + * Create a new EncodedResource for the given Resource, + * using the specified encoding. + * @param resource the Resource to hold + * @param charset the charset to use for reading from the resource + */ + public EncodedResource(Resource resource, Charset charset) { + Assert.notNull(resource, "Resource must not be null"); + this.resource = resource; + this.charset = charset; + } + /** * Return the Resource held. @@ -79,13 +96,36 @@ public class EncodedResource { return this.encoding; } + /** + * Return the charset to use for reading from the resource, + * or {@code null} if none specified. + */ + public final Charset getCharset() { + return this.charset; + } + + + /** + * Determine whether a {@link Reader} is required as opposed to an {@link InputStream}, + * i.e. whether an encoding or a charset has been specified. + * @see #getReader() + * @see #getInputStream() + */ + public boolean requiresReader() { + return (this.encoding != null || this.charset != null); + } + /** * Open a {@code java.io.Reader} for the specified resource, * using the specified encoding (if any). * @throws IOException if opening the Reader failed + * @see #requiresReader() */ public Reader getReader() throws IOException { - if (this.encoding != null) { + if (this.charset != null) { + return new InputStreamReader(this.resource.getInputStream(), this.charset); + } + else if (this.encoding != null) { return new InputStreamReader(this.resource.getInputStream(), this.encoding); } else { @@ -93,6 +133,16 @@ public class EncodedResource { } } + /** + * Open an {@code java.io.InputStream} for the specified resource, + * typically assuming that there is no specific encoding to use. + * @throws IOException if opening the InputStream failed + * @see #requiresReader() + */ + public InputStream getInputStream() throws IOException { + return this.resource.getInputStream(); + } + @Override public boolean equals(Object obj) { diff --git a/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderSupport.java b/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderSupport.java index d99e69806e6..1e68ba23d88 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderSupport.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,8 +17,6 @@ package org.springframework.core.io.support; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.util.Properties; import org.apache.commons.logging.Log; @@ -39,9 +37,6 @@ import org.springframework.util.PropertiesPersister; */ public abstract class PropertiesLoaderSupport { - public static final String XML_FILE_EXTENSION = ".xml"; - - /** Logger available to subclasses */ protected final Log logger = LogFactory.getLog(getClass()); @@ -167,7 +162,7 @@ public abstract class PropertiesLoaderSupport { /** * Load properties into the given instance. * @param props the Properties instance to load into - * @throws java.io.IOException in case of I/O errors + * @throws IOException in case of I/O errors * @see #setLocations */ protected void loadProperties(Properties props) throws IOException { @@ -176,21 +171,9 @@ public abstract class PropertiesLoaderSupport { if (logger.isInfoEnabled()) { logger.info("Loading properties file from " + location); } - InputStream is = null; try { - is = location.getInputStream(); - String filename = location.getFilename(); - if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) { - this.propertiesPersister.loadFromXml(props, is); - } - else { - if (this.fileEncoding != null) { - this.propertiesPersister.load(props, new InputStreamReader(is, this.fileEncoding)); - } - else { - this.propertiesPersister.load(props, is); - } - } + PropertiesLoaderUtils.fillProperties( + props, new EncodedResource(location, this.fileEncoding), this.propertiesPersister); } catch (IOException ex) { if (this.ignoreResourceNotFound) { @@ -202,11 +185,6 @@ public abstract class PropertiesLoaderSupport { throw ex; } } - finally { - if (is != null) { - is.close(); - } - } } } } diff --git a/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java b/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java index 6f3308d8308..6aaa486fd37 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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.core.io.support; import java.io.IOException; import java.io.InputStream; +import java.io.Reader; import java.net.URL; import java.net.URLConnection; import java.util.Enumeration; @@ -26,6 +27,8 @@ import java.util.Properties; import org.springframework.core.io.Resource; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.DefaultPropertiesPersister; +import org.springframework.util.PropertiesPersister; import org.springframework.util.ResourceUtils; /** @@ -42,6 +45,9 @@ import org.springframework.util.ResourceUtils; */ public abstract class PropertiesLoaderUtils { + private static final String XML_FILE_EXTENSION = ".xml"; + + /** * Load properties from the given resource. * @param resource the resource to load from @@ -120,4 +126,52 @@ public abstract class PropertiesLoaderUtils { return properties; } + + /** + * Load the properties from the given encoded resource. + * @see #fillProperties + */ + static Properties loadProperties(EncodedResource resource) throws IOException { + Properties props = new Properties(); + fillProperties(props, resource, new DefaultPropertiesPersister()); + return props; + } + + /** + * Actually load properties from the given EncodedResource into the given Properties instance. + * @param props the Properties instance to load into + * @param resource the resource to load from + * @param persister the PropertiesPersister to use + * @throws IOException in case of I/O errors + */ + static void fillProperties(Properties props, EncodedResource resource, PropertiesPersister persister) + throws IOException { + + InputStream stream = null; + Reader reader = null; + try { + String filename = resource.getResource().getFilename(); + if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) { + stream = resource.getInputStream(); + persister.loadFromXml(props, stream); + } + else if (resource.requiresReader()) { + reader = resource.getReader(); + persister.load(props, reader); + } + else { + stream = resource.getInputStream(); + persister.load(props, stream); + } + } + finally { + if (stream != null) { + stream.close(); + } + if (reader != null) { + reader.close(); + } + } + } + } diff --git a/spring-core/src/main/java/org/springframework/core/io/support/ResourcePropertySource.java b/spring-core/src/main/java/org/springframework/core/io/support/ResourcePropertySource.java index 705d8d44e71..d07955456cc 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/ResourcePropertySource.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/ResourcePropertySource.java @@ -17,12 +17,11 @@ package org.springframework.core.io.support; import java.io.IOException; -import java.io.InputStream; import java.util.Properties; import org.springframework.core.env.PropertiesPropertySource; +import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; -import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; /** @@ -35,16 +34,34 @@ import org.springframework.util.StringUtils; * return non-{@code null} and end in ".xml". * * @author Chris Beams + * @author Juergen Hoeller * @since 3.1 */ public class ResourcePropertySource extends PropertiesPropertySource { /** * Create a PropertySource having the given name based on Properties - * loaded from the given resource. + * loaded from the given encoded resource. + */ + public ResourcePropertySource(String name, EncodedResource resource) throws IOException { + super(name, PropertiesLoaderUtils.loadProperties(resource)); + } + + /** + * Create a PropertySource based on Properties loaded from the given resource. + * The name of the PropertySource will be generated based on the + * {@link Resource#getDescription() description} of the given resource. + */ + public ResourcePropertySource(EncodedResource resource) throws IOException { + this(getNameForResource(resource.getResource()), resource); + } + + /** + * Create a PropertySource having the given name based on Properties + * loaded from the given encoded resource. */ public ResourcePropertySource(String name, Resource resource) throws IOException { - super(name, loadPropertiesForResource(resource)); + super(name, PropertiesLoaderUtils.loadProperties(new EncodedResource(resource))); } /** @@ -62,17 +79,7 @@ public class ResourcePropertySource extends PropertiesPropertySource { * resource (assuming it is prefixed with {@code classpath:}). */ public ResourcePropertySource(String name, String location, ClassLoader classLoader) throws IOException { - this(name, getResourceForLocation(location, classLoader)); - } - - /** - * Create a PropertySource having the given name based on Properties loaded from - * the given resource location. The default thread context class loader will be - * used to load the resource (assuming the location string is prefixed with - * {@code classpath:}. - */ - public ResourcePropertySource(String name, String location) throws IOException { - this(name, location, ClassUtils.getDefaultClassLoader()); + this(name, new DefaultResourceLoader(classLoader).getResource(location)); } /** @@ -83,7 +90,17 @@ public class ResourcePropertySource extends PropertiesPropertySource { * resource. */ public ResourcePropertySource(String location, ClassLoader classLoader) throws IOException { - this(getResourceForLocation(location, classLoader)); + this(new DefaultResourceLoader(classLoader).getResource(location)); + } + + /** + * Create a PropertySource having the given name based on Properties loaded from + * the given resource location. The default thread context class loader will be + * used to load the resource (assuming the location string is prefixed with + * {@code classpath:}. + */ + public ResourcePropertySource(String name, String location) throws IOException { + this(name, new DefaultResourceLoader().getResource(location)); } /** @@ -92,34 +109,12 @@ public class ResourcePropertySource extends PropertiesPropertySource { * {@link Resource#getDescription() description} of the resource. */ public ResourcePropertySource(String location) throws IOException { - this(getResourceForLocation(location, ClassUtils.getDefaultClassLoader())); + this(new DefaultResourceLoader().getResource(location)); } - private static Resource getResourceForLocation(String location, ClassLoader classLoader) { - return new PathMatchingResourcePatternResolver(classLoader).getResource(location); - } - - private static Properties loadPropertiesForResource(Resource resource) throws IOException { - Properties props = new Properties(); - InputStream is = resource.getInputStream(); - String filename = resource.getFilename(); - if (filename != null && filename.endsWith(".xml")) { - props.loadFromXML(is); - } - else { - props.load(is); - } - try { - is.close(); - } catch (IOException ex) { - // ignore - } - return props; - } - /** - * Returns the description string for the resource, and if empty returns + * Return the description string for the resource, and if empty returns * the class name of the resource plus its identity hash code. */ private static String getNameForResource(Resource resource) { From e5d937848296dc30cdad3403c1b04fe09c83b887 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 23 Jan 2013 22:17:38 +0100 Subject: [PATCH 132/143] Removed pre-JDK-1.5 checks --- .../util/DefaultPropertiesPersister.java | 36 ++++--------------- 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/DefaultPropertiesPersister.java b/spring-core/src/main/java/org/springframework/util/DefaultPropertiesPersister.java index 19f2698c367..1337a5a5440 100644 --- a/spring-core/src/main/java/org/springframework/util/DefaultPropertiesPersister.java +++ b/spring-core/src/main/java/org/springframework/util/DefaultPropertiesPersister.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -33,8 +33,8 @@ import java.util.Properties; * *

Allows for reading from any Reader and writing to any Writer, for example * to specify a charset for a properties file. This is a capability that standard - * {@code java.util.Properties} unfortunately lacks up until JDK 1.5: - * You can only load files using the ISO-8859-1 charset there. + * {@code java.util.Properties} unfortunately lacked up until JDK 1.5: + * You were only able to load files using the ISO-8859-1 charset there. * *

Loading from and storing to a stream delegates to {@code Properties.load} * and {@code Properties.store}, respectively, to be fully compatible with @@ -49,20 +49,11 @@ import java.util.Properties; * an encoding for a Reader/Writer (like ReloadableResourceBundleMessageSource's * "defaultEncoding" and "fileEncodings" properties). * - *

As of Spring 1.2.2, this implementation also supports properties XML files, - * through the {@code loadFromXml} and {@code storeToXml} methods. - * The default implementations delegate to JDK 1.5's corresponding methods, - * throwing an exception if running on an older JDK. Those implementations - * could be subclassed to apply custom XML handling on JDK 1.4, for example. - * * @author Juergen Hoeller * @since 10.03.2004 * @see java.util.Properties * @see java.util.Properties#load * @see java.util.Properties#store - * @see org.springframework.context.support.ReloadableResourceBundleMessageSource#setPropertiesPersister - * @see org.springframework.context.support.ReloadableResourceBundleMessageSource#setDefaultEncoding - * @see org.springframework.context.support.ReloadableResourceBundleMessageSource#setFileEncodings */ public class DefaultPropertiesPersister implements PropertiesPersister { @@ -228,30 +219,15 @@ public class DefaultPropertiesPersister implements PropertiesPersister { public void loadFromXml(Properties props, InputStream is) throws IOException { - try { - props.loadFromXML(is); - } - catch (NoSuchMethodError err) { - throw new IOException("Cannot load properties XML file - not running on JDK 1.5+: " + err.getMessage()); - } + props.loadFromXML(is); } public void storeToXml(Properties props, OutputStream os, String header) throws IOException { - try { - props.storeToXML(os, header); - } - catch (NoSuchMethodError err) { - throw new IOException("Cannot store properties XML file - not running on JDK 1.5+: " + err.getMessage()); - } + props.storeToXML(os, header); } public void storeToXml(Properties props, OutputStream os, String header, String encoding) throws IOException { - try { - props.storeToXML(os, header, encoding); - } - catch (NoSuchMethodError err) { - throw new IOException("Cannot store properties XML file - not running on JDK 1.5+: " + err.getMessage()); - } + props.storeToXML(os, header, encoding); } } From 575926932ddac03afb3e275dbb4a0e6d270803eb Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 23 Jan 2013 22:17:48 +0100 Subject: [PATCH 133/143] Polishing --- .../beans/factory/support/AbstractBeanFactory.java | 5 ++++- .../beans/factory/support/DefaultListableBeanFactory.java | 4 ++-- .../springframework/core/env/PropertiesPropertySource.java | 5 ++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index f298c6522ff..6ec60e6e61b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -746,7 +746,10 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp public String resolveEmbeddedValue(String value) { String result = value; for (StringValueResolver resolver : this.embeddedValueResolvers) { - result = (result == null ? null : resolver.resolveStringValue(result)); + if (result == null) { + return null; + } + result = resolver.resolveStringValue(result); } return result; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 23512f9e232..9e681d0dd69 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -277,14 +277,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto for (String beanName : beanNames) { T beanInstance = getBean(beanName, requiredType); if (isPrimary(beanName, beanInstance)) { - if(primaryBean != null) { + if (primaryBean != null) { throw new NoUniqueBeanDefinitionException(requiredType, beanNames.length, "more than one 'primary' bean found of required type: " + Arrays.asList(beanNames)); } primaryBean = beanInstance; } } - if(primaryBean != null) { + if (primaryBean != null) { return primaryBean; } throw new NoUniqueBeanDefinitionException(requiredType, beanNames); diff --git a/spring-core/src/main/java/org/springframework/core/env/PropertiesPropertySource.java b/spring-core/src/main/java/org/springframework/core/env/PropertiesPropertySource.java index 8da8c15d8b5..492d142d372 100644 --- a/spring-core/src/main/java/org/springframework/core/env/PropertiesPropertySource.java +++ b/spring-core/src/main/java/org/springframework/core/env/PropertiesPropertySource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2013 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,13 +31,12 @@ import java.util.Properties; * * @author Chris Beams * @since 3.1 - * @see org.springframework.mock.env.MockPropertySource */ public class PropertiesPropertySource extends MapPropertySource { @SuppressWarnings({ "unchecked", "rawtypes" }) public PropertiesPropertySource(String name, Properties source) { - super(name, (Map)source); + super(name, (Map) source); } } From 919aeb5df9a7b170c30afa6a417507e3bbd75eba Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 23 Jan 2013 23:34:21 +0100 Subject: [PATCH 134/143] Final preparations for 3.2.1 --- src/dist/changelog.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/dist/changelog.txt b/src/dist/changelog.txt index af25c8c883e..82ae86682b0 100644 --- a/src/dist/changelog.txt +++ b/src/dist/changelog.txt @@ -11,8 +11,12 @@ Changes in version 3.2.1 (2013-01-24) * added dedicated sort method to AnnotationAwareOrderComparator (SPR-9625) * BridgeMethodResolver properly handles bridge methods in interfaces (SPR-9330) * LocalVariableTableParameterNameDiscoverer works for bridge methods as well (SPR-9429) +* added constructor with Charset argument to EncodedResource (SPR-10096) +* ResourcePropertyResource accepts EncodedResource for properties files with a specific encoding (SPR-10096) * CachedIntrospectionResults.clearClassLoader(null) removes cached classes for the system class loader (SPR-9189) +* DisposableBeanAdapter detects "shutdown" as a destroy method as well (for EHCache CacheManager setup; SPR-9713) * introduced NoUniqueBeanDefinitionException as a dedicated subclass of NoSuchBeanDefinitionException (SPR-10194) +* DefaultListableBeanFactory's getBean(Class) checks primary marker in case of multiple matches (SPR-7854) * fixed QualifierAnnotationAutowireCandidateResolver's detection of custom qualifier annotations (SPR-10107) * fixed AbstractAutoProxyCreator to accept null bean names again (SPR-10108) * AbstractAdvisingBeanPostProcessor caches per bean target class, working for null bean names as well (SPR-10144) @@ -21,8 +25,10 @@ Changes in version 3.2.1 (2013-01-24) * SpringBeanAutowiringInterceptor eagerly releases BeanFactory if post-construction fails (SPR-10013) * added "exposeAccessContext" flag JndiRmiClientInterceptor/ProxyFactoryBean (for WebLogic; SPR-9428) * MBeanExporter does not log warnings for manually unregistered MBeans (SPR-9451) +* MBeanInfoAssembler impls expose actual method parameter names if possible (SPR-9985) * AbstractCacheManager accepts no caches defined, allowing for EHCache default cache setup (SPR-7955) * EhCacheManagerFactoryBean applies cacheManagerName ahead of creation (for EHCache 2.5 compatibility; SPR-9171) +* ThreadPoolExecutorFactoryBean exposes "createExecutor" method for custom ThreadPoolExecutor subclasses (SPR-9435) * added "awaitTerminationSeconds" property to ThreadPoolTaskExecutor/ThreadPoolTaskScheduler (SPR-5387) * aligned XML scheduled-task elements with @Scheduled in terms of kicking in after context refresh (SPR-9231) * reintroduced "mode" and "proxy-target-class" attributes in spring-task-3.1/3.2.xsd (SPR-10177) From 7f928e83ffb3abdd62f9a1dc4ed1062bfc3b06f6 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 23 Jan 2013 15:09:47 -0800 Subject: [PATCH 135/143] Change merge.into project dependencies to provided Change 'compile' dependencies to 'provided' for projects that are merged into other projects. This seems to prevent '-sources' and '-javadoc' jars from appearing on the classpath which can break javadoc generation. --- build.gradle | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 813816bdfd9..19fe164e4dc 100644 --- a/build.gradle +++ b/build.gradle @@ -526,8 +526,8 @@ project("spring-orm-hibernate4") { description = "Spring Object/Relational Mapping - Hibernate 4 support" merge.into = project(":spring-orm") dependencies { - compile(project(":spring-tx")) - compile(project(":spring-jdbc")) + provided(project(":spring-tx")) + provided(project(":spring-jdbc")) optional("org.hibernate:hibernate-core:4.1.0.Final") optional("org.hibernate:hibernate-entitymanager:4.1.0.Final") optional(project(":spring-web")) @@ -597,8 +597,8 @@ project("spring-webmvc-tiles3") { description = "Spring Framework Tiles3 Integration" merge.into = project(":spring-webmvc") dependencies { - compile(project(":spring-context")) - compile(project(":spring-web")) + provided(project(":spring-context")) + provided(project(":spring-web")) provided("javax.el:el-api:1.0") provided("javax.servlet:jstl:1.2") provided("javax.servlet.jsp:jsp-api:2.1") @@ -689,8 +689,8 @@ project("spring-test-mvc") { description = "Spring Test MVC Framework" merge.into = project(":spring-test") dependencies { - optional(project(":spring-context")) - compile(project(":spring-webmvc")) + provided(project(":spring-context")) + provided(project(":spring-webmvc")) provided("javax.servlet:javax.servlet-api:3.0.1") optional("org.hamcrest:hamcrest-core:1.3") optional("com.jayway.jsonpath:json-path:0.8.1") From d46a82bbb0156fa6037b2bdd2543a655f474817c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 24 Jan 2013 11:19:06 +0100 Subject: [PATCH 136/143] Added note on thread safety to TypeConverter and SimpleTypeConverter javadoc Issue: SPR-8659 --- .../java/org/springframework/beans/SimpleTypeConverter.java | 5 ++++- .../main/java/org/springframework/beans/TypeConverter.java | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/SimpleTypeConverter.java b/spring-beans/src/main/java/org/springframework/beans/SimpleTypeConverter.java index 1e074dcc338..8ab240836bf 100644 --- a/spring-beans/src/main/java/org/springframework/beans/SimpleTypeConverter.java +++ b/spring-beans/src/main/java/org/springframework/beans/SimpleTypeConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,9 @@ package org.springframework.beans; * algorithm (including delegation to {@link java.beans.PropertyEditor} and * {@link org.springframework.core.convert.ConversionService}) underneath. * + *

Note: Due to its reliance on {@link java.beans.PropertyEditor PropertyEditors}, + * SimpleTypeConverter is not thread-safe. Use a separate instance for each thread. + * * @author Juergen Hoeller * @since 2.0 * @see BeanWrapperImpl diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeConverter.java b/spring-beans/src/main/java/org/springframework/beans/TypeConverter.java index 2efc1edf737..6d69a63ce69 100644 --- a/spring-beans/src/main/java/org/springframework/beans/TypeConverter.java +++ b/spring-beans/src/main/java/org/springframework/beans/TypeConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,10 @@ import org.springframework.core.MethodParameter; * Interface that defines type conversion methods. Typically (but not necessarily) * implemented in conjunction with the {@link PropertyEditorRegistry} interface. * + *

Note: Since TypeConverter implementations are typically based on + * {@link java.beans.PropertyEditor PropertyEditors} which aren't thread-safe, + * TypeConverters themselves are not to be considered as thread-safe either. + * * @author Juergen Hoeller * @since 2.0 * @see SimpleTypeConverter From 078a1c5db84467a52e9de4078d139ee6fded08c7 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 24 Jan 2013 14:56:45 +0100 Subject: [PATCH 137/143] Made EncodedResource based variant public; consistently detect XML properties across all variants Issue: SPR-9078 --- .../io/support/PropertiesLoaderUtils.java | 172 ++++++++++-------- 1 file changed, 97 insertions(+), 75 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java b/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java index 6aaa486fd37..503f2bb1bb5 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java @@ -49,92 +49,27 @@ public abstract class PropertiesLoaderUtils { /** - * Load properties from the given resource. - * @param resource the resource to load from - * @return the populated Properties instance - * @throws IOException if loading failed + * Load properties from the given EncodedResource, + * potentially defining a specific encoding for the properties file. + * @see #fillProperties(java.util.Properties, EncodedResource) */ - public static Properties loadProperties(Resource resource) throws IOException { + public static Properties loadProperties(EncodedResource resource) throws IOException { Properties props = new Properties(); fillProperties(props, resource); return props; } /** - * Fill the given properties from the given resource. - * @param props the Properties instance to fill + * Fill the given properties from the given EncodedResource, + * potentially defining a specific encoding for the properties file. + * @param props the Properties instance to load into * @param resource the resource to load from - * @throws IOException if loading failed + * @throws IOException in case of I/O errors */ - public static void fillProperties(Properties props, Resource resource) throws IOException { - InputStream is = resource.getInputStream(); - try { - props.load(is); - } - finally { - is.close(); - } - } + public static void fillProperties(Properties props, EncodedResource resource) + throws IOException { - /** - * Load all properties from the given class path resource, - * using the default class loader. - *

Merges properties if more than one resource of the same name - * found in the class path. - * @param resourceName the name of the class path resource - * @return the populated Properties instance - * @throws IOException if loading failed - */ - public static Properties loadAllProperties(String resourceName) throws IOException { - return loadAllProperties(resourceName, null); - } - - /** - * Load all properties from the given class path resource, - * using the given class loader. - *

Merges properties if more than one resource of the same name - * found in the class path. - * @param resourceName the name of the class path resource - * @param classLoader the ClassLoader to use for loading - * (or {@code null} to use the default class loader) - * @return the populated Properties instance - * @throws IOException if loading failed - */ - public static Properties loadAllProperties(String resourceName, ClassLoader classLoader) throws IOException { - Assert.notNull(resourceName, "Resource name must not be null"); - ClassLoader clToUse = classLoader; - if (clToUse == null) { - clToUse = ClassUtils.getDefaultClassLoader(); - } - Properties properties = new Properties(); - Enumeration urls = clToUse.getResources(resourceName); - while (urls.hasMoreElements()) { - URL url = (URL) urls.nextElement(); - InputStream is = null; - try { - URLConnection con = url.openConnection(); - ResourceUtils.useCachesIfNecessary(con); - is = con.getInputStream(); - properties.load(is); - } - finally { - if (is != null) { - is.close(); - } - } - } - return properties; - } - - - /** - * Load the properties from the given encoded resource. - * @see #fillProperties - */ - static Properties loadProperties(EncodedResource resource) throws IOException { - Properties props = new Properties(); fillProperties(props, resource, new DefaultPropertiesPersister()); - return props; } /** @@ -174,4 +109,91 @@ public abstract class PropertiesLoaderUtils { } } + /** + * Load properties from the given resource (in ISO-8859-1 encoding). + * @param resource the resource to load from + * @return the populated Properties instance + * @throws IOException if loading failed + * @see #fillProperties(java.util.Properties, Resource) + */ + public static Properties loadProperties(Resource resource) throws IOException { + Properties props = new Properties(); + fillProperties(props, resource); + return props; + } + + /** + * Fill the given properties from the given resource (in ISO-8859-1 encoding). + * @param props the Properties instance to fill + * @param resource the resource to load from + * @throws IOException if loading failed + */ + public static void fillProperties(Properties props, Resource resource) throws IOException { + InputStream is = resource.getInputStream(); + try { + String filename = resource.getFilename(); + if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) { + props.loadFromXML(is); + } + else { + props.load(is); + } + } + finally { + is.close(); + } + } + + /** + * Load all properties from the specified class path resource + * (in ISO-8859-1 encoding), using the default class loader. + *

Merges properties if more than one resource of the same name + * found in the class path. + * @param resourceName the name of the class path resource + * @return the populated Properties instance + * @throws IOException if loading failed + */ + public static Properties loadAllProperties(String resourceName) throws IOException { + return loadAllProperties(resourceName, null); + } + + /** + * Load all properties from the specified class path resource + * (in ISO-8859-1 encoding), using the given class loader. + *

Merges properties if more than one resource of the same name + * found in the class path. + * @param resourceName the name of the class path resource + * @param classLoader the ClassLoader to use for loading + * (or {@code null} to use the default class loader) + * @return the populated Properties instance + * @throws IOException if loading failed + */ + public static Properties loadAllProperties(String resourceName, ClassLoader classLoader) throws IOException { + Assert.notNull(resourceName, "Resource name must not be null"); + ClassLoader clToUse = classLoader; + if (clToUse == null) { + clToUse = ClassUtils.getDefaultClassLoader(); + } + Properties props = new Properties(); + Enumeration urls = clToUse.getResources(resourceName); + while (urls.hasMoreElements()) { + URL url = (URL) urls.nextElement(); + URLConnection con = url.openConnection(); + ResourceUtils.useCachesIfNecessary(con); + InputStream is = con.getInputStream(); + try { + if (resourceName != null && resourceName.endsWith(XML_FILE_EXTENSION)) { + props.loadFromXML(is); + } + else { + props.load(is); + } + } + finally { + is.close(); + } + } + return props; + } + } From 39c00c489e47c2737cea45df3157e719900699c1 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Thu, 24 Jan 2013 14:15:53 +0100 Subject: [PATCH 138/143] Avoid UnsupportedOperationEx. with active SecurityManager Issue: SPR-9970 --- ...onmentSecurityManagerIntegrationTests.java | 118 ++++++++++++++++++ .../core/env/ReadOnlySystemAttributesMap.java | 20 +-- .../env/SystemEnvironmentPropertySource.java | 10 +- .../core/env/StandardEnvironmentTests.java | 4 +- 4 files changed, 137 insertions(+), 15 deletions(-) create mode 100644 spring-context/src/test/java/org/springframework/context/support/EnvironmentSecurityManagerIntegrationTests.java diff --git a/spring-context/src/test/java/org/springframework/context/support/EnvironmentSecurityManagerIntegrationTests.java b/spring-context/src/test/java/org/springframework/context/support/EnvironmentSecurityManagerIntegrationTests.java new file mode 100644 index 00000000000..42647392832 --- /dev/null +++ b/spring-context/src/test/java/org/springframework/context/support/EnvironmentSecurityManagerIntegrationTests.java @@ -0,0 +1,118 @@ +/* + * Copyright 2002-2013 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.context.support; + +import static java.lang.String.format; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.security.AccessControlException; +import java.security.Permission; +import java.util.Map; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; +import org.springframework.context.annotation.Profile; +import org.springframework.core.env.AbstractEnvironment; +import org.springframework.core.env.StandardEnvironmentTests; +import org.springframework.stereotype.Component; + + +/** + * Tests integration between Environment and SecurityManagers. See SPR-9970. + * + * @author Chris Beams + */ +public class EnvironmentSecurityManagerIntegrationTests { + + private SecurityManager originalSecurityManager; + private Map env; + + @Before + public void setUp() { + originalSecurityManager = System.getSecurityManager(); + env = StandardEnvironmentTests.getModifiableSystemEnvironment(); + env.put(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, "p1"); + } + + @After + public void tearDown() { + env.remove(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME); + System.setSecurityManager(originalSecurityManager); + } + + @Test + public void securityManagerDisallowsAccessToSystemEnvironmentButAllowsAccessToIndividualKeys() { + SecurityManager securityManager = new SecurityManager() { + @Override + public void checkPermission(Permission perm) { + // disallowing access to System#getenv means that our + // ReadOnlySystemAttributesMap will come into play. + if ("getenv.*".equals(perm.getName())) { + throw new AccessControlException( + "Accessing the system environment is disallowed"); + } + } + }; + System.setSecurityManager(securityManager); + + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(bf); + reader.register(C1.class); + assertThat(bf.containsBean("c1"), is(true)); + } + + @Test + public void securityManagerDisallowsAccessToSystemEnvironmentAndDisallowsAccessToIndividualKey() { + SecurityManager securityManager = new SecurityManager() { + @Override + public void checkPermission(Permission perm) { + // disallowing access to System#getenv means that our + // ReadOnlySystemAttributesMap will come into play. + if ("getenv.*".equals(perm.getName())) { + throw new AccessControlException( + "Accessing the system environment is disallowed"); + } + // disallowing access to the spring.profiles.active property means that + // the BeanDefinitionReader won't be able to determine which profiles are + // active. We should see an INFO-level message in the console about this + // and as a result, any components marked with a non-default profile will + // be ignored. + if (("getenv."+AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME).equals(perm.getName())) { + throw new AccessControlException( + format("Accessing system environment variable [%s] is disallowed", + AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME)); + } + } + }; + System.setSecurityManager(securityManager); + + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(bf); + reader.register(C1.class); + assertThat(bf.containsBean("c1"), is(false)); + } + + @Component("c1") + @Profile("p1") + static class C1 { + } +} diff --git a/spring-core/src/main/java/org/springframework/core/env/ReadOnlySystemAttributesMap.java b/spring-core/src/main/java/org/springframework/core/env/ReadOnlySystemAttributesMap.java index df310c6002b..5ded8cbbc5e 100644 --- a/spring-core/src/main/java/org/springframework/core/env/ReadOnlySystemAttributesMap.java +++ b/spring-core/src/main/java/org/springframework/core/env/ReadOnlySystemAttributesMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2013 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,17 +17,21 @@ package org.springframework.core.env; import java.util.Collection; +import java.util.Collections; import java.util.Map; import java.util.Set; import org.springframework.util.Assert; /** - * Read-only {@code Map} implementation that is backed by system properties or environment - * variables. + * Read-only {@code Map} implementation that is backed by system + * properties or environment variables. * - *

Used by {@link AbstractApplicationContext} when a {@link SecurityManager} prohibits access to {@link - * System#getProperties()} or {@link System#getenv()}. + *

Used by {@link AbstractApplicationContext} when a {@link SecurityManager} prohibits + * access to {@link System#getProperties()} or {@link System#getenv()}. It is for this + * reason that the implementations of {@link #keySet()}, {@link #entrySet()}, and + * {@link #values()} always return empty even though {@link #get(Object)} may in fact + * return non-null if the current security manager allows access to individual keys. * * @author Arjen Poutsma * @author Chris Beams @@ -85,7 +89,7 @@ abstract class ReadOnlySystemAttributesMap implements Map { } public Set keySet() { - throw new UnsupportedOperationException(); + return Collections.emptySet(); } public void putAll(Map m) { @@ -93,11 +97,11 @@ abstract class ReadOnlySystemAttributesMap implements Map { } public Collection values() { - throw new UnsupportedOperationException(); + return Collections.emptySet(); } public Set> entrySet() { - throw new UnsupportedOperationException(); + return Collections.emptySet(); } } diff --git a/spring-core/src/main/java/org/springframework/core/env/SystemEnvironmentPropertySource.java b/spring-core/src/main/java/org/springframework/core/env/SystemEnvironmentPropertySource.java index 66eeb1e5bb0..09082aad5c2 100644 --- a/spring-core/src/main/java/org/springframework/core/env/SystemEnvironmentPropertySource.java +++ b/spring-core/src/main/java/org/springframework/core/env/SystemEnvironmentPropertySource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2013 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. @@ -76,7 +76,7 @@ public class SystemEnvironmentPropertySource extends MapPropertySource { */ @Override public boolean containsProperty(String name) { - return resolvePropertyName(name) != null; + return getProperty(name) != null; } /** @@ -102,8 +102,8 @@ public class SystemEnvironmentPropertySource extends MapPropertySource { /** * Check to see if this property source contains a property with the given name, or - * any underscore / uppercase variation thereof. Return the resolved name or - * {@code null} if none found. + * any underscore / uppercase variation thereof. Return the resolved name if one is + * found or otherwise the original name. Never returns {@code null}. */ private String resolvePropertyName(String name) { if (super.containsProperty(name)) { @@ -127,6 +127,6 @@ public class SystemEnvironmentPropertySource extends MapPropertySource { } } - return null; + return name; } } diff --git a/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java b/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java index 8cec8f813d5..131917c8ccf 100644 --- a/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java +++ b/spring-core/src/test/java/org/springframework/core/env/StandardEnvironmentTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -456,7 +456,7 @@ public class StandardEnvironmentTests { } @SuppressWarnings("unchecked") - private static Map getModifiableSystemEnvironment() { + public static Map getModifiableSystemEnvironment() { // for os x / linux Class[] classes = Collections.class.getDeclaredClasses(); Map env = System.getenv(); From 8625504711223c8cce42dc8706c53b064a52b0d3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 24 Jan 2013 16:45:06 +0100 Subject: [PATCH 139/143] Completed changelog entries for 3.2.1 --- src/dist/changelog.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/dist/changelog.txt b/src/dist/changelog.txt index 82ae86682b0..25b90985b9b 100644 --- a/src/dist/changelog.txt +++ b/src/dist/changelog.txt @@ -13,6 +13,7 @@ Changes in version 3.2.1 (2013-01-24) * LocalVariableTableParameterNameDiscoverer works for bridge methods as well (SPR-9429) * added constructor with Charset argument to EncodedResource (SPR-10096) * ResourcePropertyResource accepts EncodedResource for properties files with a specific encoding (SPR-10096) +* SystemEnvironmentPropertySource properly works with an active JVM SecurityManager (SPR-9970) * CachedIntrospectionResults.clearClassLoader(null) removes cached classes for the system class loader (SPR-9189) * DisposableBeanAdapter detects "shutdown" as a destroy method as well (for EHCache CacheManager setup; SPR-9713) * introduced NoUniqueBeanDefinitionException as a dedicated subclass of NoSuchBeanDefinitionException (SPR-10194) @@ -42,6 +43,11 @@ Changes in version 3.2.1 (2013-01-24) * FreeMarkerConfigurationFactory properly supports TemplateLoaders when recreating Configurations (SPR-9389) * SpringContextResourceAdapter implements equals/hashCode according to the JCA 1.5 contract (SPR-9162) * ContextLoader properly detects pre-refreshed WebApplicationContext (SPR-9996) +* added support for placeholders in @RequestMapping annotation value (SPR-9935) +* added support for specifying a message code as @ResponseStatus reason (SPR-6044) +* HttpEntityMethodProcessor supports HttpEntity/ResponseEntity subclasses as well (SPR-10207) +* Tiles 3 TilesConfigurer properly works in combination with "completeAutoload" (SPR-10195) +* Spring MVC Test framework supports HTTP OPTIONS method as well (SPR-10093) * MockHttpServletRequest's getParameter(Values) returns null for null parameter name (SPR-10192) * MockHttpServletResponse's getHeaderNames declares Collection instead of Set for Servlet 3.0 compatibility (SPR-9885) From a99a4ed9b5597ef437bfbaff809f4d7e4ae48a20 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 24 Jan 2013 09:44:25 -0800 Subject: [PATCH 140/143] Tweak gradle generated eclipse meta-data - Change output folders to /bin/main and /bin/test. This prevents 'gradle clean' from breaking eclipse. - Update copyright header for new files to '2002-2013' --- gradle/ide.gradle | 13 +++++++------ src/eclipse/org.eclipse.jdt.ui.prefs | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/gradle/ide.gradle b/gradle/ide.gradle index bb73f9f456f..3f73282254a 100644 --- a/gradle/ide.gradle +++ b/gradle/ide.gradle @@ -27,17 +27,17 @@ eclipse.classpath.file.whenMerged { classpath -> // Use separate main/test outputs (prevents WTP from packaging test classes) -eclipse.classpath.defaultOutputDir = file(project.name+"/eclipse/bin") +eclipse.classpath.defaultOutputDir = file(project.name+"/bin/eclipse") eclipse.classpath.file.beforeMerged { classpath -> classpath.entries.findAll{ it instanceof SourceFolder }.each { - if(it.output.startsWith("build/eclipse")) { + if(it.output.startsWith("bin/")) { it.output = null } } } eclipse.classpath.file.whenMerged { classpath -> classpath.entries.findAll{ it instanceof SourceFolder }.each { - it.output = "build/eclipse/" + it.path.split("/")[1] + it.output = "bin/" + it.path.split("/")[1] } } @@ -90,9 +90,10 @@ eclipse.project.file.withXml { node.remove(filteredResources) } def filterNode = node.appendNode("filteredResources").appendNode("filter") - filterNode.appendNode("name", "build") - filterNode.appendNode("type", "26") + filterNode.appendNode("id", "1359048889071") + filterNode.appendNode("name", "") + filterNode.appendNode("type", "30") def matcherNode = filterNode.appendNode("matcher") matcherNode.appendNode("id", "org.eclipse.ui.ide.multiFilter") - matcherNode.appendNode("arguments", "1.0-projectRelativePath-matches-false-true-build\\/((?!eclipse).)*") + matcherNode.appendNode("arguments", "1.0-projectRelativePath-matches-false-false-build") } diff --git a/src/eclipse/org.eclipse.jdt.ui.prefs b/src/eclipse/org.eclipse.jdt.ui.prefs index 47eb08906bb..4aa12d7db50 100644 --- a/src/eclipse/org.eclipse.jdt.ui.prefs +++ b/src/eclipse/org.eclipse.jdt.ui.prefs @@ -59,4 +59,4 @@ org.eclipse.jdt.ui.importorder=java;javax;org;com; org.eclipse.jdt.ui.javadoc=true org.eclipse.jdt.ui.ondemandthreshold=9999 org.eclipse.jdt.ui.staticondemandthreshold=9999 -org.eclipse.jdt.ui.text.custom_code_templates= +org.eclipse.jdt.ui.text.custom_code_templates= From 234cb84e832da30b6f53ccca4ef28043aacfcecc Mon Sep 17 00:00:00 2001 From: Spring Buildmaster Date: Thu, 24 Jan 2013 10:44:42 -0800 Subject: [PATCH 141/143] Release version 3.2.1.RELEASE --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 89015ba3ec8..e91e1373ca5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=3.2.1.BUILD-SNAPSHOT +version=3.2.1.RELEASE From 08ba5a0d4a579f29a67bcaeba0f23be610583500 Mon Sep 17 00:00:00 2001 From: Spring Buildmaster Date: Thu, 24 Jan 2013 20:49:04 +0100 Subject: [PATCH 142/143] Increment version to 3.2.2.BUILD-SNAPSHOT --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e91e1373ca5..e69ccb3080b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=3.2.1.RELEASE +version=3.2.2.BUILD-SNAPSHOT From 1065d82f089eeeace3795992a50e79e387841de6 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 25 Jan 2013 10:58:24 -0800 Subject: [PATCH 143/143] Remove eclipse project specific Javadoc settings Remove Javadoc settings from generated eclipse meta-data. Unfortunately eclipse provides too many false warnings due to the fact that @Link and @See tags in Spring often refer modules to which they cannot directly depend. --- src/eclipse/org.eclipse.jdt.core.prefs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/eclipse/org.eclipse.jdt.core.prefs b/src/eclipse/org.eclipse.jdt.core.prefs index 2286420ffe9..80a095b1a1a 100644 --- a/src/eclipse/org.eclipse.jdt.core.prefs +++ b/src/eclipse/org.eclipse.jdt.core.prefs @@ -23,7 +23,6 @@ org.eclipse.jdt.core.compiler.compliance=1.7 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.doc.comment.support=enabled org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.autoboxing=ignore @@ -47,25 +46,12 @@ org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore -org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning -org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=default org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore -org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore -org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled -org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public -org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag -org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore -org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled -org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled -org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore