Merge remote-tracking branch 'springsource/3.2.x' into cleanup-3.2.x

* springsource/3.2.x: (143 commits)
  Remove eclipse project specific Javadoc settings
  Increment version to 3.2.2.BUILD-SNAPSHOT
  Release version 3.2.1.RELEASE
  Tweak gradle generated eclipse meta-data
  Completed changelog entries for 3.2.1
  Avoid UnsupportedOperationEx. with active SecurityManager
  Made EncodedResource based variant public; consistently detect XML properties across all variants
  Added note on thread safety to TypeConverter and SimpleTypeConverter javadoc
  Change merge.into project dependencies to provided
  Final preparations for 3.2.1
  Polishing
  Removed pre-JDK-1.5 checks
  ResourcePropertyResource accepts EncodedResource for properties files with a specific encoding
  Updated resolvePath javadoc to reflect Environment-based placeholder resolution
  DisposableBeanAdapter detects "shutdown" as a destroy method as well (for EHCache CacheManager setup)
  Added further MySQL error code for DataIntegrityViolationException
  ThreadPoolExecutorFactoryBean exposes "createExecutor" method for custom ThreadPoolExecutor subclasses
  MBeanInfoAssembler impls expose actual method parameter names if possible
  Suppress serialization warning
  Allow nulls with multiple embedded value resolvers
  ...
This commit is contained in:
Phillip Webb 2013-01-25 11:06:58 -08:00
commit 36b5ba1871
200 changed files with 3984 additions and 1156 deletions

7
.gitignore vendored
View File

@ -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

View File

@ -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")
}
}
@ -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"
@ -60,7 +58,7 @@ configure(allprojects) { project ->
test {
systemProperty("java.awt.headless", "true")
systemProperty("testGroups", properties.get("testGroups"))
systemProperty("testGroups", project.properties.get("testGroups"))
}
repositories {
@ -235,7 +233,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 {
@ -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")
@ -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"
}
@ -640,10 +643,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", "**/*TestSuite.class"])
}
dependencies {
compile(project(":spring-core"))
optional(project(":spring-beans"))
@ -674,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")
@ -752,6 +767,7 @@ configure(rootProject) {
apply plugin: "docbook-reference"
apply plugin: "groovy"
apply plugin: "detect-split-packages"
apply from: "${gradleScriptDir}/jdiff.gradle"
reference {
@ -759,7 +775,11 @@ configure(rootProject) {
pdfFilename = "spring-framework-reference.pdf"
}
// don"t publish the default jar for the root project
detectSplitPackages {
projectsToScan -= project(":spring-instrument-tomcat")
}
// don't publish the default jar for the root project
configurations.archives.artifacts.clear()
dependencies { // for integration tests
@ -959,6 +979,7 @@ configure(rootProject) {
"set GRADLE_OPTS=$gradleBatOpts %GRADLE_OPTS%\nset DEFAULT_JVM_OPTS=")
}
}
}
/*

View File

@ -0,0 +1,158 @@
/*
* 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<Project> {
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<Project> 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<String, List<Project>> 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<Project, Set<String>> mapPackagesByProject() {
def packagesByProject = [:]
this.projectsToScan.each { Project p ->
def packages = new HashSet<String>()
p.sourceSets.main.java.srcDirs.each { File dir ->
findPackages(packages, dir, "")
}
if (!packages.isEmpty()) {
packagesByProject.put(p, packages)
}
}
return packagesByProject;
}
private Map<Project, Project> 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<String> 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)
}
}
}

View File

@ -0,0 +1 @@
implementation-class=org.springframework.build.gradle.DetectSplitPackagesPlugin

View File

@ -1 +1 @@
version=3.2.1.BUILD-SNAPSHOT
version=3.2.2.BUILD-SNAPSHOT

View File

@ -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,76 @@ 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+"/bin/eclipse")
eclipse.classpath.file.beforeMerged { classpath ->
classpath.entries.findAll{ it instanceof SourceFolder }.each {
if(it.output.startsWith("bin/")) {
it.output = null
}
}
}
eclipse.classpath.file.whenMerged { classpath ->
classpath.entries.findAll{ it instanceof SourceFolder }.each {
it.output = "bin/" + 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/')
outputs.upToDateWhen { false }
}
task eclipseWstComponent(type: Copy) {
from rootProject.files(
"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("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)
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("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-false-build")
}

View File

@ -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<String, Boolean> eligibleBeans = new ConcurrentHashMap<String, Boolean>(64);
private final Map<Class, Boolean> eligibleBeans = new ConcurrentHashMap<Class, Boolean>(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}.
* <p>Implements caching of {@code canApply} results per bean name.
* <p>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;
}

View File

@ -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)
@ -30,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 {
@ -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)

View File

@ -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.beans.factory.wiring.BeanConfigurerSupport;
* pointcut in subaspects.
*
* <p>Subaspects may also need a metadata resolution strategy, in the
* {@code BeanWiringInfoResolver} interface. The default implementation
* <code>BeanWiringInfoResolver</code> 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.
*

View File

@ -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.

View File

@ -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,8 +29,8 @@ import java.io.Serializable;
* <p>
* There are two cases that needs to be handled:
* <ol>
* <li>Normal object creation via the '{@code new}' operator: this is
* taken care of by advising {@code initialization()} join points.</li>
* <li>Normal object creation via the '<code>new</code>' operator: this is
* taken care of by advising <code>initialization()</code> join points.</li>
* <li>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
@ -41,21 +41,21 @@ import java.io.Serializable;
* introduced implementation). There are a few choices for the chosen method:
* <ul>
* <li>readObject(ObjectOutputStream): Java requires that the method must be
* {@code private}. Since aspects cannot introduce a private member,
* <code>private</p>. Since aspects cannot introduce a private member,
* while preserving its name, this option is ruled out.</li>
* <li>readResolve(): Java doesn't pose any restriction on an access specifier.
* <li>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
* <code>public</code>. 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 &#64;Configurable, and
* in any case asking to make that method {@code public} should not
* in any case asking to make that method <code>public</code> should not
* pose any undue burden.</li>
* </ul>
* 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.</li>
* implementation of <code>readResolve()</code>, if any, must be
* <code>public</code>) can be lifted as well if we were to use an
* experimental feature in AspectJ - the <code>hasmethod()</code> PCD.</li>
* </ol>
* <p>
@ -103,17 +103,17 @@ public abstract aspect AbstractInterfaceDrivenDependencyInjectionAspect extends
ConfigurableObject+ && Serializable+ implements ConfigurableDeserializationSupport;
/**
* A marker interface to which the {@code readResolve()} is introduced.
* A marker interface to which the <code>readResolve()</code> is introduced.
*/
static interface ConfigurableDeserializationSupport extends Serializable {
}
/**
* Introduce the {@code readResolve()} method so that we can advise its
* Introduce the <code>readResolve()</code> method so that we can advise its
* execution to configure the object.
*
* <p>Note if a method with the same signature already exists in a
* {@code Serializable} class of ConfigurableObject type,
* <code>Serializable</code> class of ConfigurableObject type,
* that implementation will take precedence (a good thing, since we are
* merely interested in an opportunity to detect deserialization.)
*/

View File

@ -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.
*
* <p>The bean name to look up will be taken from the
* {@code &#64;Configurable} annotation if specified, otherwise the
* <code>&#64;Configurable</code> annotation if specified, otherwise the
* default bean name to look up will be the FQN of the class being configured.
*
* @author Rod Johnson

View File

@ -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,7 +22,7 @@ package org.springframework.beans.factory.aspectj;
* the use of the &#64;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.
* For example, here is a subaspect that configures the <code>PricingStrategyClient</code> objects.
* <pre>
* aspect PricingStrategyDependencyInjectionAspect
* extends GenericInterfaceDrivenDependencyInjectionAspect<PricingStrategyClient> {
@ -41,7 +41,7 @@ package org.springframework.beans.factory.aspectj;
* @since 3.0.0
*/
public abstract aspect GenericInterfaceDrivenDependencyInjectionAspect<I> extends AbstractInterfaceDrivenDependencyInjectionAspect {
declare parents: I implements ConfigurableObject;
declare parents: I implements ConfigurableObject;
public pointcut inConfigurableBean() : within(I+);

View File

@ -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.

View File

@ -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.

View File

@ -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.
@ -106,7 +106,7 @@ public abstract aspect AbstractMethodMockingControl percflow(mockStaticsTestMeth
* 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
}

View File

@ -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,7 +18,7 @@ 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 <code>@Entity</code> classes, as used by Roo for finders.
*
* <p>Mocking will occur in the call stack of any method in a class (typically a test class)
* that is annotated with the @MockStaticEntityMethods annotation.

View File

@ -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.*(..));
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;
}
}
after() throwing(RuntimeException re): entityManagerCall() {
DataAccessException dex = EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(re);
if (dex != null) {
throw dex;
} else {
throw re;
}
}
}

View File

@ -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.

View File

@ -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 <code>transactionalMethodExecution()</code>
* pointcut using a strategy such as Java 5 annotations.
*
* <p>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);

View File

@ -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.

View File

@ -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<Class> 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();

View File

@ -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<Method>() {
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);

View File

@ -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.
*
* <p><b>Note:</b> Due to its reliance on {@link java.beans.PropertyEditor PropertyEditors},
* SimpleTypeConverter is <em>not</em> thread-safe. Use a separate instance for each thread.
*
* @author Juergen Hoeller
* @since 2.0
* @see BeanWrapperImpl

View File

@ -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.
*
* <p><b>Note:</b> Since TypeConverter implementations are typically based on
* {@link java.beans.PropertyEditor PropertyEditors} which aren't thread-safe,
* TypeConverters themselves are <em>not</em> to be considered as thread-safe either.
*
* @author Juergen Hoeller
* @since 2.0
* @see SimpleTypeConverter

View File

@ -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

View File

@ -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> T beanOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<T> type)
throws BeansException {
Map<String, T> 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> T beanOfTypeIncludingAncestors(
@ -360,12 +355,7 @@ public abstract class BeanFactoryUtils {
throws BeansException {
Map<String, T> 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> T beanOfType(ListableBeanFactory lbf, Class<T> type) throws BeansException {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
Map<String, T> 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> T beanOfType(
@ -425,11 +410,27 @@ public abstract class BeanFactoryUtils {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
Map<String, T> 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> T uniqueBean(Class<T> type, Map<String, T> 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);
}
}

View File

@ -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 <em>by name</em>
* that failed.
* Return the name of the missing bean, if it was a lookup <em>by name</em> that failed.
*/
public String getBeanName() {
return this.beanName;
}
/**
* Return the required type of the missing bean, if it was a lookup
* <em>by type</em> that failed.
* Return the required type of the missing bean, if it was a lookup <em>by type</em> 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;
}
}

View File

@ -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<String> 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;
}
}

View File

@ -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 {
* <p>In an EJB usage scenario this would normally be called from
* {@code ejbRemove()} and {@code ejbPassivate()}.
* <p>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();
}

View File

@ -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<Object>, 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) {

View File

@ -746,6 +746,9 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
public String resolveEmbeddedValue(String value) {
String result = value;
for (StringValueResolver resolver : this.embeddedValueResolvers) {
if (result == null) {
return null;
}
result = resolver.resolveStringValue(result);
}
return result;

View File

@ -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;
@ -89,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
@ -270,12 +272,28 @@ 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) {
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) {
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 +841,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 +912,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) {

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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<Class<?>> {
@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;
}

View File

@ -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;
@ -26,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;
@ -49,7 +56,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;
@ -93,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.
@ -102,11 +112,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() {
@ -1270,6 +1283,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);
@ -1279,6 +1298,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();
@ -1296,7 +1341,8 @@ public class DefaultListableBeanFactoryTests {
try {
lbf.getBean(TestBean.class);
fail("Should have thrown NoSuchBeanDefinitionException");
} catch (NoSuchBeanDefinitionException ex) {
}
catch (NoSuchBeanDefinitionException ex) {
// expected
}
}
@ -2221,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 { }

View File

@ -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<CacheManager>, 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<CacheManager>, 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);
}
}

View File

@ -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()) {

View File

@ -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<TemplateLoader> templateLoaders = new LinkedList<TemplateLoader>(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);
}

View File

@ -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();

View File

@ -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<? extends Cache> 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());

View File

@ -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<? extends Annotation> 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");

View File

@ -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);
}

View File

@ -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<String> getKeys() {
return null;
throw new UnsupportedOperationException("MessageSourceResourceBundle does not support enumerating its keys");
}
/**

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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<Date, ReadableInstant> {
public ReadableInstant convert(Date source) {
return new DateTime(source);
}
}
}

View File

@ -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).
* <p>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.
* <p>The default implementation returns an empty arry of {@code MBeanParameterInfo}.
* <p>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.

View File

@ -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;
}

View File

@ -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<ObjectName> registeredBeans = new LinkedHashSet<ObjectName>();
private final Set<ObjectName> registeredBeans = Collections.synchronizedSet(new LinkedHashSet<ObjectName>());
/**
* 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<ObjectName>(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);
}
/**

View File

@ -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.
* <p>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() + "]";

View File

@ -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.
* <p>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.
* <p>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.
* <p>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.
* <p>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.
* <p>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();
}
}
}
}

View File

@ -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
* <p>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<Runnable> 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.
* <p>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<Runnable> queue,
ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
return new ThreadPoolExecutor(corePoolSize, maxPoolSize,
keepAliveSeconds, TimeUnit.SECONDS, queue, threadFactory, rejectedExecutionHandler);
}
/**
* Create the BlockingQueue to use for the ThreadPoolExecutor.
* <p>A LinkedBlockingQueue instance will be created for a positive

View File

@ -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 redirects the actual scheduling
* of tasks to the {@link ContextRefreshedEvent} callback. Falls back to regular
* {@code ScheduledTaskRegistrar} behavior when not running within an ApplicationContext.
*
* @author Juergen Hoeller
* @since 3.2.1
*/
public class ContextLifecycleScheduledTaskRegistrar extends ScheduledTaskRegistrar
implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent> {
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();
}
}

View File

@ -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) {

View File

@ -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

View File

@ -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;
* <emphasis>completion</emphasis> time). To measure the interval between the
* scheduled <emphasis>start</emphasis> time of each execution instead, set the
* 'fixedRate' property to {@code true}.
* <p>
* Note that the TaskScheduler interface already defines methods for scheduling
*
* <p>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);
}
}

View File

@ -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<Validator> validators = new ArrayList<Validator>();
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<Validator> 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.
* <p>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());
}
}
}

View File

@ -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);

View File

@ -52,6 +52,39 @@
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="mode" default="proxy">
<xsd:annotation>
<xsd:documentation><![CDATA[
Should annotated beans be proxied using Spring's AOP framework,
or should they rather be weaved with an AspectJ async execution aspect?
AspectJ weaving requires spring-aspects.jar on the classpath,
as well as load-time weaving (or compile-time weaving) enabled.
Note: The weaving-based aspect requires the @Async annotation to be
defined on the concrete class. Annotations in interfaces will not work
in that case (they will rather only work with interface-based proxies)!
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="proxy"/>
<xsd:enumeration value="aspectj"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="proxy-target-class" type="xsd:boolean" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Are class-based (CGLIB) proxies to be created? By default, standard
Java interface-based proxies are created.
Note: Class-based proxies require the @Async annotation to be defined
on the concrete class. Annotations in interfaces will not work in
that case (they will rather only work with interface-based proxies)!
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>

View File

@ -52,6 +52,39 @@
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="mode" default="proxy">
<xsd:annotation>
<xsd:documentation><![CDATA[
Should annotated beans be proxied using Spring's AOP framework,
or should they rather be weaved with an AspectJ async execution aspect?
AspectJ weaving requires spring-aspects.jar on the classpath,
as well as load-time weaving (or compile-time weaving) enabled.
Note: The weaving-based aspect requires the @Async annotation to be
defined on the concrete class. Annotations in interfaces will not work
in that case (they will rather only work with interface-based proxies)!
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="proxy"/>
<xsd:enumeration value="aspectj"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="proxy-target-class" type="xsd:boolean" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Are class-based (CGLIB) proxies to be created? By default, standard
Java interface-based proxies are created.
Note: Class-based proxies require the @Async annotation to be defined
on the concrete class. Annotations in interfaces will not work in
that case (they will rather only work with interface-based proxies)!
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>

View File

@ -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")
)

View File

@ -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;
}
}
}

View File

@ -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<String, String> 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 {
}
}

View File

@ -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) {

View File

@ -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 {

View File

@ -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 {

View File

@ -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";

View File

@ -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

View File

@ -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<NameAddressValid, ValidPerson> {
@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<? extends Payload>[] payload() default {};
}
public static class InnerValidator implements ConstraintValidator<InnerValid, InnerBean> {
@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;
}
}
}

View File

@ -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;
}

View File

@ -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<Member, String[]> 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<Member, String[]> 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;
}

View File

@ -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
@ -46,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();
}
@ -54,4 +59,31 @@ public class AnnotationAwareOrderComparator extends OrderComparator {
return Ordered.LOWEST_PRECEDENCE;
}
/**
* Sort the given List with a default AnnotationAwareOrderComparator.
* <p>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.
* <p>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);
}
}
}

View File

@ -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.
* <p>Note: This method does <strong>not</strong> 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).
* <p>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
* &#064;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 <em>inherited</em>
* @see Class#isAnnotationPresent(Class)
* @see #isAnnotationDeclaredLocally(Class, Class)
*/

View File

@ -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,8 @@ import org.springframework.util.ObjectUtils;
* @author Keith Donald
* @author Andy Clement
* @author Juergen Hoeller
* @author Phillip Webb
* @author Sam Brannen
* @since 3.0
*/
public class TypeDescriptor {
@ -75,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.
* <p>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) {
@ -84,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.
* <p>Use this constructor when a source or target conversion point is a field.
* @param field the field
*/
public TypeDescriptor(Field field) {
@ -93,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.
* <p>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) {
@ -103,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.
* <p>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.
* <p>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
*/
@ -114,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&lt;String&gt; could be converted to a List&lt;EmailAddress&gt; 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.
* <p>Useful for converting to typed Collections.
* <p>For example, a {@code List<String>} could be converted to a
* {@code List<EmailAddress>} 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) {
@ -130,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&lt;String, String&gt; could be converted to a Map&lt;Id, EmailAddress&gt; by converting to a targetType built with this method:
* Create a new type descriptor from a {@link java.util.Map} type.
* <p>Useful for converting to typed Maps.
* <p>For example, a Map&lt;String, String&gt; could be converted to a Map&lt;Id, EmailAddress&gt; 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
@ -146,19 +157,44 @@ public class TypeDescriptor {
return new TypeDescriptor(mapType, keyTypeDescriptor, valueTypeDescriptor);
}
/**
* Create a new type descriptor as an array of the specified type.
* <p>For example to create a {@code Map<String,String>[]} 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.1
*/
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&lt;String&gt; and the nestingLevel is 1, the nested type descriptor will be String.class.
* If the methodParameter is a List<List<String>> and the nestingLevel is 2, the nested type descriptor will also be a String.class.
* If the methodParameter is a Map<Integer, String> and the nesting level is 1, the nested type descriptor will be String, derived from the map value.
* If the methodParameter is a List<Map<Integer, String>> 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&lt;?&gt;, the nested type descriptor returned will be <tt>null</tt>.
* <p>For example, if the methodParameter is a {@code List<String>} and the
* nesting level is 1, the nested type descriptor will be String.class.
* <p>If the methodParameter is a {@code List<List<String>>} and the nesting
* level is 2, the nested type descriptor will also be a String.class.
* <p>If the methodParameter is a {@code Map<Integer, String>} and the nesting
* level is 1, the nested type descriptor will be String, derived from the map value.
* <p>If the methodParameter is a {@code List<Map<Integer, String>>} and the
* nesting level is 2, the nested type descriptor will be String, derived from the map value.
* <p>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) {
@ -169,16 +205,23 @@ public class TypeDescriptor {
/**
* Creates a type descriptor for a nested type declared within the field.
* <p>For example, if the field is a {@code List&lt;String&gt;} and the nestingLevel is 1, the nested type descriptor will be {@code String.class}.
* If the field is a {@code List&lt;List&lt;String&gt;&gt;} and the nestingLevel is 2, the nested type descriptor will also be a {@code String.class}.
* If the field is a {@code Map&lt;Integer, String&gt;} and the nestingLevel is 1, the nested type descriptor will be String, derived from the map value.
* If the field is a {@code List&lt;Map&lt;Integer, String&gt;&gt;} 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&lt;?&gt;}, the nested type descriptor returned will be {@code null}.
* <p>For example, if the field is a {@code List<String>} and the nesting
* level is 1, the nested type descriptor will be {@code String.class}.
* <p>If the field is a {@code List<List<String>>} and the nesting level is
* 2, the nested type descriptor will also be a {@code String.class}.
* <p>If the field is a {@code Map<Integer, String>} and the nesting level
* is 1, the nested type descriptor will be String, derived from the map value.
* <p>If the field is a {@code List<Map<Integer, String>>} and the nesting
* level is 2, the nested type descriptor will be String, derived from the map value.
* <p>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);
@ -186,16 +229,24 @@ public class TypeDescriptor {
/**
* Creates a type descriptor for a nested type declared within the property.
* <p>For example, if the property is a {@code List&lt;String&gt;} and the nestingLevel is 1, the nested type descriptor will be {@code String.class}.
* If the property is a {@code List&lt;List&lt;String&gt;&gt;} and the nestingLevel is 2, the nested type descriptor will also be a {@code String.class}.
* If the property is a {@code Map&lt;Integer, String&gt;} and the nestingLevel is 1, the nested type descriptor will be String, derived from the map value.
* If the property is a {@code List&lt;Map&lt;Integer, String&gt;&gt;} 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&lt;?&gt;}, the nested type descriptor returned will be {@code null}.
* <p>For example, if the property is a {@code List<String>} and the nesting
* level is 1, the nested type descriptor will be {@code String.class}.
* <p>If the property is a {@code List<List<String>>} and the nesting level
* is 2, the nested type descriptor will also be a {@code String.class}.
* <p>If the property is a {@code Map<Integer, String>} and the nesting level
* is 1, the nested type descriptor will be String, derived from the map value.
* <p>If the property is a {@code List<Map<Integer, String>>} and the nesting
* level is 2, the nested type descriptor will be String, derived from the map value.
* <p>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);
@ -203,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.
* <p>Use this factory method to introspect a source object before asking the conversion system to convert it to some another type.
* <p>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
*/
@ -214,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.
* <p>Returns primitive types as-is.
* <p>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}
@ -235,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.
* <p>If the value is {@code null}, no narrowing is performed and this TypeDescriptor is returned unchanged.
* <p>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}
@ -255,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
@ -324,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.
* <p>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.
* <p>
* For arrays, collections, and maps, element and key/value types are checked if declared.
@ -382,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.
* <p>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&lt;java.lang.Number&lt; and the element argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer.
* If this describes a java.util.List&lt;?&gt; 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.
* <p>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
@ -417,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.
* <p>Narrows the {@link #getMapKeyTypeDescriptor() mapKeyType} property to the class of the provided map key.
* For example, if this describes a java.util.Map&lt;java.lang.Number, java.lang.String&lt; and the key argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer.
* If this describes a java.util.Map&lt;?, ?&gt; 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.
* <p>If this describes a java.util.Map&lt;?, ?&gt; and the key argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer as well.
* <p>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
@ -432,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.
* <p>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
*/
@ -443,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.
* <p>Narrows the {@link #getMapValueTypeDescriptor() mapValueType} property to the class of the provided map value.
* For example, if this describes a java.util.Map&lt;java.lang.String, java.lang.Number&lt; and the value argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer.
* If this describes a java.util.Map&lt;?, ?&gt; 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.
* <p>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

View File

@ -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);
}
}

View File

@ -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<String, String>} implementation that is backed by system properties or environment
* variables.
* Read-only {@code Map<String, String>} implementation that is backed by system
* properties or environment variables.
*
* <p>Used by {@link AbstractApplicationContext} when a {@link SecurityManager} prohibits access to {@link
* System#getProperties()} or {@link System#getenv()}.
* <p>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<String, String> {
}
public Set<String> keySet() {
throw new UnsupportedOperationException();
return Collections.emptySet();
}
public void putAll(Map<? extends String, ? extends String> m) {
@ -93,11 +97,11 @@ abstract class ReadOnlySystemAttributesMap implements Map<String, String> {
}
public Collection<String> values() {
throw new UnsupportedOperationException();
return Collections.emptySet();
}
public Set<Entry<String, String>> entrySet() {
throw new UnsupportedOperationException();
return Collections.emptySet();
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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) {

View File

@ -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();
}
}
}
}
}

View File

@ -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,11 +45,76 @@ import org.springframework.util.ResourceUtils;
*/
public abstract class PropertiesLoaderUtils {
private static final String XML_FILE_EXTENSION = ".xml";
/**
* Load properties from the given resource.
* 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(EncodedResource resource) throws IOException {
Properties props = new Properties();
fillProperties(props, resource);
return props;
}
/**
* 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 in case of I/O errors
*/
public static void fillProperties(Properties props, EncodedResource resource)
throws IOException {
fillProperties(props, resource, new DefaultPropertiesPersister());
}
/**
* 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();
}
}
}
/**
* 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();
@ -55,7 +123,7 @@ public abstract class PropertiesLoaderUtils {
}
/**
* Fill the given properties from the given resource.
* 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
@ -63,7 +131,13 @@ public abstract class PropertiesLoaderUtils {
public static void fillProperties(Properties props, Resource resource) throws IOException {
InputStream is = resource.getInputStream();
try {
props.load(is);
String filename = resource.getFilename();
if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
props.loadFromXML(is);
}
else {
props.load(is);
}
}
finally {
is.close();
@ -71,8 +145,8 @@ public abstract class PropertiesLoaderUtils {
}
/**
* Load all properties from the given class path resource,
* using the default class loader.
* Load all properties from the specified class path resource
* (in ISO-8859-1 encoding), using the default class loader.
* <p>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
@ -84,8 +158,8 @@ public abstract class PropertiesLoaderUtils {
}
/**
* Load all properties from the given class path resource,
* using the given class loader.
* Load all properties from the specified class path resource
* (in ISO-8859-1 encoding), using the given class loader.
* <p>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
@ -100,24 +174,26 @@ public abstract class PropertiesLoaderUtils {
if (clToUse == null) {
clToUse = ClassUtils.getDefaultClassLoader();
}
Properties properties = new Properties();
Properties props = new Properties();
Enumeration urls = clToUse.getResources(resourceName);
while (urls.hasMoreElements()) {
URL url = (URL) urls.nextElement();
InputStream is = null;
URLConnection con = url.openConnection();
ResourceUtils.useCachesIfNecessary(con);
InputStream is = con.getInputStream();
try {
URLConnection con = url.openConnection();
ResourceUtils.useCachesIfNecessary(con);
is = con.getInputStream();
properties.load(is);
}
finally {
if (is != null) {
is.close();
if (resourceName != null && resourceName.endsWith(XML_FILE_EXTENSION)) {
props.loadFromXML(is);
}
else {
props.load(is);
}
}
finally {
is.close();
}
}
return properties;
return props;
}
}

View File

@ -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,30 +17,51 @@
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;
/**
* 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
* @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)));
}
/**
@ -58,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));
}
/**
@ -79,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));
}
/**
@ -88,28 +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();
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) {

View File

@ -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<String, AntPathStringMatcher> stringMatcherCache =
new ConcurrentHashMap<String, AntPathStringMatcher>(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 <strong>not</strong> 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();

View File

@ -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;
*
* <p>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.
*
* <p>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).
*
* <p>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);
}
}

View File

@ -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<Object> 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<Object> 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 {
}
}

View File

@ -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));
}
}

View File

@ -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<String, char[]>() {
@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;

View File

@ -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> T getProperty(String key, Class<T> targetType) {
return null;
}
public <T> T getProperty(String key, Class<T> targetType, T defaultValue) {
return null;
}
public <T> Class<T> getPropertyAsClass(String key, Class<T> targetType) {
return null;
}
public String getRequiredProperty(String key) throws IllegalStateException {
return null;
}
public <T> T getRequiredProperty(String key, Class<T> 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;
}
}

View File

@ -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<String, String> getModifiableSystemEnvironment() {
public static Map<String, String> getModifiableSystemEnvironment() {
// for os x / linux
Class<?>[] classes = Collections.class.getDeclaredClasses();
Map<String, String> env = System.getenv();

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties version="1.0">
<entry key="foo">bar</entry>
</properties>

View File

@ -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);

View File

@ -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"));
}
}

View File

@ -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<String> methodNames = new LinkedList<String>();

View File

@ -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;

View File

@ -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);

View File

@ -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)) {

View File

@ -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,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;
@ -42,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 {
@ -284,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;
@ -292,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;
@ -300,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;
@ -310,13 +313,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 +327,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 +340,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 +351,19 @@ 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<Method>() {
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;
@ -367,6 +383,20 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return field;
}
}
// 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) {
return field;
}
}
for (Class<?> implementedInterface : clazz.getInterfaces()) {
Field field = findField(name, implementedInterface, mustBeStatic);
if (field != null) {
return field;
}
}
return null;
}

Some files were not shown because too many files have changed in this diff Show More