mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
68 Commits
main
...
v3.1.2.RELEASE
| Author | SHA1 | Date | |
|---|---|---|---|
| 49f728eae8 | |||
| 16da15eca1 | |||
| efabc6bf18 | |||
| 98b281ea45 | |||
| 00a69394e6 | |||
| 45f1438ef8 | |||
| e7b44e015a | |||
| 23fe1e2934 | |||
| 1f64b147b1 | |||
| 5e4f3b3108 | |||
| c87989477d | |||
| 3b4210fa6f | |||
| 79b86a7e3a | |||
| b488ae6009 | |||
| aeef8c87f6 | |||
| 556c6a79c5 | |||
| 0281158fcc | |||
| 100fa178dd | |||
| a387d13d5f | |||
| 841d9533db | |||
| 0d18deb6a4 | |||
| 42204e5098 | |||
| eed090e5f5 | |||
| 0320cc582b | |||
| a76c7ca299 | |||
| 8bab873107 | |||
| e8006bdf78 | |||
| 3f387eb9cf | |||
| 0769d53a6a | |||
| 6263c9abf9 | |||
| 1eb50297ad | |||
| 20f87ab98d | |||
| c25a1bcb60 | |||
| e8deba2915 | |||
| fa33c4b857 | |||
| 24c30eac49 | |||
| 1af7575bde | |||
| 8143d66e4c | |||
| 9e370208d0 | |||
| e66bc07c37 | |||
| c9a2dbdbfd | |||
| 2fa0e63e5a | |||
| 83ac44d1e4 | |||
| 0cf93711b9 | |||
| 8faa2e61ec | |||
| 71443efd90 | |||
| bc243b5aa7 | |||
| d36979bc80 | |||
| 5875695d99 | |||
| dcb44264ad | |||
| 9fcb578187 | |||
| 8cdfbfe864 | |||
| 9ca407d55e | |||
| 1380d053c4 | |||
| 6b76afd1ce | |||
| 2fe74a4ef0 | |||
| ee9b9d6a57 | |||
| bf8fa6e268 | |||
| c976fa329f | |||
| d22c8aca49 | |||
| 79d9f7a5f7 | |||
| 9d3cfcd8e6 | |||
| 7d72ab59ae | |||
| 40a6769309 | |||
| e30d6104f3 | |||
| 129b4b3136 | |||
| b32a365f14 | |||
| 79c9ca1a26 |
@@ -3,6 +3,38 @@ SPRING FRAMEWORK CHANGELOG
|
||||
http://www.springsource.org
|
||||
|
||||
|
||||
Changes in version 3.1.2 (2012-07-09)
|
||||
-------------------------------------
|
||||
|
||||
* removed INDEX.LIST entries from Spring jar META-INF dirs
|
||||
* fixed OSGi metadata for optional "javax.inject" package import
|
||||
* fixed AbstractResource's InputStream-based "contentLength()" implementation
|
||||
* fixed MutablePropertySources "get" implementation to correctly return null if not found
|
||||
* fixed @PropertySource placeholder resolution to apply to all specified values
|
||||
* JDBC parameter binding uses JDBC 3.0 ParameterMetaData (if available) for type determination
|
||||
* added "jtaTransactionManager" property to Hibernate 4 LocalSessionFactoryBean/Builder
|
||||
* EhCacheFactoryBean applies listeners and enabled/disabled flags to existing cache regions as well
|
||||
* added "repeatCount" bean property to Quartz SimpleTriggerFactoryBean
|
||||
* support executor qualification with @Async#value
|
||||
* ServletContextResource's getFile implementation falls back to getRealPath for non-existent files
|
||||
* fixed StandardServletMultipartResolver compatibility with Resin (only deleting actual file parts)
|
||||
* fixed RequestConditionHolder to return null when no match is found
|
||||
* fixed concurrency issue in AnnotationMethodHandlerExceptionResolver
|
||||
* fixed issue with parsing invalid Content-Type or Accept headers
|
||||
* added "defaultCharset" property to StringHttpMessageConverter
|
||||
* added Jackson 2 HttpMessageConverter and View types
|
||||
* translate IOException on EOF from Jackson to HttpMessageNotReadableException
|
||||
* fix content negotiation issue when sorting selected media types by quality value
|
||||
* raise RestClientException instead of IllegalArgumentException for unknown status codes
|
||||
* discover controllers based on type-level @RequestMapping in new @MVC support classes
|
||||
* fixed issue with suffix pattern in AnnotationMethodHandlerAdapter
|
||||
* merge rather than add URI vars in ExtendedServletRequestDataBinder
|
||||
* raise RestClientException for unknown HTTP status codes
|
||||
* fixed issue with encoded params in UriComponentsBuilder
|
||||
* XStreamMarshaller converts StreamException to Spring exception in case of unmarshalling an empty stream
|
||||
* corrected DispatcherPortlet behavior to not forward event exceptions to the render phase by default
|
||||
|
||||
|
||||
Changes in version 3.1.1 (2012-02-16)
|
||||
-------------------------------------
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
SPRING FRAMEWORK 3.1.1 (February 2012)
|
||||
--------------------------------------
|
||||
SPRING FRAMEWORK 3.1.2 (July 2012)
|
||||
----------------------------------
|
||||
http://www.springsource.org
|
||||
|
||||
1. INTRODUCTION
|
||||
|
||||
+8
-5
@@ -1,4 +1,4 @@
|
||||
version=3.1.1
|
||||
version=3.1.2
|
||||
|
||||
natural.name=spring-framework
|
||||
project.name=Spring Framework
|
||||
@@ -17,13 +17,16 @@ spring.osgi.range="${spring.osgi.range.nq}"
|
||||
aj.osgi.range="[1.6.8, 2.0.0)"
|
||||
|
||||
## For GA releases
|
||||
#release.type=release
|
||||
#build.stamp=RELEASE
|
||||
release.type=release
|
||||
build.stamp=RELEASE
|
||||
|
||||
## For milestone releases
|
||||
#release.type=milestone
|
||||
#build.stamp=M1
|
||||
|
||||
## For trunk development / ci builds
|
||||
release.type=integration
|
||||
build.stamp=BUILD-SNAPSHOT
|
||||
#release.type=integration
|
||||
#build.stamp=BUILD-SNAPSHOT
|
||||
|
||||
#bundlor
|
||||
fail.on.warnings=false
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aop</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.aop.interceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.core.task.support.TaskExecutorAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base class for asynchronous method execution aspects, such as
|
||||
* {@link org.springframework.scheduling.annotation.AnnotationAsyncExecutionInterceptor}
|
||||
* or {@link org.springframework.scheduling.aspectj.AnnotationAsyncExecutionAspect}.
|
||||
*
|
||||
* <p>Provides support for <i>executor qualification</i> on a method-by-method basis.
|
||||
* {@code AsyncExecutionAspectSupport} objects must be constructed with a default {@code
|
||||
* Executor}, but each individual method may further qualify a specific {@code Executor}
|
||||
* bean to be used when executing it, e.g. through an annotation attribute.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1.2
|
||||
*/
|
||||
public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
||||
|
||||
private final Map<Method, AsyncTaskExecutor> executors = new HashMap<Method, AsyncTaskExecutor>();
|
||||
|
||||
private Executor defaultExecutor;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link AsyncExecutionAspectSupport}, using the provided default
|
||||
* executor unless individual async methods indicate via qualifier that a more
|
||||
* specific executor should be used.
|
||||
* @param defaultExecutor the executor to use when executing asynchronous methods
|
||||
*/
|
||||
public AsyncExecutionAspectSupport(Executor defaultExecutor) {
|
||||
this.setExecutor(defaultExecutor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Supply the executor to be used when executing async methods.
|
||||
* @param defaultExecutor the {@code Executor} (typically a Spring {@code
|
||||
* AsyncTaskExecutor} or {@link java.util.concurrent.ExecutorService}) to delegate to
|
||||
* unless a more specific executor has been requested via a qualifier on the async
|
||||
* method, in which case the executor will be looked up at invocation time against the
|
||||
* enclosing bean factory.
|
||||
* @see #getExecutorQualifier
|
||||
* @see #setBeanFactory(BeanFactory)
|
||||
*/
|
||||
public void setExecutor(Executor defaultExecutor) {
|
||||
this.defaultExecutor = defaultExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link BeanFactory} to be used when looking up executors by qualifier.
|
||||
*/
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the qualifier or bean name of the executor to be used when executing the
|
||||
* given async method, typically specified in the form of an annotation attribute.
|
||||
* Returning an empty string or {@code null} indicates that no specific executor has
|
||||
* been specified and that the {@linkplain #setExecutor(Executor) default executor}
|
||||
* should be used.
|
||||
* @param method the method to inspect for executor qualifier metadata
|
||||
* @return the qualifier if specified, otherwise empty string or {@code null}
|
||||
* @see #determineAsyncExecutor(Method)
|
||||
*/
|
||||
protected abstract String getExecutorQualifier(Method method);
|
||||
|
||||
/**
|
||||
* Determine the specific executor to use when executing the given method.
|
||||
* @returns the executor to use (never {@code null})
|
||||
*/
|
||||
protected AsyncTaskExecutor determineAsyncExecutor(Method method) {
|
||||
if (!this.executors.containsKey(method)) {
|
||||
Executor executor = this.defaultExecutor;
|
||||
|
||||
String qualifier = getExecutorQualifier(method);
|
||||
if (StringUtils.hasLength(qualifier)) {
|
||||
Assert.notNull(this.beanFactory,
|
||||
"BeanFactory must be set on " + this.getClass().getSimpleName() +
|
||||
" to access qualified executor [" + qualifier + "]");
|
||||
executor = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
|
||||
this.beanFactory, Executor.class, qualifier);
|
||||
}
|
||||
|
||||
if (executor instanceof AsyncTaskExecutor) {
|
||||
this.executors.put(method, (AsyncTaskExecutor) executor);
|
||||
}
|
||||
else if (executor instanceof Executor) {
|
||||
this.executors.put(method, new TaskExecutorAdapter(executor));
|
||||
}
|
||||
}
|
||||
|
||||
return this.executors.get(method);
|
||||
}
|
||||
|
||||
}
|
||||
+52
-35
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.aop.interceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Future;
|
||||
@@ -25,14 +27,12 @@ import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.core.task.support.TaskExecutorAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* AOP Alliance <code>MethodInterceptor</code> that processes method invocations
|
||||
* asynchronously, using a given {@link org.springframework.core.task.AsyncTaskExecutor}.
|
||||
* Typically used with the {@link org.springframework.context.task.Async} annotation.
|
||||
* Typically used with the {@link org.springframework.scheduling.annotation.Async} annotation.
|
||||
*
|
||||
* <p>In terms of target method signatures, any parameter types are supported.
|
||||
* However, the return type is constrained to either <code>void</code> or
|
||||
@@ -44,50 +44,53 @@ import org.springframework.util.ReflectionUtils;
|
||||
* (like Spring's {@link org.springframework.scheduling.annotation.AsyncResult}
|
||||
* or EJB 3.1's <code>javax.ejb.AsyncResult</code>).
|
||||
*
|
||||
* <p>As of Spring 3.1.2 the {@code AnnotationAsyncExecutionInterceptor} subclass is
|
||||
* preferred for use due to its support for executor qualification in conjunction with
|
||||
* Spring's {@code @Async} annotation.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
* @see org.springframework.scheduling.annotation.Async
|
||||
* @see org.springframework.scheduling.annotation.AsyncAnnotationAdvisor
|
||||
* @see org.springframework.scheduling.annotation.AnnotationAsyncExecutionInterceptor
|
||||
*/
|
||||
public class AsyncExecutionInterceptor implements MethodInterceptor, Ordered {
|
||||
|
||||
private final AsyncTaskExecutor asyncExecutor;
|
||||
|
||||
public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport
|
||||
implements MethodInterceptor, Ordered {
|
||||
|
||||
/**
|
||||
* Create a new AsyncExecutionInterceptor.
|
||||
* @param asyncExecutor the Spring AsyncTaskExecutor to delegate to
|
||||
* Create a new {@code AsyncExecutionInterceptor}.
|
||||
* @param executor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor}
|
||||
* or {@link java.util.concurrent.ExecutorService}) to delegate to.
|
||||
*/
|
||||
public AsyncExecutionInterceptor(AsyncTaskExecutor asyncExecutor) {
|
||||
Assert.notNull(asyncExecutor, "TaskExecutor must not be null");
|
||||
this.asyncExecutor = asyncExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new AsyncExecutionInterceptor.
|
||||
* @param asyncExecutor the <code>java.util.concurrent</code> Executor
|
||||
* to delegate to (typically a {@link java.util.concurrent.ExecutorService}
|
||||
*/
|
||||
public AsyncExecutionInterceptor(Executor asyncExecutor) {
|
||||
this.asyncExecutor = new TaskExecutorAdapter(asyncExecutor);
|
||||
public AsyncExecutionInterceptor(Executor executor) {
|
||||
super(executor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Intercept the given method invocation, submit the actual calling of the method to
|
||||
* the correct task executor and return immediately to the caller.
|
||||
* @param invocation the method to intercept and make asynchronous
|
||||
* @return {@link Future} if the original method returns {@code Future}; {@code null}
|
||||
* otherwise.
|
||||
*/
|
||||
public Object invoke(final MethodInvocation invocation) throws Throwable {
|
||||
Future result = this.asyncExecutor.submit(new Callable<Object>() {
|
||||
public Object call() throws Exception {
|
||||
try {
|
||||
Object result = invocation.proceed();
|
||||
if (result instanceof Future) {
|
||||
return ((Future) result).get();
|
||||
Future<?> result = this.determineAsyncExecutor(invocation.getMethod()).submit(
|
||||
new Callable<Object>() {
|
||||
public Object call() throws Exception {
|
||||
try {
|
||||
Object result = invocation.proceed();
|
||||
if (result instanceof Future) {
|
||||
return ((Future<?>) result).get();
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
ReflectionUtils.rethrowException(ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
ReflectionUtils.rethrowException(ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
});
|
||||
if (Future.class.isAssignableFrom(invocation.getMethod().getReturnType())) {
|
||||
return result;
|
||||
}
|
||||
@@ -96,6 +99,20 @@ public class AsyncExecutionInterceptor implements MethodInterceptor, Ordered {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>This implementation is a no-op for compatibility in Spring 3.1.2. Subclasses may
|
||||
* override to provide support for extracting qualifier information, e.g. via an
|
||||
* annotation on the given method.
|
||||
* @return always {@code null}
|
||||
* @see #determineAsyncExecutor(Method)
|
||||
* @since 3.1.2
|
||||
*/
|
||||
@Override
|
||||
protected String getExecutorQualifier(Method method) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return Ordered.HIGHEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
@@ -206,6 +206,7 @@ public abstract class AopUtils {
|
||||
* @return whether the pointcut can apply on any method
|
||||
*/
|
||||
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
|
||||
Assert.notNull(pc, "Pointcut must not be null");
|
||||
if (!pc.getClassFilter().matches(targetClass)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-asm</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.apache.commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-1.1.1.jar" sourcepath="/IVY_CACHE/org.apache.commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-sources-1.1.1.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.aspectj/com.springsource.org.aspectj.weaver/1.6.8.RELEASE/com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar" sourcepath="/IVY_CACHE/org.aspectj/com.springsource.org.aspectj.weaver/1.6.8.RELEASE/com.springsource.org.aspectj.weaver-sources-1.6.8.RELEASE.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.junit/com.springsource.org.junit/4.9.0/com.springsource.org.junit-4.9.0.jar" sourcepath="/IVY_CACHE/org.junit/com.springsource.org.junit/4.9.0/com.springsource.org.junit-sources-4.9.0.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.hamcrest/com.springsource.org.hamcrest/1.1.0/com.springsource.org.hamcrest-1.1.0.jar" sourcepath="IVY_CACHE/org.hamcrest/com.springsource.org.hamcrest/1.1.0/com.springsource.org.hamcrest-sources-1.1.0.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/javax.persistence/com.springsource.javax.persistence/1.0.0/com.springsource.javax.persistence-1.0.0.jar" sourcepath="/IVY_CACHE/javax.persistence/com.springsource.javax.persistence/1.0.0/com.springsource.javax.persistence-sources-1.0.0.jar"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/org.springframework.orm"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/org.springframework.aop"/>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
<dependency org="org.springframework" name="org.springframework.context.support" rev="latest.integration" conf="optional, cache->compile"/>
|
||||
<dependency org="javax.persistence" name="com.springsource.javax.persistence" rev="1.0.0" conf="optional, orm->compile"/>
|
||||
<dependency org="org.junit" name="com.springsource.org.junit" rev="${junit.version}" conf="test->compile"/>
|
||||
<dependency org="org.hamcrest" name="com.springsource.org.hamcrest" rev="1.1.0" conf="test->compile"/>
|
||||
<dependency org="javax.mail" name="com.springsource.javax.mail" rev="1.4.0" conf="optional, test->compile"/>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aspects</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+10
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.aspectj;
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
@@ -25,14 +25,16 @@ import java.lang.annotation.Target;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Signals the current application context to apply dependency injection to non-managed
|
||||
* classes that are instantiated outside of the Spring bean factory (typically classes
|
||||
* annotated with the @{@link org.springframework.beans.factory.annotation.Configurable
|
||||
* Signals the current application context to apply dependency injection to
|
||||
* non-managed classes that are instantiated outside of the Spring bean factory
|
||||
* (typically classes annotated with the @
|
||||
* {@link org.springframework.beans.factory.annotation.Configurable
|
||||
* Configurable} annotation).
|
||||
*
|
||||
* <p>Similar to functionality found in Spring's {@code <context:spring-configured>} XML
|
||||
* element. Often used in conjunction with {@link
|
||||
* org.springframework.context.annotation.EnableLoadTimeWeaving @EnableLoadTimeWeaving}.
|
||||
* <p>Similar to functionality found in Spring's
|
||||
* {@code <context:spring-configured>} XML element. Often used in conjunction
|
||||
* with {@link org.springframework.context.annotation.EnableLoadTimeWeaving
|
||||
* @EnableLoadTimeWeaving}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -14,8 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.aspectj;
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
+28
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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.
|
||||
@@ -21,9 +21,9 @@ import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
|
||||
import org.springframework.aop.interceptor.AsyncExecutionAspectSupport;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.core.task.support.TaskExecutorAdapter;
|
||||
|
||||
/**
|
||||
* Abstract aspect that routes selected methods asynchronously.
|
||||
@@ -34,25 +34,33 @@ import org.springframework.core.task.support.TaskExecutorAdapter;
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 3.0.5
|
||||
*/
|
||||
public abstract aspect AbstractAsyncExecutionAspect {
|
||||
public abstract aspect AbstractAsyncExecutionAspect extends AsyncExecutionAspectSupport {
|
||||
|
||||
private AsyncTaskExecutor asyncExecutor;
|
||||
|
||||
public void setExecutor(Executor executor) {
|
||||
if (executor instanceof AsyncTaskExecutor) {
|
||||
this.asyncExecutor = (AsyncTaskExecutor) executor;
|
||||
}
|
||||
else {
|
||||
this.asyncExecutor = new TaskExecutorAdapter(executor);
|
||||
}
|
||||
/**
|
||||
* Create an {@code AnnotationAsyncExecutionAspect} with a {@code null} default
|
||||
* executor, which should instead be set via {@code #aspectOf} and
|
||||
* {@link #setExecutor(Executor)}.
|
||||
*/
|
||||
public AbstractAsyncExecutionAspect() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply around advice to methods matching the {@link #asyncMethod()} pointcut,
|
||||
* submit the actual calling of the method to the correct task executor and return
|
||||
* immediately to the caller.
|
||||
* @return {@link Future} if the original method returns {@code Future}; {@code null}
|
||||
* otherwise.
|
||||
*/
|
||||
Object around() : asyncMethod() {
|
||||
if (this.asyncExecutor == null) {
|
||||
MethodSignature methodSignature = (MethodSignature) thisJoinPointStaticPart.getSignature();
|
||||
AsyncTaskExecutor executor = determineAsyncExecutor(methodSignature.getMethod());
|
||||
if (executor == null) {
|
||||
return proceed();
|
||||
}
|
||||
}
|
||||
Callable<Object> callable = new Callable<Object>() {
|
||||
public Object call() throws Exception {
|
||||
Object result = proceed();
|
||||
@@ -61,8 +69,8 @@ public abstract aspect AbstractAsyncExecutionAspect {
|
||||
}
|
||||
return null;
|
||||
}};
|
||||
Future<?> result = this.asyncExecutor.submit(callable);
|
||||
if (Future.class.isAssignableFrom(((MethodSignature) thisJoinPointStaticPart.getSignature()).getReturnType())) {
|
||||
Future<?> result = executor.submit(callable);
|
||||
if (Future.class.isAssignableFrom(methodSignature.getReturnType())) {
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
@@ -70,6 +78,9 @@ public abstract aspect AbstractAsyncExecutionAspect {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of joinpoints at which async advice should be applied.
|
||||
*/
|
||||
public abstract pointcut asyncMethod();
|
||||
|
||||
}
|
||||
|
||||
+44
-16
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
package org.springframework.scheduling.aspectj;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
|
||||
/**
|
||||
@@ -24,31 +28,55 @@ import org.springframework.scheduling.annotation.Async;
|
||||
*
|
||||
* <p>This aspect routes methods marked with the {@link Async} annotation
|
||||
* as well as methods in classes marked with the same. Any method expected
|
||||
* to be routed asynchronously must return either void, {@link Future},
|
||||
* or a subtype of {@link Future}. This aspect, therefore, will produce
|
||||
* a compile-time error for methods that violate this constraint on the return type.
|
||||
* If, however, a class marked with <code>@Async</code> contains a method that
|
||||
* violates this constraint, it produces only a warning.
|
||||
*
|
||||
* to be routed asynchronously must return either {@code void}, {@link Future},
|
||||
* or a subtype of {@link Future}. This aspect, therefore, will produce
|
||||
* a compile-time error for methods that violate this constraint on the return type.
|
||||
* If, however, a class marked with {@code @Async} contains a method that violates this
|
||||
* constraint, it produces only a warning.
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
* @author Chris Beams
|
||||
* @since 3.0.5
|
||||
*/
|
||||
public aspect AnnotationAsyncExecutionAspect extends AbstractAsyncExecutionAspect {
|
||||
|
||||
private pointcut asyncMarkedMethod()
|
||||
private pointcut asyncMarkedMethod()
|
||||
: execution(@Async (void || Future+) *(..));
|
||||
|
||||
private pointcut asyncTypeMarkedMethod()
|
||||
private pointcut asyncTypeMarkedMethod()
|
||||
: execution((void || Future+) (@Async *).*(..));
|
||||
|
||||
|
||||
public pointcut asyncMethod() : asyncMarkedMethod() || asyncTypeMarkedMethod();
|
||||
|
||||
declare error:
|
||||
execution(@Async !(void||Future) *(..)):
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>This implementation inspects the given method and its declaring class for the
|
||||
* {@code @Async} annotation, returning the qualifier value expressed by
|
||||
* {@link Async#value()}. If {@code @Async} is specified at both the method and class level, the
|
||||
* method's {@code #value} takes precedence (even if empty string, indicating that
|
||||
* the default executor should be used preferentially).
|
||||
* @return the qualifier if specified, otherwise empty string indicating that the
|
||||
* {@linkplain #setExecutor(Executor) default executor} should be used
|
||||
* @see #determineAsyncExecutor(Method)
|
||||
*/
|
||||
@Override
|
||||
protected String getExecutorQualifier(Method method) {
|
||||
// maintainer's note: changes made here should also be made in
|
||||
// AnnotationAsyncExecutionInterceptor#getExecutorQualifier
|
||||
Async async = AnnotationUtils.findAnnotation(method, Async.class);
|
||||
if (async == null) {
|
||||
async = AnnotationUtils.findAnnotation(method.getDeclaringClass(), Async.class);
|
||||
}
|
||||
return async == null ? null : async.value();
|
||||
}
|
||||
|
||||
declare error:
|
||||
execution(@Async !(void||Future) *(..)):
|
||||
"Only methods that return void or Future may have an @Async annotation";
|
||||
|
||||
declare warning:
|
||||
execution(!(void||Future) (@Async *).*(..)):
|
||||
"Methods in a class marked with @Async that do not return void or Future will be routed synchronously";
|
||||
declare warning:
|
||||
execution(!(void||Future) (@Async *).*(..)):
|
||||
"Methods in a class marked with @Async that do not return void or Future will " +
|
||||
"be routed synchronously";
|
||||
|
||||
}
|
||||
|
||||
+5
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -14,16 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.aspectj;
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import org.springframework.beans.factory.aspectj.AbstractBeanConfigurerTests;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportResource;
|
||||
|
||||
/**
|
||||
* Tests that @EnableSpringConfigured properly registers an
|
||||
* {@link AnnotationBeanConfigurerAspect}, just as does {@code <context:spring-configured>}
|
||||
* {@link org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect}, just
|
||||
* as does {@code <context:spring-configured>}
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
+70
-30
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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.
|
||||
@@ -20,31 +20,38 @@ import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import static junit.framework.Assert.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.scheduling.annotation.AsyncResult;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AnnotationAsyncExecutionAspect}.
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
*/
|
||||
public class AnnotationAsyncExecutionAspectTests {
|
||||
|
||||
private static final long WAIT_TIME = 1000; //milli seconds
|
||||
private static final long WAIT_TIME = 1000; //milliseconds
|
||||
|
||||
private CountingExecutor executor;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
executor = new CountingExecutor();
|
||||
AnnotationAsyncExecutionAspect.aspectOf().setExecutor(executor);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void asyncMethodGetsRoutedAsynchronously() {
|
||||
ClassWithoutAsyncAnnotation obj = new ClassWithoutAsyncAnnotation();
|
||||
@@ -54,7 +61,7 @@ public class AnnotationAsyncExecutionAspectTests {
|
||||
assertEquals(1, executor.submitStartCounter);
|
||||
assertEquals(1, executor.submitCompleteCounter);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void asyncMethodReturningFutureGetsRoutedAsynchronouslyAndReturnsAFuture() throws InterruptedException, ExecutionException {
|
||||
ClassWithoutAsyncAnnotation obj = new ClassWithoutAsyncAnnotation();
|
||||
@@ -73,8 +80,8 @@ public class AnnotationAsyncExecutionAspectTests {
|
||||
assertEquals(1, obj.counter);
|
||||
assertEquals(0, executor.submitStartCounter);
|
||||
assertEquals(0, executor.submitCompleteCounter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void voidMethodInAsyncClassGetsRoutedAsynchronously() {
|
||||
ClassWithAsyncAnnotation obj = new ClassWithAsyncAnnotation();
|
||||
@@ -102,13 +109,30 @@ public class AnnotationAsyncExecutionAspectTests {
|
||||
assertEquals(5, returnValue);
|
||||
assertEquals(0, executor.submitStartCounter);
|
||||
assertEquals(0, executor.submitCompleteCounter);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void qualifiedAsyncMethodsAreRoutedToCorrectExecutor() throws InterruptedException, ExecutionException {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("e1", new RootBeanDefinition(ThreadPoolTaskExecutor.class));
|
||||
AnnotationAsyncExecutionAspect.aspectOf().setBeanFactory(beanFactory);
|
||||
|
||||
ClassWithQualifiedAsyncMethods obj = new ClassWithQualifiedAsyncMethods();
|
||||
|
||||
Future<Thread> defaultThread = obj.defaultWork();
|
||||
assertThat(defaultThread.get(), not(Thread.currentThread()));
|
||||
assertThat(defaultThread.get().getName(), not(startsWith("e1-")));
|
||||
|
||||
Future<Thread> e1Thread = obj.e1Work();
|
||||
assertThat(e1Thread.get().getName(), startsWith("e1-"));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class CountingExecutor extends SimpleAsyncTaskExecutor {
|
||||
int submitStartCounter;
|
||||
int submitCompleteCounter;
|
||||
|
||||
|
||||
@Override
|
||||
public <T> Future<T> submit(Callable<T> task) {
|
||||
submitStartCounter++;
|
||||
@@ -119,52 +143,56 @@ public class AnnotationAsyncExecutionAspectTests {
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
|
||||
public synchronized void waitForCompletion() {
|
||||
try {
|
||||
wait(WAIT_TIME);
|
||||
} catch (InterruptedException e) {
|
||||
Assert.fail("Didn't finish the async job in " + WAIT_TIME + " milliseconds");
|
||||
fail("Didn't finish the async job in " + WAIT_TIME + " milliseconds");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static class ClassWithoutAsyncAnnotation {
|
||||
int counter;
|
||||
|
||||
|
||||
@Async public void incrementAsync() {
|
||||
counter++;
|
||||
}
|
||||
|
||||
|
||||
public void increment() {
|
||||
counter++;
|
||||
}
|
||||
|
||||
|
||||
@Async public Future<Integer> incrementReturningAFuture() {
|
||||
counter++;
|
||||
return new AsyncResult<Integer>(5);
|
||||
}
|
||||
|
||||
// It should be an error to attach @Async to a method that returns a non-void
|
||||
// or non-Future.
|
||||
// We need to keep this commented out, otherwise there will be a compile-time error.
|
||||
// Please uncomment and re-comment this periodically to check that the compiler
|
||||
// produces an error message due to the 'declare error' statement
|
||||
// in AnnotationAsyncExecutionAspect
|
||||
|
||||
/**
|
||||
* It should raise an error to attach @Async to a method that returns a non-void
|
||||
* or non-Future. This method must remain commented-out, otherwise there will be a
|
||||
* compile-time error. Uncomment to manually verify that the compiler produces an
|
||||
* error message due to the 'declare error' statement in
|
||||
* {@link AnnotationAsyncExecutionAspect}.
|
||||
*/
|
||||
// @Async public int getInt() {
|
||||
// return 0;
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Async
|
||||
static class ClassWithAsyncAnnotation {
|
||||
int counter;
|
||||
|
||||
|
||||
public void increment() {
|
||||
counter++;
|
||||
}
|
||||
|
||||
// Manually check that there is a warning from the 'declare warning' statement in AnnotationAsynchExecutionAspect
|
||||
|
||||
// Manually check that there is a warning from the 'declare warning' statement in
|
||||
// AnnotationAsyncExecutionAspect
|
||||
public int return5() {
|
||||
return 5;
|
||||
}
|
||||
@@ -175,4 +203,16 @@ public class AnnotationAsyncExecutionAspectTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class ClassWithQualifiedAsyncMethods {
|
||||
@Async
|
||||
public Future<Thread> defaultWork() {
|
||||
return new AsyncResult<Thread>(Thread.currentThread());
|
||||
}
|
||||
|
||||
@Async("e1")
|
||||
public Future<Thread> e1Work() {
|
||||
return new AsyncResult<Thread>(Thread.currentThread());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.beans.factory;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -37,6 +38,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 04.07.2003
|
||||
*/
|
||||
public abstract class BeanFactoryUtils {
|
||||
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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.beans.factory.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Convenience methods performing bean lookups related to annotations, for example
|
||||
* Spring's {@link Qualifier @Qualifier} annotation.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1.2
|
||||
* @see BeanFactoryUtils
|
||||
*/
|
||||
public class BeanFactoryAnnotationUtils {
|
||||
|
||||
/**
|
||||
* Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a
|
||||
* qualifier (e.g. via {@code <qualifier>} or {@code @Qualifier}) matching the given
|
||||
* qualifier, or having a bean name matching the given qualifier.
|
||||
* @param bf the BeanFactory to get the target bean from
|
||||
* @param beanType the type of bean to retrieve
|
||||
* @param qualifier the qualifier for selecting between multiple bean matches
|
||||
* @return the matching bean of type {@code T} (never {@code null})
|
||||
* @throws IllegalStateException if no matching bean of type {@code T} found
|
||||
*/
|
||||
public static <T> T qualifiedBeanOfType(BeanFactory beanFactory, Class<T> beanType, String qualifier) {
|
||||
if (beanFactory instanceof ConfigurableListableBeanFactory) {
|
||||
// Full qualifier matching supported.
|
||||
return qualifiedBeanOfType((ConfigurableListableBeanFactory) beanFactory, beanType, qualifier);
|
||||
}
|
||||
else if (beanFactory.containsBean(qualifier)) {
|
||||
// Fallback: target bean at least found by bean name.
|
||||
return beanFactory.getBean(qualifier, beanType);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("No matching " + beanType.getSimpleName() +
|
||||
" bean found for bean name '" + qualifier +
|
||||
"'! (Note: Qualifier matching not supported because given " +
|
||||
"BeanFactory does not implement ConfigurableListableBeanFactory.)");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a
|
||||
* qualifier (e.g. {@code <qualifier>} or {@code @Qualifier}) matching the given
|
||||
* qualifier
|
||||
* @param bf the BeanFactory to get the target bean from
|
||||
* @param beanType the type of bean to retrieve
|
||||
* @param qualifier the qualifier for selecting between multiple bean matches
|
||||
* @return the matching bean of type {@code T} (never {@code null})
|
||||
* @throws IllegalStateException if no matching bean of type {@code T} found
|
||||
*/
|
||||
private static <T> T qualifiedBeanOfType(ConfigurableListableBeanFactory bf, Class<T> beanType, String qualifier) {
|
||||
Map<String, T> candidateBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, beanType);
|
||||
T matchingBean = null;
|
||||
for (String beanName : candidateBeans.keySet()) {
|
||||
if (isQualifierMatch(qualifier, beanName, bf)) {
|
||||
if (matchingBean != null) {
|
||||
throw new IllegalStateException("No unique " + beanType.getSimpleName() +
|
||||
" bean found for qualifier '" + qualifier + "'");
|
||||
}
|
||||
matchingBean = candidateBeans.get(beanName);
|
||||
}
|
||||
}
|
||||
if (matchingBean != null) {
|
||||
return matchingBean;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("No matching " + beanType.getSimpleName() +
|
||||
" bean found for qualifier '" + qualifier + "' - neither qualifier " +
|
||||
"match nor bean name match!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the named bean declares a qualifier of the given name.
|
||||
* @param qualifier the qualifier to match
|
||||
* @param beanName the name of the candidate bean
|
||||
* @param bf the {@code BeanFactory} from which to retrieve the named bean
|
||||
* @return {@code true} if either the bean definition (in the XML case)
|
||||
* or the bean's factory method (in the {@code @Bean} case) defines a matching
|
||||
* qualifier value (through {@code <qualifier>} or {@code @Qualifier})
|
||||
*/
|
||||
private static boolean isQualifierMatch(String qualifier, String beanName, ConfigurableListableBeanFactory bf) {
|
||||
if (bf.containsBean(beanName)) {
|
||||
try {
|
||||
BeanDefinition bd = bf.getMergedBeanDefinition(beanName);
|
||||
if (bd instanceof AbstractBeanDefinition) {
|
||||
AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
|
||||
AutowireCandidateQualifier candidate = abd.getQualifier(Qualifier.class.getName());
|
||||
if ((candidate != null && qualifier.equals(candidate.getAttribute(AutowireCandidateQualifier.VALUE_KEY))) ||
|
||||
qualifier.equals(beanName) || ObjectUtils.containsElement(bf.getAliases(beanName), qualifier)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (bd instanceof RootBeanDefinition) {
|
||||
Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod();
|
||||
if (factoryMethod != null) {
|
||||
Qualifier targetAnnotation = factoryMethod.getAnnotation(Qualifier.class);
|
||||
if (targetAnnotation != null && qualifier.equals(targetAnnotation.value())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// ignore - can't compare qualifiers for a manually registered singleton object
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
+51
-20
@@ -21,13 +21,14 @@ import java.io.NotSerializableException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -38,6 +39,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
@@ -90,6 +92,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @author Costin Leau
|
||||
* @author Chris Beams
|
||||
* @since 16 April 2001
|
||||
* @see StaticListableBeanFactory
|
||||
* @see PropertiesBeanDefinitionReader
|
||||
@@ -98,7 +101,7 @@ import org.springframework.util.StringUtils;
|
||||
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
|
||||
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
|
||||
|
||||
private static Class javaxInjectProviderClass = null;
|
||||
private static Class<?> javaxInjectProviderClass = null;
|
||||
|
||||
static {
|
||||
ClassLoader cl = DefaultListableBeanFactory.class.getClassLoader();
|
||||
@@ -128,11 +131,17 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver();
|
||||
|
||||
/** Map from dependency type to corresponding autowired value */
|
||||
private final Map<Class, Object> resolvableDependencies = new HashMap<Class, Object>();
|
||||
private final Map<Class<?>, Object> resolvableDependencies = new HashMap<Class<?>, Object>();
|
||||
|
||||
/** Map of bean definition objects, keyed by bean name */
|
||||
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
|
||||
|
||||
/** Map of singleton bean names keyed by bean class */
|
||||
private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>();
|
||||
|
||||
/** Map of non-singleton bean names keyed by bean class */
|
||||
private final Map<Class<?>, String[]> nonSingletonBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>();
|
||||
|
||||
/** List of bean definition names, in registration order */
|
||||
private final List<String> beanDefinitionNames = new ArrayList<String>();
|
||||
|
||||
@@ -294,11 +303,26 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
}
|
||||
|
||||
public String[] getBeanNamesForType(Class type) {
|
||||
public String[] getBeanNamesForType(Class<?> type) {
|
||||
return getBeanNamesForType(type, true, true);
|
||||
}
|
||||
|
||||
public String[] getBeanNamesForType(Class type, boolean includeNonSingletons, boolean allowEagerInit) {
|
||||
public String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
|
||||
if (type == null || !allowEagerInit) {
|
||||
return this.doGetBeanNamesForType(type, includeNonSingletons, allowEagerInit);
|
||||
}
|
||||
Map<Class<?>, String[]> cache = includeNonSingletons ?
|
||||
this.nonSingletonBeanNamesByType : this.singletonBeanNamesByType;
|
||||
String[] resolvedBeanNames = cache.get(type);
|
||||
if (resolvedBeanNames != null) {
|
||||
return resolvedBeanNames;
|
||||
}
|
||||
resolvedBeanNames = this.doGetBeanNamesForType(type, includeNonSingletons, allowEagerInit);
|
||||
cache.put(type, resolvedBeanNames);
|
||||
return resolvedBeanNames;
|
||||
}
|
||||
|
||||
private String[] doGetBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
|
||||
// Check all bean definitions.
|
||||
@@ -441,7 +465,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
*/
|
||||
public <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType) {
|
||||
A ann = null;
|
||||
Class beanType = getType(beanName);
|
||||
Class<?> beanType = getType(beanName);
|
||||
if (beanType != null) {
|
||||
ann = AnnotationUtils.findAnnotation(beanType, annotationType);
|
||||
}
|
||||
@@ -564,18 +588,18 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
|
||||
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
|
||||
if (isFactoryBean(beanName)) {
|
||||
final FactoryBean factory = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);
|
||||
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
|
||||
boolean isEagerInit;
|
||||
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
|
||||
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
|
||||
public Boolean run() {
|
||||
return ((SmartFactoryBean) factory).isEagerInit();
|
||||
return ((SmartFactoryBean<?>) factory).isEagerInit();
|
||||
}
|
||||
}, getAccessControlContext());
|
||||
}
|
||||
else {
|
||||
isEagerInit = (factory instanceof SmartFactoryBean &&
|
||||
((SmartFactoryBean) factory).isEagerInit());
|
||||
((SmartFactoryBean<?>) factory).isEagerInit());
|
||||
}
|
||||
if (isEagerInit) {
|
||||
getBean(beanName);
|
||||
@@ -669,6 +693,10 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
destroySingleton(beanName);
|
||||
}
|
||||
|
||||
// Remove any assumptions about by-type mappings
|
||||
this.singletonBeanNamesByType.clear();
|
||||
this.nonSingletonBeanNamesByType.clear();
|
||||
|
||||
// Reset all bean definitions that have the given bean as parent (recursively).
|
||||
for (String bdName : this.beanDefinitionNames) {
|
||||
if (!beanName.equals(bdName)) {
|
||||
@@ -723,7 +751,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
|
||||
if (type.isArray()) {
|
||||
Class componentType = type.getComponentType();
|
||||
Class<?> componentType = type.getComponentType();
|
||||
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType, descriptor);
|
||||
if (matchingBeans.isEmpty()) {
|
||||
if (descriptor.isRequired()) {
|
||||
@@ -738,7 +766,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
return converter.convertIfNecessary(matchingBeans.values(), type);
|
||||
}
|
||||
else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
|
||||
Class elementType = descriptor.getCollectionType();
|
||||
Class<?> elementType = descriptor.getCollectionType();
|
||||
if (elementType == null) {
|
||||
if (descriptor.isRequired()) {
|
||||
throw new FatalBeanException("No element type declared for collection [" + type.getName() + "]");
|
||||
@@ -759,7 +787,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
return converter.convertIfNecessary(matchingBeans.values(), type);
|
||||
}
|
||||
else if (Map.class.isAssignableFrom(type) && type.isInterface()) {
|
||||
Class keyType = descriptor.getMapKeyType();
|
||||
Class<?> keyType = descriptor.getMapKeyType();
|
||||
if (keyType == null || !String.class.isAssignableFrom(keyType)) {
|
||||
if (descriptor.isRequired()) {
|
||||
throw new FatalBeanException("Key type [" + keyType + "] of map [" + type.getName() +
|
||||
@@ -767,7 +795,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Class valueType = descriptor.getMapValueType();
|
||||
Class<?> valueType = descriptor.getMapValueType();
|
||||
if (valueType == null) {
|
||||
if (descriptor.isRequired()) {
|
||||
throw new FatalBeanException("No value type declared for map [" + type.getName() + "]");
|
||||
@@ -828,12 +856,12 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
* @see #autowireConstructor
|
||||
*/
|
||||
protected Map<String, Object> findAutowireCandidates(
|
||||
String beanName, Class requiredType, DependencyDescriptor descriptor) {
|
||||
String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
|
||||
|
||||
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
|
||||
this, requiredType, true, descriptor.isEager());
|
||||
Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length);
|
||||
for (Class autowiringType : this.resolvableDependencies.keySet()) {
|
||||
for (Class<?> autowiringType : this.resolvableDependencies.keySet()) {
|
||||
if (autowiringType.isAssignableFrom(requiredType)) {
|
||||
Object autowiringValue = this.resolvableDependencies.get(autowiringType);
|
||||
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
|
||||
@@ -918,7 +946,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
* Raise a NoSuchBeanDefinitionException for an unresolvable dependency.
|
||||
*/
|
||||
private void raiseNoSuchBeanDefinitionException(
|
||||
Class type, String dependencyDescription, DependencyDescriptor descriptor)
|
||||
Class<?> type, String dependencyDescription, DependencyDescriptor descriptor)
|
||||
throws NoSuchBeanDefinitionException {
|
||||
|
||||
throw new NoSuchBeanDefinitionException(type, dependencyDescription,
|
||||
@@ -967,6 +995,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
* Minimal id reference to the factory.
|
||||
* Resolved to the actual factory instance on deserialization.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static class SerializedBeanFactoryReference implements Serializable {
|
||||
|
||||
private final String id;
|
||||
@@ -976,7 +1005,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
|
||||
private Object readResolve() {
|
||||
Reference ref = serializableFactories.get(this.id);
|
||||
Reference<?> ref = serializableFactories.get(this.id);
|
||||
if (ref == null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot deserialize BeanFactory with id " + this.id + ": no factory registered for this id");
|
||||
@@ -994,7 +1023,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
/**
|
||||
* Serializable ObjectFactory for lazy resolution of a dependency.
|
||||
*/
|
||||
private class DependencyObjectFactory implements ObjectFactory, Serializable {
|
||||
@SuppressWarnings("serial")
|
||||
private class DependencyObjectFactory implements ObjectFactory<Object>, Serializable {
|
||||
|
||||
private final DependencyDescriptor descriptor;
|
||||
|
||||
@@ -1015,7 +1045,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
/**
|
||||
* Serializable ObjectFactory for lazy resolution of a dependency.
|
||||
*/
|
||||
private class DependencyProvider extends DependencyObjectFactory implements Provider {
|
||||
@SuppressWarnings("serial")
|
||||
private class DependencyProvider extends DependencyObjectFactory implements Provider<Object> {
|
||||
|
||||
public DependencyProvider(DependencyDescriptor descriptor, String beanName) {
|
||||
super(descriptor, beanName);
|
||||
|
||||
+41
-15
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,26 +16,18 @@
|
||||
|
||||
package org.springframework.beans.factory;
|
||||
|
||||
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.assertNotSame;
|
||||
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 static org.junit.Assert.fail;
|
||||
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.Principal;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParseException;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -48,8 +40,10 @@ import javax.security.auth.Subject;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.NotWritablePropertyException;
|
||||
@@ -92,6 +86,10 @@ import test.beans.LifecycleBean;
|
||||
import test.beans.NestedTestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Tests properties population and autowire behavior.
|
||||
*
|
||||
@@ -2159,13 +2157,41 @@ public class DefaultListableBeanFactoryTests {
|
||||
@Test
|
||||
public void testContainsBeanReturnsTrueEvenForAbstractBeanDefinition() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.registerBeanDefinition("abs",
|
||||
rootBeanDefinition(TestBean.class).setAbstract(true).getBeanDefinition());
|
||||
bf.registerBeanDefinition("abs", BeanDefinitionBuilder
|
||||
.rootBeanDefinition(TestBean.class).setAbstract(true).getBeanDefinition());
|
||||
assertThat(bf.containsBean("abs"), is(true));
|
||||
assertThat(bf.containsBean("bogus"), is(false));
|
||||
}
|
||||
|
||||
|
||||
static class A { }
|
||||
static class B { }
|
||||
|
||||
/**
|
||||
* Test that by-type bean lookup caching is working effectively by searching for a
|
||||
* bean of type B 10K times within a container having 1K additional beans of type A.
|
||||
* Prior to by-type caching, each bean lookup would traverse the entire container
|
||||
* (all 1001 beans), performing expensive assignability checks, etc. Now these
|
||||
* operations are necessary only once, providing a dramatic performance improvement.
|
||||
* On load-free modern hardware (e.g. an 8-core MPB), this method should complete well
|
||||
* under the 1000 ms timeout, usually ~= 300ms. With caching removed and on the same
|
||||
* hardware the method will take ~13000 ms. See SPR-6870.
|
||||
*/
|
||||
@Test(timeout=1000)
|
||||
public void testByTypeLookupIsFastEnough() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
|
||||
for (int i=0; i<1000; i++) {
|
||||
bf.registerBeanDefinition("a"+i, new RootBeanDefinition(A.class));
|
||||
}
|
||||
bf.registerBeanDefinition("b", new RootBeanDefinition(B.class));
|
||||
|
||||
for (int i=0; i<10000; i++) {
|
||||
bf.getBean(B.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class NoDependencies {
|
||||
|
||||
private NoDependencies() {
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+3
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -56,9 +56,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* @since 3.1
|
||||
* @see #setName
|
||||
* @see #setGroup
|
||||
* @see #setStartTime
|
||||
* @see #setJobName
|
||||
* @see #setJobGroup
|
||||
* @see #setStartDelay
|
||||
* @see #setJobDetail
|
||||
* @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setTriggers
|
||||
* @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setJobDetails
|
||||
@@ -147,10 +145,9 @@ public class CronTriggerFactoryBean implements FactoryBean<CronTrigger>, BeanNam
|
||||
* Set the start delay in milliseconds.
|
||||
* <p>The start delay is added to the current system time (when the bean starts)
|
||||
* to control the start time of the trigger.
|
||||
* @param startDelay the start delay, in milliseconds
|
||||
*/
|
||||
public void setStartDelay(long startDelay) {
|
||||
Assert.state(startDelay >= 0, "Start delay cannot be negative.");
|
||||
Assert.isTrue(startDelay >= 0, "Start delay cannot be negative");
|
||||
this.startDelay = startDelay;
|
||||
}
|
||||
|
||||
|
||||
+15
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -56,9 +56,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* @since 3.1
|
||||
* @see #setName
|
||||
* @see #setGroup
|
||||
* @see #setStartTime
|
||||
* @see #setJobName
|
||||
* @see #setJobGroup
|
||||
* @see #setStartDelay
|
||||
* @see #setJobDetail
|
||||
* @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setTriggers
|
||||
* @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setJobDetails
|
||||
@@ -84,6 +82,8 @@ public class SimpleTriggerFactoryBean implements FactoryBean<SimpleTrigger>, Bea
|
||||
|
||||
private long repeatInterval;
|
||||
|
||||
private int repeatCount = -1;
|
||||
|
||||
private int priority;
|
||||
|
||||
private int misfireInstruction;
|
||||
@@ -145,10 +145,9 @@ public class SimpleTriggerFactoryBean implements FactoryBean<SimpleTrigger>, Bea
|
||||
* Set the start delay in milliseconds.
|
||||
* <p>The start delay is added to the current system time (when the bean starts)
|
||||
* to control the start time of the trigger.
|
||||
* @param startDelay the start delay, in milliseconds
|
||||
*/
|
||||
public void setStartDelay(long startDelay) {
|
||||
Assert.state(startDelay >= 0, "Start delay cannot be negative.");
|
||||
Assert.isTrue(startDelay >= 0, "Start delay cannot be negative");
|
||||
this.startDelay = startDelay;
|
||||
}
|
||||
|
||||
@@ -159,6 +158,14 @@ public class SimpleTriggerFactoryBean implements FactoryBean<SimpleTrigger>, Bea
|
||||
this.repeatInterval = repeatInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the number of times this trigger is supposed to fire.
|
||||
* <p>Default is to repeat indefinitely.
|
||||
*/
|
||||
public void setRepeatCount(int repeatCount) {
|
||||
this.repeatCount = repeatCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the priority of this trigger.
|
||||
*/
|
||||
@@ -218,6 +225,7 @@ public class SimpleTriggerFactoryBean implements FactoryBean<SimpleTrigger>, Bea
|
||||
sti.setJobDataMap(this.jobDataMap);
|
||||
sti.setStartTime(this.startTime);
|
||||
sti.setRepeatInterval(this.repeatInterval);
|
||||
sti.setRepeatCount(this.repeatCount);
|
||||
sti.setPriority(this.priority);
|
||||
sti.setMisfireInstruction(this.misfireInstruction);
|
||||
this.simpleTrigger = sti;
|
||||
@@ -250,7 +258,7 @@ public class SimpleTriggerFactoryBean implements FactoryBean<SimpleTrigger>, Bea
|
||||
pvs.add("jobDataMap", this.jobDataMap);
|
||||
pvs.add("startTime", this.startTime);
|
||||
pvs.add("repeatInterval", this.repeatInterval);
|
||||
pvs.add("repeatCount", -1);
|
||||
pvs.add("repeatCount", this.repeatCount);
|
||||
pvs.add("priority", this.priority);
|
||||
pvs.add("misfireInstruction", this.misfireInstruction);
|
||||
bw.setPropertyValues(pvs);
|
||||
|
||||
@@ -15,14 +15,8 @@
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.springframework.ide.eclipse.core.springbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.springframework.ide.eclipse.core.springnature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
|
||||
</natures>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
Vendored
+2
-4
@@ -1,8 +1,6 @@
|
||||
|
||||
/**
|
||||
* Annotations and supporting classes for declarative cache management.
|
||||
* Hooked into Spring's caching interception infrastructure
|
||||
* via {@link org.springframework.cache.interceptor.CacheOperationSource
|
||||
* CacheOperationSource} implementation.
|
||||
* Hooked into Spring's cache interception infrastructure via
|
||||
* {@link org.springframework.cache.interceptor.CacheOperationSource}.
|
||||
*/
|
||||
package org.springframework.cache.annotation;
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -62,6 +62,7 @@ public class ConcurrentMapCache implements Cache {
|
||||
/**
|
||||
* Create a new ConcurrentMapCache with the specified name.
|
||||
* @param name the name of the cache
|
||||
* @param allowNullValues whether to accept and convert null values for this cache
|
||||
*/
|
||||
public ConcurrentMapCache(String name, boolean allowNullValues) {
|
||||
this(name, new ConcurrentHashMap<Object, Object>(), allowNullValues);
|
||||
@@ -115,7 +116,7 @@ public class ConcurrentMapCache implements Cache {
|
||||
/**
|
||||
* Convert the given value from the internal store to a user value
|
||||
* returned from the get method (adapting <code>null</code>).
|
||||
* @param userValue the store value
|
||||
* @param storeValue the store value
|
||||
* @return the value to return to the user
|
||||
*/
|
||||
protected Object fromStoreValue(Object storeValue) {
|
||||
|
||||
Vendored
+1
-3
@@ -1,9 +1,7 @@
|
||||
|
||||
/**
|
||||
* Implementation package for {@code java.util.concurrent} based caches.
|
||||
* Provides a {@link org.springframework.cache.CacheManager CacheManager}
|
||||
* and {@link org.springframework.cache.Cache Cache} implementation for
|
||||
* use in a Spring context.
|
||||
* use in a Spring context, using a JDK based thread pool at runtime.
|
||||
*/
|
||||
package org.springframework.cache.concurrent;
|
||||
|
||||
|
||||
Vendored
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -57,10 +57,6 @@ public class EhCacheCache implements Cache {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.cache.removeAll();
|
||||
}
|
||||
|
||||
public ValueWrapper get(Object key) {
|
||||
Element element = this.cache.get(key);
|
||||
return (element != null ? new SimpleValueWrapper(element.getObjectValue()) : null);
|
||||
@@ -74,4 +70,8 @@ public class EhCacheCache implements Cache {
|
||||
this.cache.remove(key);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.cache.removeAll();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
org.springframework.context/src/main/java/org/springframework/cache/ehcache/EhCacheCacheManager.java
Vendored
+11
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -39,20 +39,19 @@ public class EhCacheCacheManager extends AbstractCacheManager {
|
||||
|
||||
|
||||
/**
|
||||
* Returns the backing Ehcache {@link net.sf.ehcache.CacheManager}.
|
||||
* @return
|
||||
*/
|
||||
public net.sf.ehcache.CacheManager getCacheManager() {
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the backing EhCache {@link net.sf.ehcache.CacheManager}.
|
||||
* Set the backing EhCache {@link net.sf.ehcache.CacheManager}.
|
||||
*/
|
||||
public void setCacheManager(net.sf.ehcache.CacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the backing EhCache {@link net.sf.ehcache.CacheManager}.
|
||||
*/
|
||||
public net.sf.ehcache.CacheManager getCacheManager() {
|
||||
return this.cacheManager;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Collection<Cache> loadCaches() {
|
||||
@@ -83,4 +82,5 @@ public class EhCacheCacheManager extends AbstractCacheManager {
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Vendored
+17
-18
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -332,6 +332,21 @@ public class EhCacheFactoryBean implements FactoryBean<Ehcache>, BeanNameAware,
|
||||
this.cacheManager.addCache(rawCache);
|
||||
}
|
||||
|
||||
if (this.cacheEventListeners != null) {
|
||||
for (CacheEventListener listener : this.cacheEventListeners) {
|
||||
rawCache.getCacheEventNotificationService().registerListener(listener);
|
||||
}
|
||||
}
|
||||
if (this.statisticsEnabled) {
|
||||
rawCache.setStatisticsEnabled(true);
|
||||
}
|
||||
if (this.sampledStatisticsEnabled) {
|
||||
rawCache.setSampledStatisticsEnabled(true);
|
||||
}
|
||||
if (this.disabled) {
|
||||
rawCache.setDisabled(true);
|
||||
}
|
||||
|
||||
// Decorate cache if necessary.
|
||||
Ehcache decoratedCache = decorateCache(rawCache);
|
||||
if (decoratedCache != rawCache) {
|
||||
@@ -345,7 +360,7 @@ public class EhCacheFactoryBean implements FactoryBean<Ehcache>, BeanNameAware,
|
||||
*/
|
||||
protected Cache createCache() {
|
||||
// Only call EHCache 1.6 constructor if actually necessary (for compatibility with EHCache 1.3+)
|
||||
Cache cache = (!this.clearOnFlush) ?
|
||||
return (!this.clearOnFlush) ?
|
||||
new Cache(this.cacheName, this.maxElementsInMemory, this.memoryStoreEvictionPolicy,
|
||||
this.overflowToDisk, null, this.eternal, this.timeToLive, this.timeToIdle,
|
||||
this.diskPersistent, this.diskExpiryThreadIntervalSeconds, null,
|
||||
@@ -355,22 +370,6 @@ public class EhCacheFactoryBean implements FactoryBean<Ehcache>, BeanNameAware,
|
||||
this.overflowToDisk, null, this.eternal, this.timeToLive, this.timeToIdle,
|
||||
this.diskPersistent, this.diskExpiryThreadIntervalSeconds, null,
|
||||
this.bootstrapCacheLoader, this.maxElementsOnDisk, this.diskSpoolBufferSize);
|
||||
|
||||
if (this.cacheEventListeners != null) {
|
||||
for (CacheEventListener listener : this.cacheEventListeners) {
|
||||
cache.getCacheEventNotificationService().registerListener(listener);
|
||||
}
|
||||
}
|
||||
if (this.statisticsEnabled) {
|
||||
cache.setStatisticsEnabled(true);
|
||||
}
|
||||
if (this.sampledStatisticsEnabled) {
|
||||
cache.setSampledStatisticsEnabled(true);
|
||||
}
|
||||
if (this.disabled) {
|
||||
cache.setDisabled(true);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Vendored
+2
-6
@@ -1,11 +1,7 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Support classes for the open source cache
|
||||
* <a href="http://ehcache.sourceforge.net">Ehcache</a>,
|
||||
* allowing to set up an EHCache CacheManager and Caches
|
||||
* <a href="http://ehcache.sourceforge.net">EhCache</a>,
|
||||
* allowing to set up an EhCache CacheManager and Caches
|
||||
* as beans in a Spring context.
|
||||
*
|
||||
*/
|
||||
package org.springframework.cache.ehcache;
|
||||
|
||||
|
||||
+21
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -35,6 +35,7 @@ import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.annotation.Resource;
|
||||
@@ -509,9 +510,12 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
*/
|
||||
private class ResourceElement extends LookupElement {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
protected boolean shareable = true;
|
||||
|
||||
private volatile boolean cached = false;
|
||||
|
||||
private volatile Object cachedFieldValue;
|
||||
|
||||
public ResourceElement(Member member, PropertyDescriptor pd) {
|
||||
super(member, pd);
|
||||
}
|
||||
@@ -546,7 +550,20 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
|
||||
@Override
|
||||
protected Object getResourceToInject(Object target, String requestingBeanName) {
|
||||
return getResource(this, requestingBeanName);
|
||||
Object value = null;
|
||||
if (this.cached && this.shareable) {
|
||||
value = this.cachedFieldValue;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
value = getResource(this, requestingBeanName);
|
||||
if (value != null && this.shareable) {
|
||||
this.cachedFieldValue = value;
|
||||
this.cached = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,4 +741,4 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -185,7 +185,7 @@ class ConfigurationClassParser {
|
||||
throw new IllegalArgumentException("At least one @PropertySource(value) location is required");
|
||||
}
|
||||
for (int i = 0; i < nLocations; i++) {
|
||||
locations[0] = this.environment.resolveRequiredPlaceholders(locations[0]);
|
||||
locations[i] = this.environment.resolveRequiredPlaceholders(locations[i]);
|
||||
}
|
||||
ClassLoader classLoader = this.resourceLoader.getClassLoader();
|
||||
if (!StringUtils.hasText(name)) {
|
||||
|
||||
+12
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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.
|
||||
@@ -378,14 +378,19 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>The parent {@linkplain #getEnvironment() environment} is
|
||||
* delegated to this (child) context if the parent is a
|
||||
* {@link ConfigurableApplicationContext} implementation.
|
||||
* <p>The parent {@linkplain ApplicationContext#getEnvironment() environment} is
|
||||
* {@linkplain ConfigurableEnvironment#merge(ConfigurableEnvironment) merged} with
|
||||
* this (child) application context environment if the parent is non-{@code null} and
|
||||
* its environment is an instance of {@link ConfigurableEnvironment}.
|
||||
* @see ConfigurableEnvironment#merge(ConfigurableEnvironment)
|
||||
*/
|
||||
public void setParent(ApplicationContext parent) {
|
||||
this.parent = parent;
|
||||
if (parent instanceof ConfigurableApplicationContext) {
|
||||
this.setEnvironment(((ConfigurableApplicationContext)parent).getEnvironment());
|
||||
if (parent != null) {
|
||||
Object parentEnvironment = parent.getEnvironment();
|
||||
if (parentEnvironment instanceof ConfigurableEnvironment) {
|
||||
this.environment.merge((ConfigurableEnvironment)parentEnvironment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,7 +511,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
/**
|
||||
* <p>Replace any stub property sources with actual instances.
|
||||
* @see org.springframework.core.env.PropertySource.StubPropertySource
|
||||
* @see org.springframework.web.context.support.WebApplicationContextUtils#initSerlvetPropertySources
|
||||
* @see org.springframework.web.context.support.WebApplicationContextUtils#initServletPropertySources
|
||||
*/
|
||||
protected void initPropertySources() {
|
||||
// For subclasses: do nothing by default.
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.scheduling.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.springframework.aop.interceptor.AsyncExecutionInterceptor;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
||||
/**
|
||||
* Specialization of {@link AsyncExecutionInterceptor} that delegates method execution to
|
||||
* an {@code Executor} based on the {@link Async} annotation. Specifically designed to
|
||||
* support use of {@link Async#value()} executor qualification mechanism introduced in
|
||||
* Spring 3.1.2. Supports detecting qualifier metadata via {@code @Async} at the method or
|
||||
* declaring class level. See {@link #getExecutorQualifier(Method)} for details.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1.2
|
||||
* @see org.springframework.scheduling.annotation.Async
|
||||
* @see org.springframework.scheduling.annotation.AsyncAnnotationAdvisor
|
||||
*/
|
||||
public class AnnotationAsyncExecutionInterceptor extends AsyncExecutionInterceptor {
|
||||
|
||||
/**
|
||||
* Create a new {@code AnnotationAsyncExecutionInterceptor} with the given executor.
|
||||
* @param defaultExecutor the executor to be used by default if no more specific
|
||||
* executor has been qualified at the method level using {@link Async#value()}.
|
||||
*/
|
||||
public AnnotationAsyncExecutionInterceptor(Executor defaultExecutor) {
|
||||
super(defaultExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the qualifier or bean name of the executor to be used when executing the
|
||||
* given method, specified via {@link Async#value} at the method or declaring
|
||||
* class level. If {@code @Async} is specified at both the method and class level, the
|
||||
* method's {@code #value} takes precedence (even if empty string, indicating that
|
||||
* the default executor should be used preferentially).
|
||||
* @param method the method to inspect for executor qualifier metadata
|
||||
* @return the qualifier if specified, otherwise empty string indicating that the
|
||||
* {@linkplain #setExecutor(Executor) default executor} should be used
|
||||
* @see #determineAsyncExecutor(Method)
|
||||
*/
|
||||
@Override
|
||||
protected String getExecutorQualifier(Method method) {
|
||||
// maintainer's note: changes made here should also be made in
|
||||
// AnnotationAsyncExecutionAspect#getExecutorQualifier
|
||||
Async async = AnnotationUtils.findAnnotation(method, Async.class);
|
||||
if (async == null) {
|
||||
async = AnnotationUtils.findAnnotation(method.getDeclaringClass(), Async.class);
|
||||
}
|
||||
return async == null ? null : async.value();
|
||||
}
|
||||
|
||||
}
|
||||
+22
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* 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.
|
||||
@@ -28,17 +28,18 @@ import java.lang.annotation.Target;
|
||||
* considered as asynchronous.
|
||||
*
|
||||
* <p>In terms of target method signatures, any parameter types are supported.
|
||||
* However, the return type is constrained to either <code>void</code> or
|
||||
* <code>java.util.concurrent.Future</code>. In the latter case, the Future handle
|
||||
* returned from the proxy will be an actual asynchronous Future that can be used
|
||||
* However, the return type is constrained to either {@code void} or
|
||||
* {@link java.util.concurrent.Future}. In the latter case, the {@code Future} handle
|
||||
* returned from the proxy will be an actual asynchronous {@code Future} that can be used
|
||||
* to track the result of the asynchronous method execution. However, since the
|
||||
* target method needs to implement the same signature, it will have to return
|
||||
* a temporary Future handle that just passes the return value through: e.g.
|
||||
* Spring's {@link AsyncResult} or EJB 3.1's <code>javax.ejb.AsyncResult</code>.
|
||||
* a temporary {@code Future} handle that just passes the return value through: e.g.
|
||||
* Spring's {@link AsyncResult} or EJB 3.1's {@link javax.ejb.AsyncResult}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
* @see org.springframework.aop.interceptor.AsyncExecutionInterceptor
|
||||
* @see AnnotationAsyncExecutionInterceptor
|
||||
* @see AsyncAnnotationAdvisor
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@@ -46,4 +47,18 @@ import java.lang.annotation.Target;
|
||||
@Documented
|
||||
public @interface Async {
|
||||
|
||||
/**
|
||||
* A qualifier value for the specified asynchronous operation(s).
|
||||
* <p>May be used to determine the target executor to be used when executing this
|
||||
* method, matching the qualifier value (or the bean name) of a specific
|
||||
* {@link java.util.concurrent.Executor Executor} or
|
||||
* {@link org.springframework.core.task.TaskExecutor TaskExecutor}
|
||||
* bean definition.
|
||||
* <p>When specified on a class level {@code @Async} annotation, indicates that the
|
||||
* given executor should be used for all methods within the class. Method level use
|
||||
* of {@link Async#value} always overrides any value set at the class level.
|
||||
* @since 3.1.2
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
|
||||
+31
-16
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* 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.
|
||||
@@ -25,11 +25,12 @@ import java.util.concurrent.Executor;
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.interceptor.AsyncExecutionInterceptor;
|
||||
import org.springframework.aop.support.AbstractPointcutAdvisor;
|
||||
import org.springframework.aop.support.ComposablePointcut;
|
||||
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -45,27 +46,30 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
* @see PersistenceExceptionTranslationAdvisor
|
||||
* @see org.springframework.dao.annotation.PersistenceExceptionTranslationAdvisor
|
||||
* @see org.springframework.stereotype.Repository
|
||||
* @see org.springframework.dao.DataAccessException
|
||||
* @see org.springframework.dao.support.PersistenceExceptionTranslator
|
||||
*/
|
||||
public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor {
|
||||
@SuppressWarnings("serial")
|
||||
public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
|
||||
|
||||
private Advice advice;
|
||||
|
||||
private Pointcut pointcut;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ConcurrencyAnnotationBeanPostProcessor for bean-style configuration.
|
||||
* Create a new {@code AsyncAnnotationAdvisor} for bean-style configuration.
|
||||
*/
|
||||
public AsyncAnnotationAdvisor() {
|
||||
this(new SimpleAsyncTaskExecutor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ConcurrencyAnnotationBeanPostProcessor for the given task executor.
|
||||
* Create a new {@code AsyncAnnotationAdvisor} for the given task executor.
|
||||
* @param executor the task executor to use for asynchronous methods
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -74,20 +78,36 @@ public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor {
|
||||
asyncAnnotationTypes.add(Async.class);
|
||||
ClassLoader cl = AsyncAnnotationAdvisor.class.getClassLoader();
|
||||
try {
|
||||
asyncAnnotationTypes.add((Class) cl.loadClass("javax.ejb.Asynchronous"));
|
||||
asyncAnnotationTypes.add((Class<? extends Annotation>) cl.loadClass("javax.ejb.Asynchronous"));
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// If EJB 3.1 API not present, simply ignore.
|
||||
}
|
||||
this.advice = buildAdvice(executor);
|
||||
this.setTaskExecutor(executor);
|
||||
this.pointcut = buildPointcut(asyncAnnotationTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@code BeanFactory} to be used when looking up executors by qualifier.
|
||||
*/
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
delegateBeanFactory(beanFactory);
|
||||
}
|
||||
|
||||
public void delegateBeanFactory(BeanFactory beanFactory) {
|
||||
if (this.advice instanceof AnnotationAsyncExecutionInterceptor) {
|
||||
((AnnotationAsyncExecutionInterceptor)this.advice).setBeanFactory(beanFactory);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the task executor to use for asynchronous methods.
|
||||
*/
|
||||
public void setTaskExecutor(Executor executor) {
|
||||
this.advice = buildAdvice(executor);
|
||||
delegateBeanFactory(this.beanFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,17 +137,12 @@ public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor {
|
||||
|
||||
|
||||
protected Advice buildAdvice(Executor executor) {
|
||||
if (executor instanceof AsyncTaskExecutor) {
|
||||
return new AsyncExecutionInterceptor((AsyncTaskExecutor) executor);
|
||||
}
|
||||
else {
|
||||
return new AsyncExecutionInterceptor(executor);
|
||||
}
|
||||
return new AnnotationAsyncExecutionInterceptor(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a pointcut for the given target class, if any.
|
||||
* @param targetClass the class to introspect
|
||||
* Calculate a pointcut for the given async annotation types, if any.
|
||||
* @param asyncAnnotationTypes the async annotation types to introspect
|
||||
* @return the applicable Pointcut object, or <code>null</code> if none
|
||||
*/
|
||||
protected Pointcut buildPointcut(Set<Class<? extends Annotation>> asyncAnnotationTypes) {
|
||||
|
||||
+13
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -24,7 +24,10 @@ import org.springframework.aop.framework.AopInfrastructureBean;
|
||||
import org.springframework.aop.framework.ProxyConfig;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.core.Ordered;
|
||||
@@ -53,7 +56,8 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class AsyncAnnotationBeanPostProcessor extends ProxyConfig
|
||||
implements BeanPostProcessor, BeanClassLoaderAware, InitializingBean, Ordered {
|
||||
implements BeanPostProcessor, BeanClassLoaderAware, BeanFactoryAware,
|
||||
InitializingBean, Ordered {
|
||||
|
||||
private Class<? extends Annotation> asyncAnnotationType;
|
||||
|
||||
@@ -69,6 +73,8 @@ public class AsyncAnnotationBeanPostProcessor extends ProxyConfig
|
||||
*/
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Set the 'async' annotation type to be detected at either class or method
|
||||
@@ -95,12 +101,17 @@ public class AsyncAnnotationBeanPostProcessor extends ProxyConfig
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
this.asyncAnnotationAdvisor = (this.executor != null ?
|
||||
new AsyncAnnotationAdvisor(this.executor) : new AsyncAnnotationAdvisor());
|
||||
if (this.asyncAnnotationType != null) {
|
||||
this.asyncAnnotationAdvisor.setAsyncAnnotationType(this.asyncAnnotationType);
|
||||
}
|
||||
this.asyncAnnotationAdvisor.setBeanFactory(this.beanFactory);
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
|
||||
+7
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -105,12 +105,16 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
* taskRegistrar.setScheduler(taskExecutor());
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* @Bean(destroyMethod="shutdown")
|
||||
* public Executor taskExecutor() {
|
||||
* return Executors.newScheduledThreadPool(100);
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* Note in the example above the use of {@code @Bean(destroyMethod="shutdown")}. This
|
||||
* ensures that the task executor is properly shut down when the Spring application
|
||||
* context itself is closed.
|
||||
*
|
||||
* Implementing {@code SchedulingConfigurer} also allows for fine-grained
|
||||
* control over task registration via the {@code ScheduledTaskRegistrar}.
|
||||
* For example, the following configures the execution of a particular bean
|
||||
@@ -133,7 +137,7 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
* );
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* @Bean(destroyMethod="shutdown")
|
||||
* public Executor taskScheduler() {
|
||||
* return Executors.newScheduledThreadPool(42);
|
||||
* }
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
* of that class. By default, JSR-303 will validate against its default group only.
|
||||
*
|
||||
* <p>As of Spring 3.1, this functionality requires Hibernate Validator 4.2 or higher.
|
||||
* In Spring 3.2, this class will autodetect a Bean Validation 1.1 compliant provider
|
||||
* In Spring 3.1.2, this class will autodetect a Bean Validation 1.1 compliant provider
|
||||
* and automatically use the standard method validation support there (once available).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
* as well. By default, JSR-303 will validate against its default group only.
|
||||
*
|
||||
* <p>As of Spring 3.1, this functionality requires Hibernate Validator 4.2 or higher.
|
||||
* In Spring 3.2, this class will autodetect a Bean Validation 1.1 compliant provider
|
||||
* In Spring 3.1.2, this class will autodetect a Bean Validation 1.1 compliant provider
|
||||
* and automatically use the standard method validation support there (once available).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
|
||||
+7
-1
@@ -35,7 +35,10 @@
|
||||
<xsd:documentation><![CDATA[
|
||||
Specifies the java.util.Executor instance to use when invoking asynchronous methods.
|
||||
If not provided, an instance of org.springframework.core.task.SimpleAsyncTaskExecutor
|
||||
will be used by default
|
||||
will be used by default.
|
||||
Note that as of Spring 3.1.2, individual @Async methods may qualify which executor to
|
||||
use, meaning that the executor specified here acts as a default for all non-qualified
|
||||
@Async methods.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
@@ -98,6 +101,9 @@
|
||||
required even when defining the executor as an inner bean: The executor
|
||||
won't be directly accessible then but will nevertheless use the specified
|
||||
id as the thread name prefix of the threads that it manages.
|
||||
In the case of multiple task:executors, as of Spring 3.1.2 this value may be used to
|
||||
qualify which executor should handle a given @Async method, e.g. @Async("executorId").
|
||||
See the Javadoc for the #value attribute of Spring's @Async annotation for details.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.scheduling.annotation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AnnotationAsyncExecutionInterceptor}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1.2
|
||||
*/
|
||||
public class AnnotationAsyncExecutionInterceptorTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unused")
|
||||
public void testGetExecutorQualifier() throws SecurityException, NoSuchMethodException {
|
||||
AnnotationAsyncExecutionInterceptor i = new AnnotationAsyncExecutionInterceptor(null);
|
||||
{ // method level
|
||||
class C { @Async("qMethod") void m() { } }
|
||||
assertThat(i.getExecutorQualifier(C.class.getDeclaredMethod("m")), is("qMethod"));
|
||||
}
|
||||
{ // class level
|
||||
@Async("qClass") class C { void m() { } }
|
||||
assertThat(i.getExecutorQualifier(C.class.getDeclaredMethod("m")), is("qClass"));
|
||||
}
|
||||
{ // method and class level -> method value overrides
|
||||
@Async("qClass") class C { @Async("qMethod") void m() { } }
|
||||
assertThat(i.getExecutorQualifier(C.class.getDeclaredMethod("m")), is("qMethod"));
|
||||
}
|
||||
{ // method and class level -> method value, even if empty, overrides
|
||||
@Async("qClass") class C { @Async void m() { } }
|
||||
assertThat(i.getExecutorQualifier(C.class.getDeclaredMethod("m")), is(""));
|
||||
}
|
||||
{ // meta annotation with qualifier
|
||||
@MyAsync class C { void m() { } }
|
||||
assertThat(i.getExecutorQualifier(C.class.getDeclaredMethod("m")), is("qMeta"));
|
||||
}
|
||||
}
|
||||
|
||||
@Async("qMeta")
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface MyAsync { }
|
||||
}
|
||||
+55
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* 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.
|
||||
@@ -18,8 +18,6 @@ package org.springframework.scheduling.annotation;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
|
||||
@@ -27,10 +25,13 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.scheduling.annotation.AsyncResult;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class AsyncExecutionTests {
|
||||
|
||||
@@ -56,6 +57,26 @@ public class AsyncExecutionTests {
|
||||
assertEquals("20", future.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asyncMethodsWithQualifier() throws Exception {
|
||||
originalThreadName = Thread.currentThread().getName();
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
context.registerBeanDefinition("asyncTest", new RootBeanDefinition(AsyncMethodWithQualifierBean.class));
|
||||
context.registerBeanDefinition("autoProxyCreator", new RootBeanDefinition(DefaultAdvisorAutoProxyCreator.class));
|
||||
context.registerBeanDefinition("asyncAdvisor", new RootBeanDefinition(AsyncAnnotationAdvisor.class));
|
||||
context.registerBeanDefinition("e0", new RootBeanDefinition(ThreadPoolTaskExecutor.class));
|
||||
context.registerBeanDefinition("e1", new RootBeanDefinition(ThreadPoolTaskExecutor.class));
|
||||
context.registerBeanDefinition("e2", new RootBeanDefinition(ThreadPoolTaskExecutor.class));
|
||||
context.refresh();
|
||||
AsyncMethodWithQualifierBean asyncTest = context.getBean("asyncTest", AsyncMethodWithQualifierBean.class);
|
||||
asyncTest.doNothing(5);
|
||||
asyncTest.doSomething(10);
|
||||
Future<String> future = asyncTest.returnSomething(20);
|
||||
assertEquals("20", future.get());
|
||||
Future<String> future2 = asyncTest.returnSomething2(30);
|
||||
assertEquals("30", future2.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asyncClass() throws Exception {
|
||||
originalThreadName = Thread.currentThread().getName();
|
||||
@@ -155,7 +176,6 @@ public class AsyncExecutionTests {
|
||||
|
||||
@Async
|
||||
public void doSomething(int i) {
|
||||
System.out.println(Thread.currentThread().getName() + ": " + i);
|
||||
assertTrue(!Thread.currentThread().getName().equals(originalThreadName));
|
||||
}
|
||||
|
||||
@@ -167,11 +187,38 @@ public class AsyncExecutionTests {
|
||||
}
|
||||
|
||||
|
||||
@Async("e0")
|
||||
public static class AsyncMethodWithQualifierBean {
|
||||
|
||||
public void doNothing(int i) {
|
||||
assertTrue(Thread.currentThread().getName().equals(originalThreadName));
|
||||
}
|
||||
|
||||
@Async("e1")
|
||||
public void doSomething(int i) {
|
||||
assertTrue(!Thread.currentThread().getName().equals(originalThreadName));
|
||||
assertTrue(Thread.currentThread().getName().startsWith("e1-"));
|
||||
}
|
||||
|
||||
@Async("e2")
|
||||
public Future<String> returnSomething(int i) {
|
||||
assertTrue(!Thread.currentThread().getName().equals(originalThreadName));
|
||||
assertTrue(Thread.currentThread().getName().startsWith("e2-"));
|
||||
return new AsyncResult<String>(Integer.toString(i));
|
||||
}
|
||||
|
||||
public Future<String> returnSomething2(int i) {
|
||||
assertTrue(!Thread.currentThread().getName().equals(originalThreadName));
|
||||
assertTrue(Thread.currentThread().getName().startsWith("e0-"));
|
||||
return new AsyncResult<String>(Integer.toString(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Async
|
||||
public static class AsyncClassBean {
|
||||
|
||||
public void doSomething(int i) {
|
||||
System.out.println(Thread.currentThread().getName() + ": " + i);
|
||||
assertTrue(!Thread.currentThread().getName().equals(originalThreadName));
|
||||
}
|
||||
|
||||
@@ -194,7 +241,6 @@ public class AsyncExecutionTests {
|
||||
public static class AsyncInterfaceBean implements AsyncInterface {
|
||||
|
||||
public void doSomething(int i) {
|
||||
System.out.println(Thread.currentThread().getName() + ": " + i);
|
||||
assertTrue(!Thread.currentThread().getName().equals(originalThreadName));
|
||||
}
|
||||
|
||||
@@ -224,7 +270,6 @@ public class AsyncExecutionTests {
|
||||
}
|
||||
|
||||
public void doSomething(int i) {
|
||||
System.out.println(Thread.currentThread().getName() + ": " + i);
|
||||
assertTrue(!Thread.currentThread().getName().equals(originalThreadName));
|
||||
}
|
||||
|
||||
@@ -235,7 +280,7 @@ public class AsyncExecutionTests {
|
||||
}
|
||||
|
||||
|
||||
public static class AsyncMethodListener implements ApplicationListener {
|
||||
public static class AsyncMethodListener implements ApplicationListener<ApplicationEvent> {
|
||||
|
||||
@Async
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
@@ -246,7 +291,7 @@ public class AsyncExecutionTests {
|
||||
|
||||
|
||||
@Async
|
||||
public static class AsyncClassListener implements ApplicationListener {
|
||||
public static class AsyncClassListener implements ApplicationListener<ApplicationEvent> {
|
||||
|
||||
public AsyncClassListener() {
|
||||
listenerConstructed++;
|
||||
|
||||
+75
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,22 +16,22 @@
|
||||
|
||||
package org.springframework.scheduling.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -39,6 +39,11 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Tests use of @EnableAsync on @Configuration classes.
|
||||
*
|
||||
@@ -69,6 +74,48 @@ public class EnableAsyncTests {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void withAsyncBeanWithExecutorQualifiedByName() throws ExecutionException, InterruptedException {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(AsyncWithExecutorQualifiedByNameConfig.class);
|
||||
ctx.refresh();
|
||||
|
||||
AsyncBeanWithExecutorQualifiedByName asyncBean = ctx.getBean(AsyncBeanWithExecutorQualifiedByName.class);
|
||||
Future<Thread> workerThread0 = asyncBean.work0();
|
||||
assertThat(workerThread0.get().getName(), not(anyOf(startsWith("e1-"), startsWith("otherExecutor-"))));
|
||||
Future<Thread> workerThread = asyncBean.work();
|
||||
assertThat(workerThread.get().getName(), startsWith("e1-"));
|
||||
Future<Thread> workerThread2 = asyncBean.work2();
|
||||
assertThat(workerThread2.get().getName(), startsWith("otherExecutor-"));
|
||||
Future<Thread> workerThread3 = asyncBean.work3();
|
||||
assertThat(workerThread3.get().getName(), startsWith("otherExecutor-"));
|
||||
}
|
||||
|
||||
|
||||
static class AsyncBeanWithExecutorQualifiedByName {
|
||||
@Async
|
||||
public Future<Thread> work0() {
|
||||
return new AsyncResult<Thread>(Thread.currentThread());
|
||||
}
|
||||
|
||||
@Async("e1")
|
||||
public Future<Thread> work() {
|
||||
return new AsyncResult<Thread>(Thread.currentThread());
|
||||
}
|
||||
|
||||
@Async("otherExecutor")
|
||||
public Future<Thread> work2() {
|
||||
return new AsyncResult<Thread>(Thread.currentThread());
|
||||
}
|
||||
|
||||
@Async("e2")
|
||||
public Future<Thread> work3() {
|
||||
return new AsyncResult<Thread>(Thread.currentThread());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class AsyncBean {
|
||||
private Thread threadOfExecution;
|
||||
|
||||
@@ -206,6 +253,28 @@ public class EnableAsyncTests {
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
static class AsyncWithExecutorQualifiedByNameConfig {
|
||||
@Bean
|
||||
public AsyncBeanWithExecutorQualifiedByName asyncBean() {
|
||||
return new AsyncBeanWithExecutorQualifiedByName();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Executor e1() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
return executor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Qualifier("e2")
|
||||
public Executor otherExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ Bundle-Name: Spring Context
|
||||
Bundle-Vendor: SpringSource
|
||||
Bundle-ManifestVersion: 2
|
||||
Import-Package:
|
||||
javax.inject;version="[1.0.0, 2.0.0)";resolution:=optional,
|
||||
javax.persistence;version="[1.0.0, 3.0.0)";resolution:=optional,
|
||||
org.springframework.orm.jpa.support;version="${spring.osgi.range.nq}";resolution:=optional,
|
||||
com.ibm.websphere.management;version="0";resolution:=optional
|
||||
@@ -13,7 +14,6 @@ Import-Template:
|
||||
groovy.*;version="[1.5.0, 2.0.0)";resolution:=optional,
|
||||
javax.annotation.*;version="0";resolution:=optional,
|
||||
javax.ejb.*;version="[2.1.0, 4.0.0)";resolution:=optional,
|
||||
javax.inject.*;version="[1.0.0, 2.0.0)";resolution:=optional,
|
||||
javax.interceptor.*;version="[3.0.0, 4.0.0)";resolution:=optional,
|
||||
javax.jms.*;version="[1.1.0, 2.0.0)";resolution:=optional,
|
||||
javax.management.*;version="0";resolution:=optional,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.aspectj/com.springsource.org.aspectj.weaver/1.6.8.RELEASE/com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.custommonkey.xmlunit/com.springsource.org.custommonkey.xmlunit/1.2.0/com.springsource.org.custommonkey.xmlunit-1.2.0.jar" sourcepath="/IVY_CACHE/org.custommonkey.xmlunit/com.springsource.org.custommonkey.xmlunit/1.2.0/com.springsource.org.custommonkey.xmlunit-sources-1.2.0.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.easymock/com.springsource.org.easymock/2.5.1/com.springsource.org.easymock-2.5.1.jar" sourcepath="/IVY_CACHE/org.easymock/com.springsource.org.easymock/2.5.1/com.springsource.org.easymock-sources-2.5.1.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.hamcrest/com.springsource.org.hamcrest/1.1.0/com.springsource.org.hamcrest-1.1.0.jar" sourcepath="IVY_CACHE/org.hamcrest/com.springsource.org.hamcrest/1.1.0/com.springsource.org.hamcrest-sources-1.1.0.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.codehaus.woodstox/com.springsource.com.ctc.wstx/3.2.7/com.springsource.com.ctc.wstx-3.2.7.jar" sourcepath="/IVY_CACHE/org.codehaus.woodstox/com.springsource.com.ctc.wstx/3.2.7/com.springsource.com.ctc.wstx-sources-3.2.7.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/net.sourceforge.jopt-simple/com.springsource.joptsimple/3.0.0/com.springsource.joptsimple-3.0.0.jar" sourcepath="IVY_CACHE/net.sourceforge.jopt-simple/com.springsource.joptsimple/3.0.0/com.springsource.joptsimple-sources-3.0.0.jar"/>
|
||||
<classpathentry kind="lib" path="/org.springframework.asm/target/artifacts/org.springframework.asm.jar" sourcepath="/org.springframework.asm/target/artifacts/org.springframework.asm-sources.jar"/>
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<dependency org="net.sourceforge.jopt-simple" name="com.springsource.joptsimple" rev="3.0.0" conf="optional->compile"/>
|
||||
<!-- test dependencies -->
|
||||
<dependency org="org.junit" name="com.springsource.org.junit" rev="${junit.version}" conf="test->runtime"/>
|
||||
<dependency org="org.hamcrest" name="com.springsource.org.hamcrest" rev="1.1.0" conf="test->compile"/>
|
||||
<dependency org="org.easymock" name="com.springsource.org.easymock" rev="2.5.1" conf="test->compile"/>
|
||||
<dependency org="org.custommonkey.xmlunit" name="com.springsource.org.custommonkey.xmlunit" rev="1.2.0" conf="test->compile"/>
|
||||
<dependency org="org.codehaus.woodstox" name="com.springsource.com.ctc.wstx" rev="3.2.7" conf="test->compile"/>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
Vendored
+17
-3
@@ -41,9 +41,9 @@ import static org.springframework.util.StringUtils.*;
|
||||
*
|
||||
* <p>Concrete subclasses differ primarily on which {@link PropertySource} objects they
|
||||
* add by default. {@code AbstractEnvironment} adds none. Subclasses should contribute
|
||||
* property sources through the protected {@link #customizePropertySources()} hook, while
|
||||
* clients should customize using {@link ConfigurableEnvironment#getPropertySources()} and
|
||||
* working against the {@link MutablePropertySources} API. See
|
||||
* property sources through the protected {@link #customizePropertySources(MutablePropertySources)}
|
||||
* hook, while clients should customize using {@link ConfigurableEnvironment#getPropertySources()}
|
||||
* and working against the {@link MutablePropertySources} API. See
|
||||
* {@link ConfigurableEnvironment} Javadoc for usage examples.
|
||||
*
|
||||
* @author Chris Beams
|
||||
@@ -387,6 +387,20 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
|
||||
return systemProperties;
|
||||
}
|
||||
|
||||
public void merge(ConfigurableEnvironment parent) {
|
||||
for (PropertySource<?> ps : parent.getPropertySources()) {
|
||||
if (!this.propertySources.contains(ps.getName())) {
|
||||
this.propertySources.addLast(ps);
|
||||
}
|
||||
}
|
||||
for (String profile : parent.getActiveProfiles()) {
|
||||
this.activeProfiles.add(profile);
|
||||
}
|
||||
for (String profile : parent.getDefaultProfiles()) {
|
||||
this.defaultProfiles.add(profile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of ConfigurablePropertyResolver interface
|
||||
|
||||
Vendored
+29
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -55,13 +55,14 @@ import java.util.Map;
|
||||
* propertySources.replace(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, mockEnvVars);
|
||||
* </pre>
|
||||
*
|
||||
* When an {@link Environment} is being used by an ApplicationContext, it is important
|
||||
* that any such PropertySource manipulations be performed <em>before</em> the context's
|
||||
* {@link org.springframework.context.support.AbstractApplicationContext#refresh()
|
||||
* refresh()} method is called. This ensures that all property sources are available
|
||||
* during the container bootstrap process, including use by
|
||||
* {@linkplain org.springframework.context.support.PropertySourcesPlaceholderConfigurer
|
||||
* property placeholder configurers}.
|
||||
* When an {@link Environment} is being used by an {@code ApplicationContext}, it is
|
||||
* important that any such {@code PropertySource} manipulations be performed
|
||||
* <em>before</em> the context's {@link
|
||||
* org.springframework.context.support.AbstractApplicationContext#refresh() refresh()}
|
||||
* method is called. This ensures that all property sources are available during the
|
||||
* container bootstrap process, including use by {@linkplain
|
||||
* org.springframework.context.support.PropertySourcesPlaceholderConfigurer property
|
||||
* placeholder configurers}.
|
||||
*
|
||||
*
|
||||
* @author Chris Beams
|
||||
@@ -78,7 +79,6 @@ public interface ConfigurableEnvironment extends Environment, ConfigurableProper
|
||||
* <p>Any existing active profiles will be replaced with the given arguments; call
|
||||
* with zero arguments to clear the current set of active profiles. Use
|
||||
* {@link #addActiveProfile} to add a profile while preserving the existing set.
|
||||
*
|
||||
* @see #addActiveProfile
|
||||
* @see #setDefaultProfiles
|
||||
* @see org.springframework.context.annotation.Profile
|
||||
@@ -123,12 +123,10 @@ public interface ConfigurableEnvironment extends Environment, ConfigurableProper
|
||||
* Return the value of {@link System#getenv()} if allowed by the current
|
||||
* {@link SecurityManager}, otherwise return a map implementation that will attempt
|
||||
* to access individual keys using calls to {@link System#getenv(String)}.
|
||||
*
|
||||
* <p>Note that most {@link Environment} implementations will include this system
|
||||
* environment map as a default {@link PropertySource} to be searched. Therefore, it
|
||||
* is recommended that this method not be used directly unless bypassing other
|
||||
* property sources is expressly intended.
|
||||
*
|
||||
* <p>Calls to {@link Map#get(Object)} on the Map returned will never throw
|
||||
* {@link IllegalAccessException}; in cases where the SecurityManager forbids access
|
||||
* to a property, {@code null} will be returned and an INFO-level log message will be
|
||||
@@ -140,12 +138,10 @@ public interface ConfigurableEnvironment extends Environment, ConfigurableProper
|
||||
* Return the value of {@link System#getProperties()} if allowed by the current
|
||||
* {@link SecurityManager}, otherwise return a map implementation that will attempt
|
||||
* to access individual keys using calls to {@link System#getProperty(String)}.
|
||||
*
|
||||
* <p>Note that most {@code Environment} implementations will include this system
|
||||
* properties map as a default {@link PropertySource} to be searched. Therefore, it is
|
||||
* recommended that this method not be used directly unless bypassing other property
|
||||
* sources is expressly intended.
|
||||
*
|
||||
* <p>Calls to {@link Map#get(Object)} on the Map returned will never throw
|
||||
* {@link IllegalAccessException}; in cases where the SecurityManager forbids access
|
||||
* to a property, {@code null} will be returned and an INFO-level log message will be
|
||||
@@ -153,4 +149,24 @@ public interface ConfigurableEnvironment extends Environment, ConfigurableProper
|
||||
*/
|
||||
Map<String, Object> getSystemProperties();
|
||||
|
||||
/**
|
||||
* Append the given parent environment's active profiles, default profiles and
|
||||
* property sources to this (child) environment's respective collections of each.
|
||||
* <p>For any identically-named {@code PropertySource} instance existing in both
|
||||
* parent and child, the child instance is to be preserved and the parent instance
|
||||
* discarded. This has the effect of allowing overriding of property sources by the
|
||||
* child as well as avoiding redundant searches through common property source types,
|
||||
* e.g. system environment and system properties.
|
||||
* <p>Active and default profile names are also filtered for duplicates, to avoid
|
||||
* confusion and redundant storage.
|
||||
* <p>The parent environment remains unmodified in any case. Note that any changes to
|
||||
* the parent environment occurring after the call to {@code merge} will not be
|
||||
* reflected in the child. Therefore, care should be taken to configure parent
|
||||
* property sources and profile information prior to calling {@code merge}.
|
||||
* @param parent the environment to merge with
|
||||
* @since 3.1.2
|
||||
* @see org.springframework.context.support.AbstractApplicationContext#setParent
|
||||
*/
|
||||
void merge(ConfigurableEnvironment parent);
|
||||
|
||||
}
|
||||
|
||||
Vendored
+9
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -79,7 +79,8 @@ public class MutablePropertySources implements PropertySources {
|
||||
}
|
||||
|
||||
public PropertySource<?> get(String name) {
|
||||
return this.propertySourceList.get(this.propertySourceList.indexOf(PropertySource.named(name)));
|
||||
int index = this.propertySourceList.indexOf(PropertySource.named(name));
|
||||
return index == -1 ? null : this.propertySourceList.get(index);
|
||||
}
|
||||
|
||||
public Iterator<PropertySource<?>> iterator() {
|
||||
@@ -146,10 +147,7 @@ public class MutablePropertySources implements PropertySources {
|
||||
public PropertySource<?> remove(String name) {
|
||||
logger.debug(String.format("Removing [%s] PropertySource", name));
|
||||
int index = this.propertySourceList.indexOf(PropertySource.named(name));
|
||||
if (index >= 0) {
|
||||
return this.propertySourceList.remove(index);
|
||||
}
|
||||
return null;
|
||||
return index == -1 ? null : this.propertySourceList.remove(index);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,11 +208,13 @@ public class MutablePropertySources implements PropertySources {
|
||||
|
||||
/**
|
||||
* Assert that the named property source is present and return its index.
|
||||
* @param name the {@linkplain PropertySource#getName() name of the property source}
|
||||
* to find
|
||||
* @throws IllegalArgumentException if the named property source is not present
|
||||
*/
|
||||
private int assertPresentAndGetIndex(String propertySourceName) {
|
||||
int index = this.propertySourceList.indexOf(PropertySource.named(propertySourceName));
|
||||
Assert.isTrue(index >= 0, String.format(NON_EXISTENT_PROPERTY_SOURCE_MESSAGE, propertySourceName));
|
||||
private int assertPresentAndGetIndex(String name) {
|
||||
int index = this.propertySourceList.indexOf(PropertySource.named(name));
|
||||
Assert.isTrue(index >= 0, String.format(NON_EXISTENT_PROPERTY_SOURCE_MESSAGE, name));
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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
|
||||
* 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,
|
||||
@@ -72,6 +72,9 @@ public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {
|
||||
Object value;
|
||||
if ((value = propertySource.getProperty(key)) != null) {
|
||||
Class<?> valueType = value.getClass();
|
||||
if (String.class.equals(valueType)) {
|
||||
value = this.resolveRequiredPlaceholders((String) value);
|
||||
}
|
||||
if (debugEnabled) {
|
||||
logger.debug(
|
||||
format("Found key '%s' in [%s] with type [%s] and value '%s'",
|
||||
|
||||
+3
-2
@@ -118,7 +118,8 @@ public abstract class AbstractResource implements Resource {
|
||||
try {
|
||||
long size = 0;
|
||||
byte[] buf = new byte[255];
|
||||
for (int read = is.read(buf); read != -1;) {
|
||||
int read;
|
||||
while ((read = is.read(buf)) != -1) {
|
||||
size += read;
|
||||
}
|
||||
return size;
|
||||
@@ -169,7 +170,7 @@ public abstract class AbstractResource implements Resource {
|
||||
* This implementation always returns <code>null</code>,
|
||||
* assuming that this resource type does not have a filename.
|
||||
*/
|
||||
public String getFilename() throws IllegalStateException {
|
||||
public String getFilename() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+15
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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.
|
||||
@@ -35,6 +35,7 @@ import org.springframework.util.StringUtils;
|
||||
* Always supports resolution as URL.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 28.12.2003
|
||||
* @see java.lang.ClassLoader#getResourceAsStream(String)
|
||||
* @see java.lang.Class#getResourceAsStream(String)
|
||||
@@ -108,7 +109,6 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the path for this resource (as resource path within the class path).
|
||||
*/
|
||||
@@ -123,7 +123,6 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
||||
return (this.classLoader != null ? this.classLoader : this.clazz.getClassLoader());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This implementation checks for the resolution of a resource URL.
|
||||
* @see java.lang.ClassLoader#getResource(String)
|
||||
@@ -155,8 +154,7 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
||||
is = this.classLoader.getResourceAsStream(this.path);
|
||||
}
|
||||
if (is == null) {
|
||||
throw new FileNotFoundException(
|
||||
getDescription() + " cannot be opened because it does not exist");
|
||||
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
|
||||
}
|
||||
return is;
|
||||
}
|
||||
@@ -176,8 +174,7 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
||||
url = this.classLoader.getResource(this.path);
|
||||
}
|
||||
if (url == null) {
|
||||
throw new FileNotFoundException(
|
||||
getDescription() + " cannot be resolved to URL because it does not exist");
|
||||
throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist");
|
||||
}
|
||||
return url;
|
||||
}
|
||||
@@ -209,17 +206,22 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
||||
public String getDescription() {
|
||||
StringBuilder builder = new StringBuilder("class path resource [");
|
||||
|
||||
if (this.clazz != null) {
|
||||
String pathToUse = path;
|
||||
|
||||
if (this.clazz != null && !pathToUse.startsWith("/")) {
|
||||
builder.append(ClassUtils.classPackageAsResourcePath(this.clazz));
|
||||
builder.append('/');
|
||||
}
|
||||
|
||||
builder.append(this.path);
|
||||
if (pathToUse.startsWith("/")) {
|
||||
pathToUse = pathToUse.substring(1);
|
||||
}
|
||||
|
||||
builder.append(pathToUse);
|
||||
builder.append(']');
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This implementation compares the underlying class path locations.
|
||||
*/
|
||||
@@ -230,9 +232,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;
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
* 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.
|
||||
@@ -47,6 +47,7 @@ public interface InputStreamSource {
|
||||
* as JavaMail, which needs to be able to read the stream multiple times when
|
||||
* creating mail attachments. For such a use case, it is <i>required</i>
|
||||
* that each <code>getInputStream()</code> call returns a fresh stream.
|
||||
* @return the input stream for the underlying resource (must not be {@code null})
|
||||
* @throws IOException if the stream could not be opened
|
||||
* @see org.springframework.mail.javamail.MimeMessageHelper#addAttachment(String, InputStreamSource)
|
||||
*/
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.core.io;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
|
||||
|
||||
+8
-10
@@ -80,18 +80,14 @@ abstract class AbstractRecursiveAnnotationVisitor implements AnnotationVisitor {
|
||||
valueToUse = enumConstant.get(null);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logNonFatalException(ex);
|
||||
catch (ClassNotFoundException ex) {
|
||||
this.logger.debug("Failed to classload enum type while reading annotation metadata", ex);
|
||||
}
|
||||
catch (IllegalAccessException ex) {
|
||||
this.logger.warn("Could not access enum value while reading annotation metadata", ex);
|
||||
}
|
||||
this.attributes.put(attributeName, valueToUse);
|
||||
}
|
||||
|
||||
|
||||
protected void logNonFatalException(Exception ex) {
|
||||
this.logger.warn("Failed to classload type while reading annotation metadata. " +
|
||||
"This is a non-fatal error, but certain annotation metadata may be " +
|
||||
"unavailable.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -168,7 +164,9 @@ class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVi
|
||||
this.doVisitEnd(annotationClass);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
logNonFatalException(ex);
|
||||
this.logger.debug("Failed to classload type while reading annotation " +
|
||||
"metadata. This is a non-fatal error, but certain annotation " +
|
||||
"metadata may be unavailable.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.core.type.classreading;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
@@ -38,15 +39,19 @@ import org.springframework.core.type.ClassMetadata;
|
||||
final class SimpleMetadataReader implements MetadataReader {
|
||||
|
||||
private final Resource resource;
|
||||
|
||||
private final ClassMetadata classMetadata;
|
||||
|
||||
private final AnnotationMetadata annotationMetadata;
|
||||
|
||||
|
||||
SimpleMetadataReader(Resource resource, ClassLoader classLoader) throws IOException {
|
||||
InputStream is = resource.getInputStream();
|
||||
InputStream is = new BufferedInputStream(resource.getInputStream());
|
||||
ClassReader classReader = null;
|
||||
try {
|
||||
classReader = new ClassReader(is);
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
is.close();
|
||||
}
|
||||
|
||||
@@ -70,4 +75,5 @@ final class SimpleMetadataReader implements MetadataReader {
|
||||
public AnnotationMetadata getAnnotationMetadata() {
|
||||
return this.annotationMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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.
|
||||
@@ -170,7 +170,8 @@ public class PropertyPlaceholderHelper {
|
||||
startIndex = buf.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Could not resolve placeholder '" + placeholder + "'");
|
||||
throw new IllegalArgumentException("Could not resolve placeholder '" +
|
||||
placeholder + "'" + " in string value [" + strVal + "]");
|
||||
}
|
||||
|
||||
visitedPlaceholders.remove(placeholder);
|
||||
|
||||
+23
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,18 +16,21 @@
|
||||
|
||||
package org.springframework.core.env;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.env.MockPropertySource;
|
||||
|
||||
public class PropertySourcesTests {
|
||||
import static java.lang.String.*;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.core.env.MutablePropertySources.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MutablePropertySources}
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class MutablePropertySourcesTests {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
MutablePropertySources sources = new MutablePropertySources();
|
||||
@@ -104,7 +107,7 @@ public class PropertySourcesTests {
|
||||
fail("expected non-existent PropertySource exception");
|
||||
} catch (IllegalArgumentException ex) {
|
||||
assertThat(ex.getMessage(),
|
||||
equalTo(String.format(MutablePropertySources.NON_EXISTENT_PROPERTY_SOURCE_MESSAGE, bogusPS)));
|
||||
equalTo(format(NON_EXISTENT_PROPERTY_SOURCE_MESSAGE, bogusPS)));
|
||||
}
|
||||
|
||||
sources.addFirst(new MockPropertySource("a"));
|
||||
@@ -126,7 +129,7 @@ public class PropertySourcesTests {
|
||||
fail("expected non-existent PropertySource exception");
|
||||
} catch (IllegalArgumentException ex) {
|
||||
assertThat(ex.getMessage(),
|
||||
equalTo(String.format(MutablePropertySources.NON_EXISTENT_PROPERTY_SOURCE_MESSAGE, bogusPS)));
|
||||
equalTo(format(NON_EXISTENT_PROPERTY_SOURCE_MESSAGE, bogusPS)));
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -134,7 +137,7 @@ public class PropertySourcesTests {
|
||||
fail("expected exception");
|
||||
} catch (IllegalArgumentException ex) {
|
||||
assertThat(ex.getMessage(),
|
||||
equalTo(String.format(MutablePropertySources.ILLEGAL_RELATIVE_ADDITION_MESSAGE, "b")));
|
||||
equalTo(format(ILLEGAL_RELATIVE_ADDITION_MESSAGE, "b")));
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -142,8 +145,14 @@ public class PropertySourcesTests {
|
||||
fail("expected exception");
|
||||
} catch (IllegalArgumentException ex) {
|
||||
assertThat(ex.getMessage(),
|
||||
equalTo(String.format(MutablePropertySources.ILLEGAL_RELATIVE_ADDITION_MESSAGE, "b")));
|
||||
equalTo(format(ILLEGAL_RELATIVE_ADDITION_MESSAGE, "b")));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNonExistentPropertySourceReturnsNull() {
|
||||
MutablePropertySources sources = new MutablePropertySources();
|
||||
assertThat(sources.get("bogus"), nullValue());
|
||||
}
|
||||
|
||||
}
|
||||
+39
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,22 +16,20 @@
|
||||
|
||||
package org.springframework.core.env;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.convert.ConversionException;
|
||||
import org.springframework.mock.env.MockPropertySource;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link PropertySourcesPropertyResolver}.
|
||||
*
|
||||
@@ -352,6 +350,39 @@ public class PropertySourcesPropertyResolverTests {
|
||||
propertyResolver.validateRequiredProperties();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveNestedPropertyPlaceholders() {
|
||||
MutablePropertySources ps = new MutablePropertySources();
|
||||
ps.addFirst(new MockPropertySource()
|
||||
.withProperty("p1", "v1")
|
||||
.withProperty("p2", "v2")
|
||||
.withProperty("p3", "${p1}:${p2}") // nested placeholders
|
||||
.withProperty("p4", "${p3}") // deeply nested placeholders
|
||||
.withProperty("p5", "${p1}:${p2}:${bogus}") // unresolvable placeholder
|
||||
.withProperty("p6", "${p1}:${p2}:${bogus:def}") // unresolvable w/ default
|
||||
.withProperty("pL", "${pR}") // cyclic reference left
|
||||
.withProperty("pR", "${pL}") // cyclic reference right
|
||||
);
|
||||
PropertySourcesPropertyResolver pr = new PropertySourcesPropertyResolver(ps);
|
||||
assertThat(pr.getProperty("p1"), equalTo("v1"));
|
||||
assertThat(pr.getProperty("p2"), equalTo("v2"));
|
||||
assertThat(pr.getProperty("p3"), equalTo("v1:v2"));
|
||||
assertThat(pr.getProperty("p4"), equalTo("v1:v2"));
|
||||
try {
|
||||
pr.getProperty("p5");
|
||||
} catch (IllegalArgumentException ex) {
|
||||
assertThat(ex.getMessage(), Matchers.containsString(
|
||||
"Could not resolve placeholder 'bogus' in string value [${p1}:${p2}:${bogus}]"));
|
||||
}
|
||||
assertThat(pr.getProperty("p6"), equalTo("v1:v2:def"));
|
||||
try {
|
||||
pr.getProperty("pL");
|
||||
} catch (StackOverflowError ex) {
|
||||
// no explicit handling for cyclic references for now
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static interface SomeType { }
|
||||
static class SpecificType implements SomeType { }
|
||||
}
|
||||
|
||||
Vendored
+52
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,22 +16,6 @@
|
||||
|
||||
package org.springframework.core.env;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.matchers.JUnitMatchers.hasItem;
|
||||
import static org.junit.matchers.JUnitMatchers.hasItems;
|
||||
import static org.springframework.core.env.AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME;
|
||||
import static org.springframework.core.env.AbstractEnvironment.DEFAULT_PROFILES_PROPERTY_NAME;
|
||||
import static org.springframework.core.env.AbstractEnvironment.RESERVED_DEFAULT_PROFILE_NAME;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.security.AccessControlException;
|
||||
import java.security.Permission;
|
||||
@@ -40,8 +24,18 @@ import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.mock.env.MockPropertySource;
|
||||
|
||||
import static java.lang.String.*;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.matchers.JUnitMatchers.*;
|
||||
|
||||
import static org.springframework.core.env.AbstractEnvironment.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link StandardEnvironment}.
|
||||
*
|
||||
@@ -62,6 +56,47 @@ public class StandardEnvironmentTests {
|
||||
|
||||
private ConfigurableEnvironment environment = new StandardEnvironment();
|
||||
|
||||
@Test
|
||||
public void merge() {
|
||||
ConfigurableEnvironment child = new StandardEnvironment();
|
||||
child.setActiveProfiles("c1", "c2");
|
||||
child.getPropertySources().addLast(
|
||||
new MockPropertySource("childMock")
|
||||
.withProperty("childKey", "childVal")
|
||||
.withProperty("bothKey", "childBothVal"));
|
||||
|
||||
ConfigurableEnvironment parent = new StandardEnvironment();
|
||||
parent.setActiveProfiles("p1", "p2");
|
||||
parent.getPropertySources().addLast(
|
||||
new MockPropertySource("parentMock")
|
||||
.withProperty("parentKey", "parentVal")
|
||||
.withProperty("bothKey", "parentBothVal"));
|
||||
|
||||
assertThat(child.getProperty("childKey"), is("childVal"));
|
||||
assertThat(child.getProperty("parentKey"), nullValue());
|
||||
assertThat(child.getProperty("bothKey"), is("childBothVal"));
|
||||
|
||||
assertThat(parent.getProperty("childKey"), nullValue());
|
||||
assertThat(parent.getProperty("parentKey"), is("parentVal"));
|
||||
assertThat(parent.getProperty("bothKey"), is("parentBothVal"));
|
||||
|
||||
assertThat(child.getActiveProfiles(), equalTo(new String[]{"c1","c2"}));
|
||||
assertThat(parent.getActiveProfiles(), equalTo(new String[]{"p1","p2"}));
|
||||
|
||||
child.merge(parent);
|
||||
|
||||
assertThat(child.getProperty("childKey"), is("childVal"));
|
||||
assertThat(child.getProperty("parentKey"), is("parentVal"));
|
||||
assertThat(child.getProperty("bothKey"), is("childBothVal"));
|
||||
|
||||
assertThat(parent.getProperty("childKey"), nullValue());
|
||||
assertThat(parent.getProperty("parentKey"), is("parentVal"));
|
||||
assertThat(parent.getProperty("bothKey"), is("parentBothVal"));
|
||||
|
||||
assertThat(child.getActiveProfiles(), equalTo(new String[]{"c1","c2","p1","p2"}));
|
||||
assertThat(parent.getActiveProfiles(), equalTo(new String[]{"p1","p2"}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertySourceOrder() {
|
||||
ConfigurableEnvironment env = new StandardEnvironment();
|
||||
|
||||
+76
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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.
|
||||
@@ -17,24 +17,59 @@
|
||||
package org.springframework.core.io;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.internal.matchers.StringContains.containsString;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit tests cornering bug SPR-6888.
|
||||
* Unit tests that serve as regression tests for the bugs described in SPR-6888
|
||||
* and SPR-9413.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class ClassPathResourceTests {
|
||||
|
||||
private static final String PACKAGE_PATH = "org/springframework/core/io";
|
||||
private static final String RESOURCE_NAME = "notexist.xml";
|
||||
private static final String FQ_RESOURCE_PATH = PACKAGE_PATH + '/' + RESOURCE_NAME;
|
||||
private static final String NONEXISTENT_RESOURCE_NAME = "nonexistent.xml";
|
||||
private static final String FQ_RESOURCE_PATH = PACKAGE_PATH + '/' + NONEXISTENT_RESOURCE_NAME;
|
||||
|
||||
/**
|
||||
* Absolute path version of {@link #FQ_RESOURCE_PATH}.
|
||||
*/
|
||||
private static final String FQ_RESOURCE_PATH_WITH_LEADING_SLASH = '/' + FQ_RESOURCE_PATH;
|
||||
|
||||
private static final Pattern DESCRIPTION_PATTERN = Pattern.compile("^class path resource \\[(.+?)\\]$");
|
||||
|
||||
|
||||
private void assertDescriptionContainsExpectedPath(ClassPathResource resource, String expectedPath) {
|
||||
Matcher matcher = DESCRIPTION_PATTERN.matcher(resource.getDescription());
|
||||
assertTrue(matcher.matches());
|
||||
assertEquals(1, matcher.groupCount());
|
||||
String match = matcher.group(1);
|
||||
|
||||
assertEquals(expectedPath, match);
|
||||
}
|
||||
|
||||
private void assertExceptionContainsFullyQualifiedPath(ClassPathResource resource) {
|
||||
try {
|
||||
resource.getInputStream();
|
||||
fail("FileNotFoundException expected for resource: " + resource);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
assertThat(ex, instanceOf(FileNotFoundException.class));
|
||||
assertThat(ex.getMessage(), containsString(FQ_RESOURCE_PATH));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stringConstructorRaisesExceptionWithFullyQualifiedPath() {
|
||||
@@ -43,21 +78,48 @@ public class ClassPathResourceTests {
|
||||
|
||||
@Test
|
||||
public void classLiteralConstructorRaisesExceptionWithFullyQualifiedPath() {
|
||||
assertExceptionContainsFullyQualifiedPath(new ClassPathResource(RESOURCE_NAME, this.getClass()));
|
||||
assertExceptionContainsFullyQualifiedPath(new ClassPathResource(NONEXISTENT_RESOURCE_NAME, this.getClass()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void classLoaderConstructorRaisesExceptionWithFullyQualifiedPath() {
|
||||
assertExceptionContainsFullyQualifiedPath(new ClassPathResource(FQ_RESOURCE_PATH, this.getClass().getClassLoader()));
|
||||
assertExceptionContainsFullyQualifiedPath(new ClassPathResource(FQ_RESOURCE_PATH,
|
||||
this.getClass().getClassLoader()));
|
||||
}
|
||||
|
||||
private void assertExceptionContainsFullyQualifiedPath(ClassPathResource resource) {
|
||||
try {
|
||||
resource.getInputStream();
|
||||
fail("FileNotFoundException expected for resource: " + resource);
|
||||
} catch (IOException ex) {
|
||||
assertThat(ex, instanceOf(FileNotFoundException.class));
|
||||
assertThat(ex.getMessage(), containsString(FQ_RESOURCE_PATH));
|
||||
}
|
||||
@Test
|
||||
public void getDescriptionWithStringConstructor() {
|
||||
assertDescriptionContainsExpectedPath(new ClassPathResource(FQ_RESOURCE_PATH), FQ_RESOURCE_PATH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDescriptionWithStringConstructorAndLeadingSlash() {
|
||||
assertDescriptionContainsExpectedPath(new ClassPathResource(FQ_RESOURCE_PATH_WITH_LEADING_SLASH),
|
||||
FQ_RESOURCE_PATH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDescriptionWithClassLiteralConstructor() {
|
||||
assertDescriptionContainsExpectedPath(new ClassPathResource(NONEXISTENT_RESOURCE_NAME, this.getClass()),
|
||||
FQ_RESOURCE_PATH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDescriptionWithClassLiteralConstructorAndLeadingSlash() {
|
||||
assertDescriptionContainsExpectedPath(
|
||||
new ClassPathResource(FQ_RESOURCE_PATH_WITH_LEADING_SLASH, this.getClass()), FQ_RESOURCE_PATH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDescriptionWithClassLoaderConstructor() {
|
||||
assertDescriptionContainsExpectedPath(
|
||||
new ClassPathResource(FQ_RESOURCE_PATH, this.getClass().getClassLoader()), FQ_RESOURCE_PATH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDescriptionWithClassLoaderConstructorAndLeadingSlash() {
|
||||
assertDescriptionContainsExpectedPath(new ClassPathResource(FQ_RESOURCE_PATH_WITH_LEADING_SLASH,
|
||||
this.getClass().getClassLoader()), FQ_RESOURCE_PATH);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+19
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
package org.springframework.core.io;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
@@ -30,8 +27,12 @@ import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 09.09.2004
|
||||
*/
|
||||
public class ResourceTests {
|
||||
@@ -174,13 +175,11 @@ public class ResourceTests {
|
||||
assertEquals(new UrlResource("file:dir/subdir"), relative);
|
||||
}
|
||||
|
||||
/*
|
||||
* @Test
|
||||
@Ignore @Test // this test is quite slow. TODO: re-enable with JUnit categories
|
||||
public void testNonFileResourceExists() throws Exception {
|
||||
Resource resource = new UrlResource("http://www.springframework.org");
|
||||
assertTrue(resource.exists());
|
||||
}
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testAbstractResourceExceptions() throws Exception {
|
||||
@@ -220,4 +219,17 @@ public class ResourceTests {
|
||||
assertThat(resource.getFilename(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContentLength() throws IOException {
|
||||
AbstractResource resource = new AbstractResource() {
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return new ByteArrayInputStream(new byte[] { 'a', 'b', 'c' });
|
||||
}
|
||||
public String getDescription() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
assertThat(resource.contentLength(), is(3L));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-expression</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-instrument-tomcat</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-instrument</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-integration-tests</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+16
-12
@@ -99,6 +99,7 @@ public class EnvironmentIntegrationTests {
|
||||
|
||||
private ConfigurableEnvironment prodEnv;
|
||||
private ConfigurableEnvironment devEnv;
|
||||
private ConfigurableEnvironment prodWebEnv;
|
||||
|
||||
/**
|
||||
* Constants used both locally and in scan* sub-packages
|
||||
@@ -125,6 +126,9 @@ public class EnvironmentIntegrationTests {
|
||||
|
||||
devEnv = new StandardEnvironment();
|
||||
devEnv.setActiveProfiles(DEV_ENV_NAME);
|
||||
|
||||
prodWebEnv = new StandardServletEnvironment();
|
||||
prodWebEnv.setActiveProfiles(PROD_ENV_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -348,24 +352,24 @@ public class EnvironmentIntegrationTests {
|
||||
|
||||
assertHasStandardServletEnvironment(ctx);
|
||||
|
||||
ctx.setEnvironment(prodEnv);
|
||||
ctx.setEnvironment(prodWebEnv);
|
||||
ctx.refresh();
|
||||
|
||||
assertHasEnvironment(ctx, prodEnv);
|
||||
assertHasEnvironment(ctx, prodWebEnv);
|
||||
assertEnvironmentBeanRegistered(ctx);
|
||||
assertEnvironmentAwareInvoked(ctx, prodEnv);
|
||||
assertEnvironmentAwareInvoked(ctx, prodWebEnv);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void xmlWebApplicationContext() {
|
||||
AbstractRefreshableWebApplicationContext ctx = new XmlWebApplicationContext();
|
||||
ctx.setConfigLocation("classpath:" + XML_PATH);
|
||||
ctx.setEnvironment(prodEnv);
|
||||
ctx.setEnvironment(prodWebEnv);
|
||||
ctx.refresh();
|
||||
|
||||
assertHasEnvironment(ctx, prodEnv);
|
||||
assertHasEnvironment(ctx, prodWebEnv);
|
||||
assertEnvironmentBeanRegistered(ctx);
|
||||
assertEnvironmentAwareInvoked(ctx, prodEnv);
|
||||
assertEnvironmentAwareInvoked(ctx, prodWebEnv);
|
||||
assertThat(ctx.containsBean(DEV_BEAN_NAME), is(false));
|
||||
assertThat(ctx.containsBean(PROD_BEAN_NAME), is(true));
|
||||
}
|
||||
@@ -394,24 +398,24 @@ public class EnvironmentIntegrationTests {
|
||||
|
||||
registerEnvironmentBeanDefinition(ctx);
|
||||
|
||||
ctx.setEnvironment(prodEnv);
|
||||
ctx.setEnvironment(prodWebEnv);
|
||||
ctx.refresh();
|
||||
|
||||
assertHasEnvironment(ctx, prodEnv);
|
||||
assertHasEnvironment(ctx, prodWebEnv);
|
||||
assertEnvironmentBeanRegistered(ctx);
|
||||
assertEnvironmentAwareInvoked(ctx, prodEnv);
|
||||
assertEnvironmentAwareInvoked(ctx, prodWebEnv);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void annotationConfigWebApplicationContext() {
|
||||
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
|
||||
ctx.setEnvironment(prodEnv);
|
||||
ctx.setEnvironment(prodWebEnv);
|
||||
ctx.setConfigLocation(EnvironmentAwareBean.class.getName());
|
||||
ctx.refresh();
|
||||
|
||||
assertHasEnvironment(ctx, prodEnv);
|
||||
assertHasEnvironment(ctx, prodWebEnv);
|
||||
assertEnvironmentBeanRegistered(ctx);
|
||||
assertEnvironmentAwareInvoked(ctx, prodEnv);
|
||||
assertEnvironmentAwareInvoked(ctx, prodWebEnv);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-jdbc</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* 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.
|
||||
@@ -235,10 +235,10 @@ public class PreparedStatementCreatorFactory {
|
||||
ps = con.prepareStatement(this.actualSql, PreparedStatement.RETURN_GENERATED_KEYS);
|
||||
}
|
||||
}
|
||||
catch (AbstractMethodError ex) {
|
||||
catch (AbstractMethodError err) {
|
||||
throw new InvalidDataAccessResourceUsageException(
|
||||
"The JDBC driver is not compliant to JDBC 3.0 and thus " +
|
||||
"does not support retrieval of auto-generated keys", ex);
|
||||
"Your JDBC driver is not compliant with JDBC 3.0 - " +
|
||||
"it does not support retrieval of auto-generated keys", err);
|
||||
}
|
||||
}
|
||||
else if (resultSetType == ResultSet.TYPE_FORWARD_ONLY && !updatableResults) {
|
||||
|
||||
+27
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* 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.
|
||||
@@ -22,6 +22,7 @@ import java.math.BigInteger;
|
||||
import java.sql.Blob;
|
||||
import java.sql.Clob;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.ParameterMetaData;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
@@ -189,7 +190,7 @@ public abstract class StatementCreatorUtils {
|
||||
if (inValue instanceof SqlParameterValue) {
|
||||
SqlParameterValue parameterValue = (SqlParameterValue) inValue;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Overriding typeinfo with runtime info from SqlParameterValue: column index " + paramIndex +
|
||||
logger.debug("Overriding type info with runtime info from SqlParameterValue: column index " + paramIndex +
|
||||
", SQL type " + parameterValue.getSqlType() +
|
||||
", Type name " + parameterValue.getTypeName());
|
||||
}
|
||||
@@ -228,18 +229,31 @@ public abstract class StatementCreatorUtils {
|
||||
boolean useSetObject = false;
|
||||
sqlType = Types.NULL;
|
||||
try {
|
||||
DatabaseMetaData dbmd = ps.getConnection().getMetaData();
|
||||
String databaseProductName = dbmd.getDatabaseProductName();
|
||||
String jdbcDriverName = dbmd.getDriverName();
|
||||
if (databaseProductName.startsWith("Informix") ||
|
||||
jdbcDriverName.startsWith("Microsoft SQL Server")) {
|
||||
useSetObject = true;
|
||||
ParameterMetaData pmd = null;
|
||||
try {
|
||||
pmd = ps.getParameterMetaData();
|
||||
}
|
||||
else if (databaseProductName.startsWith("DB2") ||
|
||||
jdbcDriverName.startsWith("jConnect") ||
|
||||
jdbcDriverName.startsWith("SQLServer")||
|
||||
jdbcDriverName.startsWith("Apache Derby")) {
|
||||
sqlType = Types.VARCHAR;
|
||||
catch (AbstractMethodError err) {
|
||||
// JDBC driver not compliant with JDBC 3.0
|
||||
// -> proceed with database-specific checks
|
||||
}
|
||||
if (pmd != null) {
|
||||
sqlType = pmd.getParameterType(paramIndex);
|
||||
}
|
||||
else {
|
||||
DatabaseMetaData dbmd = ps.getConnection().getMetaData();
|
||||
String databaseProductName = dbmd.getDatabaseProductName();
|
||||
String jdbcDriverName = dbmd.getDriverName();
|
||||
if (databaseProductName.startsWith("Informix") ||
|
||||
jdbcDriverName.startsWith("Microsoft SQL Server")) {
|
||||
useSetObject = true;
|
||||
}
|
||||
else if (databaseProductName.startsWith("DB2") ||
|
||||
jdbcDriverName.startsWith("jConnect") ||
|
||||
jdbcDriverName.startsWith("SQLServer")||
|
||||
jdbcDriverName.startsWith("Apache Derby")) {
|
||||
sqlType = Types.VARCHAR;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
|
||||
+7
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -17,16 +17,18 @@
|
||||
package org.springframework.jdbc.datasource.init;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.jdbc.datasource.DataSourceUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility methods for executing a DatabasePopulator.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Oliver Gierke
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class DatabasePopulatorUtils {
|
||||
@@ -40,16 +42,13 @@ public abstract class DatabasePopulatorUtils {
|
||||
Assert.notNull(populator, "DatabasePopulator must be provided");
|
||||
Assert.notNull(dataSource, "DataSource must be provided");
|
||||
try {
|
||||
Connection connection = dataSource.getConnection();
|
||||
Connection connection = DataSourceUtils.getConnection(dataSource);
|
||||
try {
|
||||
populator.populate(connection);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
connection.close();
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
// ignore
|
||||
if (connection != null) {
|
||||
DataSourceUtils.releaseConnection(connection, dataSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* 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.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.jdbc.core;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.ParameterMetaData;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
@@ -65,11 +66,27 @@ public class StatementCreatorUtilsTests extends TestCase {
|
||||
StatementCreatorUtils.setParameterValue(ps, 1, SqlTypeValue.TYPE_UNKNOWN, null, null);
|
||||
}
|
||||
|
||||
public void testSetParameterValueWithNullAndUnknownTypeAndJdbc30Driver() throws SQLException {
|
||||
MockControl pmdControl = MockControl.createControl(ParameterMetaData.class);
|
||||
ParameterMetaData pmd = (ParameterMetaData) pmdControl.getMock();
|
||||
ps.getParameterMetaData();
|
||||
psControl.setReturnValue(pmd, 1);
|
||||
pmd.getParameterType(1);
|
||||
pmdControl.setReturnValue(Types.INTEGER, 1);
|
||||
ps.setNull(1, Types.INTEGER);
|
||||
psControl.setVoidCallable(1);
|
||||
psControl.replay();
|
||||
pmdControl.replay();
|
||||
StatementCreatorUtils.setParameterValue(ps, 1, SqlTypeValue.TYPE_UNKNOWN, null, null);
|
||||
}
|
||||
|
||||
public void testSetParameterValueWithNullAndUnknownTypeOnInformix() throws SQLException {
|
||||
MockControl conControl = MockControl.createControl(Connection.class);
|
||||
Connection con = (Connection) conControl.getMock();
|
||||
MockControl metaDataControl = MockControl.createControl(DatabaseMetaData.class);
|
||||
DatabaseMetaData metaData = (DatabaseMetaData) metaDataControl.getMock();
|
||||
ps.getParameterMetaData();
|
||||
psControl.setReturnValue(null, 1);
|
||||
ps.getConnection();
|
||||
psControl.setReturnValue(con, 1);
|
||||
con.getMetaData();
|
||||
@@ -93,6 +110,8 @@ public class StatementCreatorUtilsTests extends TestCase {
|
||||
Connection con = (Connection) conControl.getMock();
|
||||
MockControl metaDataControl = MockControl.createControl(DatabaseMetaData.class);
|
||||
DatabaseMetaData metaData = (DatabaseMetaData) metaDataControl.getMock();
|
||||
ps.getParameterMetaData();
|
||||
psControl.setReturnValue(null, 1);
|
||||
ps.getConnection();
|
||||
psControl.setReturnValue(con, 1);
|
||||
con.getMetaData();
|
||||
|
||||
+32
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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.
|
||||
@@ -19,17 +19,24 @@ package org.springframework.jdbc.datasource.init;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.easymock.EasyMock;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.io.ClassRelativeResourceLoader;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.DataSourceUtils;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Sam Brannen
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class DatabasePopulatorTests {
|
||||
|
||||
@@ -54,6 +61,12 @@ public class DatabasePopulatorTests {
|
||||
|
||||
@After
|
||||
public void shutDown() {
|
||||
|
||||
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
TransactionSynchronizationManager.clear();
|
||||
TransactionSynchronizationManager.unbindResource(db);
|
||||
}
|
||||
|
||||
db.shutdown();
|
||||
}
|
||||
|
||||
@@ -207,4 +220,22 @@ public class DatabasePopulatorTests {
|
||||
assertEquals(1, jdbcTemplate.queryForInt("select COUNT(NAME) from T_TEST where NAME='Dave'"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see SPR-9457
|
||||
*/
|
||||
@Test
|
||||
public void usesBoundConnectionIfAvailable() throws SQLException {
|
||||
|
||||
TransactionSynchronizationManager.initSynchronization();
|
||||
Connection connection = DataSourceUtils.getConnection(db);
|
||||
|
||||
DatabasePopulator populator = EasyMock.createMock(DatabasePopulator.class);
|
||||
populator.populate(connection);
|
||||
EasyMock.expectLastCall();
|
||||
EasyMock.replay(populator);
|
||||
|
||||
DatabasePopulatorUtils.execute(populator, db);
|
||||
|
||||
EasyMock.verify(populator);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-jms</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-orm</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.hibernate4;
|
||||
|
||||
import javax.transaction.TransactionManager;
|
||||
import javax.transaction.UserTransaction;
|
||||
|
||||
import org.hibernate.service.jta.platform.internal.AbstractJtaPlatform;
|
||||
|
||||
import org.springframework.transaction.jta.UserTransactionAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of Hibernate 4's {@link org.hibernate.service.jta.platform.spi.JtaPlatform}
|
||||
* SPI, exposing passed-in {@link TransactionManager} and {@link UserTransaction} references.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1.2
|
||||
*/
|
||||
class ConfigurableJtaPlatform extends AbstractJtaPlatform {
|
||||
|
||||
private final TransactionManager transactionManager;
|
||||
|
||||
private final UserTransaction userTransaction;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ConfigurableJtaPlatform instance with the given
|
||||
* JTA TransactionManager and optionally a given UserTransaction.
|
||||
* @param tm the JTA TransactionManager reference (required)
|
||||
* @param ut the JTA UserTransaction reference (optional)
|
||||
*/
|
||||
public ConfigurableJtaPlatform(TransactionManager tm, UserTransaction ut) {
|
||||
Assert.notNull(tm, "TransactionManager reference must not be null");
|
||||
this.transactionManager = tm;
|
||||
this.userTransaction = (ut != null ? ut : new UserTransactionAdapter(tm));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected TransactionManager locateTransactionManager() {
|
||||
return this.transactionManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UserTransaction locateUserTransaction() {
|
||||
return this.userTransaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canCacheTransactionManager() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canCacheUserTransaction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+23
-1
@@ -48,6 +48,12 @@ import org.springframework.core.io.support.ResourcePatternUtils;
|
||||
* However, in practice, it is closer to <code>AnnotationSessionFactoryBean</code> since
|
||||
* its core purpose is to bootstrap a <code>SessionFactory</code> from annotation scanning.
|
||||
*
|
||||
* <p><b>NOTE:</b> To set up Hibernate 4 for Spring-driven JTA transactions, make
|
||||
* sure to either specify the {@link #setJtaTransactionManager "jtaTransactionManager"}
|
||||
* bean property or to set the "hibernate.transaction.factory_class" property to
|
||||
* {@link org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory}.
|
||||
* Otherwise, Hibernate's smart flushing mechanism won't work properly.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see #setDataSource
|
||||
@@ -82,6 +88,8 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
|
||||
|
||||
private String[] packagesToScan;
|
||||
|
||||
private Object jtaTransactionManager;
|
||||
|
||||
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
|
||||
|
||||
private Configuration configuration;
|
||||
@@ -226,7 +234,7 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
|
||||
|
||||
/**
|
||||
* Specify annotated entity classes to register with this Hibernate SessionFactory.
|
||||
* @see org.hibernate.cfg.Configuration#addAnnotatedClass(String)
|
||||
* @see org.hibernate.cfg.Configuration#addAnnotatedClass(Class)
|
||||
*/
|
||||
public void setAnnotatedClasses(Class<?>[] annotatedClasses) {
|
||||
this.annotatedClasses = annotatedClasses;
|
||||
@@ -250,6 +258,16 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
|
||||
this.packagesToScan = packagesToScan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Spring {@link org.springframework.transaction.jta.JtaTransactionManager}
|
||||
* or the JTA {@link javax.transaction.TransactionManager} to be used with Hibernate,
|
||||
* if any.
|
||||
* @see LocalSessionFactoryBuilder#setJtaTransactionManager
|
||||
*/
|
||||
public void setJtaTransactionManager(Object jtaTransactionManager) {
|
||||
this.jtaTransactionManager = jtaTransactionManager;
|
||||
}
|
||||
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
|
||||
}
|
||||
@@ -330,6 +348,10 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
|
||||
sfb.scanPackages(this.packagesToScan);
|
||||
}
|
||||
|
||||
if (this.jtaTransactionManager != null) {
|
||||
sfb.setJtaTransactionManager(this.jtaTransactionManager);
|
||||
}
|
||||
|
||||
// Build SessionFactory instance.
|
||||
this.configuration = sfb;
|
||||
this.sessionFactory = buildSessionFactory(sfb);
|
||||
|
||||
+56
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -22,12 +22,16 @@ import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.sql.DataSource;
|
||||
import javax.transaction.TransactionManager;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory;
|
||||
import org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
@@ -39,6 +43,8 @@ import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.transaction.jta.JtaTransactionManager;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
@@ -50,6 +56,11 @@ import org.springframework.util.ReflectionUtils;
|
||||
* <p>This is designed for programmatic use, e.g. in {@code @Bean} factory methods.
|
||||
* Consider using {@link LocalSessionFactoryBean} for XML bean definition files.
|
||||
*
|
||||
* <p><b>NOTE:</b> To set up Hibernate 4 for Spring-driven JTA transactions, make
|
||||
* sure to either use the {@link #setJtaTransactionManager} method or to set the
|
||||
* "hibernate.transaction.factory_class" property to {@link CMTTransactionFactory}.
|
||||
* Otherwise, Hibernate's smart flushing mechanism won't work properly.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see LocalSessionFactoryBean
|
||||
@@ -97,18 +108,60 @@ public class LocalSessionFactoryBuilder extends Configuration {
|
||||
* Create a new LocalSessionFactoryBuilder for the given DataSource.
|
||||
* @param dataSource the JDBC DataSource that the resulting Hibernate SessionFactory should be using
|
||||
* (may be <code>null</code>)
|
||||
* @param classLoader the ResourceLoader to load application classes from
|
||||
* @param resourceLoader the ResourceLoader to load application classes from
|
||||
*/
|
||||
public LocalSessionFactoryBuilder(DataSource dataSource, ResourceLoader resourceLoader) {
|
||||
getProperties().put(Environment.CURRENT_SESSION_CONTEXT_CLASS, SpringSessionContext.class.getName());
|
||||
if (dataSource != null) {
|
||||
getProperties().put(Environment.DATASOURCE, dataSource);
|
||||
}
|
||||
getProperties().put("hibernate.classLoader.application", resourceLoader.getClassLoader());
|
||||
getProperties().put(AvailableSettings.APP_CLASSLOADER, resourceLoader.getClassLoader());
|
||||
this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the Spring {@link JtaTransactionManager} or the JTA {@link TransactionManager}
|
||||
* to be used with Hibernate, if any. Allows for using a Spring-managed transaction
|
||||
* manager for Hibernate 4's session and cache synchronization, with the
|
||||
* "hibernate.transaction.jta.platform" automatically set to it. Also sets
|
||||
* "hibernate.transaction.factory_class" to {@link CMTTransactionFactory},
|
||||
* instructing Hibernate to interact with externally managed transactions.
|
||||
* <p>A passed-in Spring {@link JtaTransactionManager} needs to contain a JTA
|
||||
* {@link TransactionManager} reference to be usable here, except for the WebSphere
|
||||
* case where we'll automatically set {@link WebSphereExtendedJtaPlatform} accordingly.
|
||||
* <p>Note: If this is set, the Hibernate settings should not contain a JTA platform
|
||||
* setting to avoid meaningless double configuration.
|
||||
*/
|
||||
public LocalSessionFactoryBuilder setJtaTransactionManager(Object jtaTransactionManager) {
|
||||
Assert.notNull(jtaTransactionManager, "Transaction manager reference must not be null");
|
||||
if (jtaTransactionManager instanceof JtaTransactionManager) {
|
||||
boolean webspherePresent = ClassUtils.isPresent("com.ibm.wsspi.uow.UOWManager", getClass().getClassLoader());
|
||||
if (webspherePresent) {
|
||||
getProperties().put(AvailableSettings.JTA_PLATFORM, new WebSphereExtendedJtaPlatform());
|
||||
}
|
||||
else {
|
||||
JtaTransactionManager jtaTm = (JtaTransactionManager) jtaTransactionManager;
|
||||
if (jtaTm.getTransactionManager() == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Can only apply JtaTransactionManager which has a TransactionManager reference set");
|
||||
}
|
||||
getProperties().put(AvailableSettings.JTA_PLATFORM,
|
||||
new ConfigurableJtaPlatform(jtaTm.getTransactionManager(), jtaTm.getUserTransaction()));
|
||||
}
|
||||
}
|
||||
else if (jtaTransactionManager instanceof TransactionManager) {
|
||||
getProperties().put(AvailableSettings.JTA_PLATFORM,
|
||||
new ConfigurableJtaPlatform((TransactionManager) jtaTransactionManager, null));
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown transaction manager type: " + jtaTransactionManager.getClass().getName());
|
||||
}
|
||||
getProperties().put(AvailableSettings.TRANSACTION_STRATEGY, new CMTTransactionFactory());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given annotated classes in a batch.
|
||||
* @see #addAnnotatedClass
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-oxm</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+27
-18
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -202,7 +202,7 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin
|
||||
* @param aliases
|
||||
* @throws ClassNotFoundException
|
||||
* @throws NoSuchFieldException
|
||||
* @see XStream#aliasField(String, Class, String)
|
||||
* @see XStream#aliasField(String, Class, String)
|
||||
*/
|
||||
public void setFieldAliases(Map<String, String> aliases) throws ClassNotFoundException, NoSuchFieldException {
|
||||
for (Map.Entry<String, String> entry : aliases.entrySet()) {
|
||||
@@ -483,7 +483,12 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin
|
||||
else {
|
||||
throw new IllegalArgumentException("DOMSource contains neither Document nor Element");
|
||||
}
|
||||
return unmarshal(streamReader);
|
||||
try {
|
||||
return getXStream().unmarshal(streamReader);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw convertXStreamException(ex, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -499,7 +504,14 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin
|
||||
|
||||
@Override
|
||||
protected Object unmarshalXmlStreamReader(XMLStreamReader streamReader) throws XmlMappingException {
|
||||
return unmarshal(new StaxReader(new QNameMap(), streamReader));
|
||||
try {
|
||||
HierarchicalStreamReader hierarchicalStreamReader =
|
||||
new StaxReader(new QNameMap(),streamReader);
|
||||
return getXStream().unmarshal(hierarchicalStreamReader);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw convertXStreamException(ex, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -509,11 +521,18 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin
|
||||
|
||||
@Override
|
||||
protected Object unmarshalReader(Reader reader) throws XmlMappingException, IOException {
|
||||
if (streamDriver != null) {
|
||||
return unmarshal(streamDriver.createReader(reader));
|
||||
try {
|
||||
HierarchicalStreamReader streamReader;
|
||||
if (this.streamDriver != null) {
|
||||
streamReader = this.streamDriver.createReader(reader);
|
||||
}
|
||||
else {
|
||||
streamReader = new XppReader(reader);
|
||||
}
|
||||
return getXStream().unmarshal(streamReader);
|
||||
}
|
||||
else {
|
||||
return unmarshal(new XppReader(reader));
|
||||
catch (Exception ex) {
|
||||
throw convertXStreamException(ex, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,16 +544,6 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin
|
||||
"XStreamMarshaller does not support unmarshalling using SAX XMLReaders");
|
||||
}
|
||||
|
||||
private Object unmarshal(HierarchicalStreamReader streamReader) {
|
||||
try {
|
||||
return this.getXStream().unmarshal(streamReader);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw convertXStreamException(ex, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert the given XStream exception to an appropriate exception from the
|
||||
* <code>org.springframework.oxm</code> hierarchy.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-library</artifactId>
|
||||
<packaging>libd</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<name>Spring Framework</name>
|
||||
<description>Spring is a layered Java/J2EE application platform, based on code published in Expert
|
||||
One-on-One J2EE Design and Development by Rod Johnson (Wrox, 2002). </description>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>Spring Framework - Parent</name>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<description>Spring Framework Parent</description>
|
||||
<scm>
|
||||
<url>https://fisheye.springframework.org/browse/spring-framework</url>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -19,6 +19,7 @@ package org.springframework.test.context.transaction;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
@@ -29,6 +30,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.test.annotation.NotTransactional;
|
||||
import org.springframework.test.annotation.Rollback;
|
||||
@@ -40,7 +42,6 @@ import org.springframework.transaction.TransactionException;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
|
||||
import org.springframework.transaction.interceptor.DelegatingTransactionAttribute;
|
||||
import org.springframework.transaction.interceptor.TransactionAspectUtils;
|
||||
import org.springframework.transaction.interceptor.TransactionAttribute;
|
||||
import org.springframework.transaction.interceptor.TransactionAttributeSource;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -154,7 +155,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
|
||||
// qualifier matching (only exposed on the internal BeanFactory,
|
||||
// not on the ApplicationContext).
|
||||
BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory();
|
||||
tm = TransactionAspectUtils.getTransactionManager(bf, qualifier);
|
||||
tm = BeanFactoryAnnotationUtils.qualifiedBeanOfType(bf, PlatformTransactionManager.class, qualifier);
|
||||
}
|
||||
else {
|
||||
tm = getTransactionManager(testContext);
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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.
|
||||
@@ -27,6 +27,7 @@ import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
|
||||
import org.springframework.core.NamedThreadLocal;
|
||||
import org.springframework.transaction.NoTransactionException;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
@@ -242,7 +243,7 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init
|
||||
}
|
||||
String qualifier = txAttr.getQualifier();
|
||||
if (StringUtils.hasLength(qualifier)) {
|
||||
return TransactionAspectUtils.getTransactionManager(this.beanFactory, qualifier);
|
||||
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.beanFactory, PlatformTransactionManager.class, qualifier);
|
||||
}
|
||||
else if (this.transactionManagerBeanName != null) {
|
||||
return this.beanFactory.getBean(this.transactionManagerBeanName, PlatformTransactionManager.class);
|
||||
|
||||
+21
-93
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,120 +16,48 @@
|
||||
|
||||
package org.springframework.transaction.interceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Utility methods for obtaining a PlatformTransactionManager by
|
||||
* {@link TransactionAttribute#getQualifier() qualifier value}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 3.0.2
|
||||
* @deprecated as of Spring 3.1.2 in favor of {@link BeanFactoryUtils}
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class TransactionAspectUtils {
|
||||
|
||||
/**
|
||||
* Obtain a PlatformTransactionManager from the given BeanFactory,
|
||||
* matching the given qualifier.
|
||||
* @param beanFactory the BeanFactory to get the PlatformTransactionManager bean from
|
||||
* @param qualifier the qualifier for selecting between multiple PlatformTransactionManager matches
|
||||
* @return the chosen PlatformTransactionManager (never <code>null</code>)
|
||||
* @throws IllegalStateException if no matching PlatformTransactionManager bean found
|
||||
* Obtain a PlatformTransactionManager from the given BeanFactory, matching the given qualifier.
|
||||
* @param beanFactory the BeanFactory to get the {@code PlatformTransactionManager} bean from
|
||||
* @param qualifier the qualifier for selecting between multiple {@code PlatformTransactionManager} matches
|
||||
* @return the chosen {@code PlatformTransactionManager} (never {@code null})
|
||||
* @throws IllegalStateException if no matching {@code PlatformTransactionManager} bean found
|
||||
* @deprecated as of Spring 3.1.2 in favor of
|
||||
* {@link BeanFactoryAnnotationUtils#qualifiedBeanOfType(BeanFactory, Class, String)}
|
||||
*/
|
||||
public static PlatformTransactionManager getTransactionManager(BeanFactory beanFactory, String qualifier) {
|
||||
if (beanFactory instanceof ConfigurableListableBeanFactory) {
|
||||
// Full qualifier matching supported.
|
||||
return getTransactionManager((ConfigurableListableBeanFactory) beanFactory, qualifier);
|
||||
}
|
||||
else if (beanFactory.containsBean(qualifier)) {
|
||||
// Fallback: PlatformTransactionManager at least found by bean name.
|
||||
return beanFactory.getBean(qualifier, PlatformTransactionManager.class);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("No matching PlatformTransactionManager bean found for bean name '" +
|
||||
qualifier + "'! (Note: Qualifier matching not supported because given BeanFactory does not " +
|
||||
"implement ConfigurableListableBeanFactory.)");
|
||||
}
|
||||
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(beanFactory, PlatformTransactionManager.class, qualifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a PlatformTransactionManager from the given BeanFactory,
|
||||
* matching the given qualifier.
|
||||
* @param bf the BeanFactory to get the PlatformTransactionManager bean from
|
||||
* @param qualifier the qualifier for selecting between multiple PlatformTransactionManager matches
|
||||
* @return the chosen PlatformTransactionManager (never <code>null</code>)
|
||||
* @throws IllegalStateException if no matching PlatformTransactionManager bean found
|
||||
* Obtain a PlatformTransactionManager from the given BeanFactory, matching the given qualifier.
|
||||
* @param bf the BeanFactory to get the {@code PlatformTransactionManager} bean from
|
||||
* @param qualifier the qualifier for selecting between multiple {@code PlatformTransactionManager} matches
|
||||
* @return the chosen {@code PlatformTransactionManager} (never {@code null})
|
||||
* @throws IllegalStateException if no matching {@code PlatformTransactionManager} bean found
|
||||
* @deprecated as of Spring 3.1.2 in favor of
|
||||
* {@link BeanFactoryAnnotationUtils#qualifiedBeanOfType(BeanFactory, Class, String)}
|
||||
*/
|
||||
public static PlatformTransactionManager getTransactionManager(ConfigurableListableBeanFactory bf, String qualifier) {
|
||||
Map<String, PlatformTransactionManager> tms =
|
||||
BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, PlatformTransactionManager.class);
|
||||
PlatformTransactionManager chosen = null;
|
||||
for (String beanName : tms.keySet()) {
|
||||
if (isQualifierMatch(qualifier, beanName, bf)) {
|
||||
if (chosen != null) {
|
||||
throw new IllegalStateException("No unique PlatformTransactionManager bean found " +
|
||||
"for qualifier '" + qualifier + "'");
|
||||
}
|
||||
chosen = tms.get(beanName);
|
||||
}
|
||||
}
|
||||
if (chosen != null) {
|
||||
return chosen;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("No matching PlatformTransactionManager bean found for qualifier '" +
|
||||
qualifier + "' - neither qualifier match nor bean name match!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether we have a qualifier match for the given candidate bean.
|
||||
* @param qualifier the qualifier that we are looking for
|
||||
* @param beanName the name of the candidate bean
|
||||
* @param bf the BeanFactory to get the bean definition from
|
||||
* @return <code>true</code> if either the bean definition (in the XML case)
|
||||
* or the bean's factory method (in the @Bean case) defines a matching qualifier
|
||||
* value (through <qualifier<> or @Qualifier)
|
||||
*/
|
||||
private static boolean isQualifierMatch(String qualifier, String beanName, ConfigurableListableBeanFactory bf) {
|
||||
if (bf.containsBean(beanName)) {
|
||||
try {
|
||||
BeanDefinition bd = bf.getMergedBeanDefinition(beanName);
|
||||
if (bd instanceof AbstractBeanDefinition) {
|
||||
AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
|
||||
AutowireCandidateQualifier candidate = abd.getQualifier(Qualifier.class.getName());
|
||||
if ((candidate != null && qualifier.equals(candidate.getAttribute(AutowireCandidateQualifier.VALUE_KEY))) ||
|
||||
qualifier.equals(beanName) || ObjectUtils.containsElement(bf.getAliases(beanName), qualifier)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (bd instanceof RootBeanDefinition) {
|
||||
Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod();
|
||||
if (factoryMethod != null) {
|
||||
Qualifier targetAnnotation = factoryMethod.getAnnotation(Qualifier.class);
|
||||
if (targetAnnotation != null && qualifier.equals(targetAnnotation.value())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// ignore - can't compare qualifiers for a manually registered singleton object
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(bf, PlatformTransactionManager.class, qualifier);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc-portlet</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+51
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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.
|
||||
@@ -123,7 +123,7 @@ import org.springframework.web.servlet.ViewResolver;
|
||||
* as loaded by {@link org.springframework.web.context.ContextLoaderListener},
|
||||
* if any, will be shared.
|
||||
*
|
||||
* <p>Thanks to Rainer Schmitz and Nick Lothian for their suggestions!
|
||||
* <p>Thanks to Rainer Schmitz, Nick Lothian and Eric Dalquist for their suggestions!
|
||||
*
|
||||
* @author William G. Thompson, Jr.
|
||||
* @author John A. Lewis
|
||||
@@ -241,6 +241,12 @@ public class DispatcherPortlet extends FrameworkPortlet {
|
||||
/** Detect all ViewResolvers or just expect "viewResolver" bean? */
|
||||
private boolean detectAllViewResolvers = true;
|
||||
|
||||
/** Whether exceptions thrown during doAction should be forwarded to doRender */
|
||||
private boolean forwardActionException = true;
|
||||
|
||||
/** Whether exceptions thrown during doEvent should be forwarded to doRender */
|
||||
private boolean forwardEventException = false;
|
||||
|
||||
/** URL that points to the ViewRendererServlet */
|
||||
private String viewRendererUrl = DEFAULT_VIEW_RENDERER_URL;
|
||||
|
||||
@@ -305,6 +311,28 @@ public class DispatcherPortlet extends FrameworkPortlet {
|
||||
this.detectAllViewResolvers = detectAllViewResolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to forward exceptions thrown during the action phase
|
||||
* to the render phase via a session attribute.
|
||||
* <p>Default is true. Turn this off if you want the portlet container
|
||||
* to provide immediate exception handling for action requests.
|
||||
* @see #exposeActionException(javax.portlet.PortletRequest, javax.portlet.StateAwareResponse, Exception)
|
||||
*/
|
||||
public void setForwardActionException(boolean forwardActionException) {
|
||||
this.forwardActionException = forwardActionException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to forward exceptions thrown during the event phase
|
||||
* to the render phase via a session attribute.
|
||||
* <p>Default is false. Turn this on if you want the {@link DispatcherPortlet}
|
||||
* to forward the exception to the render phase, similar to what it does
|
||||
* for {@link #setForwardActionException action exceptions} by default.
|
||||
*/
|
||||
public void setForwardEventException(boolean forwardEventException) {
|
||||
this.forwardEventException = forwardEventException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the URL to the ViewRendererServlet. That servlet is used to
|
||||
* ultimately render all views in the portlet application.
|
||||
@@ -648,12 +676,17 @@ public class DispatcherPortlet extends FrameworkPortlet {
|
||||
// Trigger after-completion for thrown exception.
|
||||
triggerAfterActionCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
|
||||
// Forward the exception to the render phase to be displayed.
|
||||
try {
|
||||
exposeActionException(request, response, ex);
|
||||
logger.debug("Caught exception during action phase - forwarding to render phase", ex);
|
||||
if (this.forwardActionException) {
|
||||
try {
|
||||
exposeActionException(request, response, ex);
|
||||
logger.debug("Caught exception during action phase - forwarding to render phase", ex);
|
||||
}
|
||||
catch (IllegalStateException ex2) {
|
||||
// Probably sendRedirect called... need to rethrow exception immediately.
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
catch (IllegalStateException ex2) {
|
||||
// Probably sendRedirect called... need to rethrow exception immediately.
|
||||
else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
@@ -921,12 +954,17 @@ public class DispatcherPortlet extends FrameworkPortlet {
|
||||
// Trigger after-completion for thrown exception.
|
||||
triggerAfterEventCompletion(mappedHandler, interceptorIndex, request, response, ex);
|
||||
// Forward the exception to the render phase to be displayed.
|
||||
try {
|
||||
exposeActionException(request, response, ex);
|
||||
logger.debug("Caught exception during event phase - forwarding to render phase", ex);
|
||||
if (this.forwardEventException) {
|
||||
try {
|
||||
exposeActionException(request, response, ex);
|
||||
logger.debug("Caught exception during event phase - forwarding to render phase", ex);
|
||||
}
|
||||
catch (IllegalStateException ex2) {
|
||||
// Probably sendRedirect called... need to rethrow exception immediately.
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
catch (IllegalStateException ex2) {
|
||||
// Probably sendRedirect called... need to rethrow exception immediately.
|
||||
else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
@@ -963,8 +1001,7 @@ public class DispatcherPortlet extends FrameworkPortlet {
|
||||
* Return the HandlerExecutionChain for this request.
|
||||
* Try all handler mappings in order.
|
||||
* @param request current portlet request
|
||||
* @param cache whether to cache the HandlerExecutionChain in a request attribute
|
||||
* @return the HandlerExceutionChain, or null if no handler could be found
|
||||
* @return the HandlerExecutionChain, or null if no handler could be found
|
||||
*/
|
||||
protected HandlerExecutionChain getHandler(PortletRequest request) throws Exception {
|
||||
for (HandlerMapping hm : this.handlerMappings) {
|
||||
|
||||
+17
-17
@@ -75,8 +75,7 @@ import org.springframework.web.servlet.View;
|
||||
public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
|
||||
|
||||
// dummy method placeholder
|
||||
private static final Method NO_METHOD_FOUND = ClassUtils
|
||||
.getMethodIfAvailable(System.class, "currentTimeMillis", null);
|
||||
private static final Method NO_METHOD_FOUND = ClassUtils.getMethodIfAvailable(System.class, "currentTimeMillis", (Class<?>[]) null);
|
||||
|
||||
private WebArgumentResolver[] customArgumentResolvers;
|
||||
|
||||
@@ -134,10 +133,9 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc
|
||||
private Method findBestExceptionHandlerMethod(Object handler, final Exception thrownException) {
|
||||
final Class<?> handlerType = handler.getClass();
|
||||
final Class<? extends Throwable> thrownExceptionType = thrownException.getClass();
|
||||
|
||||
Method handlerMethod = null;
|
||||
Map<Class<? extends Throwable>, Method> handlers = exceptionHandlerCache
|
||||
.get(handlerType);
|
||||
|
||||
Map<Class<? extends Throwable>, Method> handlers = exceptionHandlerCache.get(handlerType);
|
||||
|
||||
if (handlers != null) {
|
||||
handlerMethod = handlers.get(thrownExceptionType);
|
||||
@@ -173,7 +171,9 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc
|
||||
}
|
||||
});
|
||||
|
||||
return getBestMatchingMethod(resolverMethods, thrownException);
|
||||
handlerMethod = getBestMatchingMethod(resolverMethods, thrownException);
|
||||
handlers.put(thrownExceptionType, (handlerMethod == null ? NO_METHOD_FOUND : handlerMethod));
|
||||
return handlerMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,19 +204,19 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the best matching method. Uses the {@link DepthComparator}.
|
||||
* Uses the {@link DepthComparator} to find the best matching method
|
||||
* @return the best matching method or {@code null}.
|
||||
*/
|
||||
private Method getBestMatchingMethod(
|
||||
Map<Class<? extends Throwable>, Method> resolverMethods, Exception thrownException) {
|
||||
|
||||
if (!resolverMethods.isEmpty()) {
|
||||
Class<? extends Throwable> closestMatch =
|
||||
ExceptionDepthComparator.findClosestMatch(resolverMethods.keySet(), thrownException);
|
||||
return resolverMethods.get(closestMatch);
|
||||
}
|
||||
else {
|
||||
if (resolverMethods.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Class<? extends Throwable> closestMatch =
|
||||
ExceptionDepthComparator.findClosestMatch(resolverMethods.keySet(), thrownException);
|
||||
Method method = resolverMethods.get(closestMatch);
|
||||
return ((method == null) || (NO_METHOD_FOUND == method)) ? null : method;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,13 +225,13 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc
|
||||
private Object[] resolveHandlerArguments(Method handlerMethod, Object handler,
|
||||
NativeWebRequest webRequest, Exception thrownException) throws Exception {
|
||||
|
||||
Class[] paramTypes = handlerMethod.getParameterTypes();
|
||||
Class<?>[] paramTypes = handlerMethod.getParameterTypes();
|
||||
Object[] args = new Object[paramTypes.length];
|
||||
Class<?> handlerType = handler.getClass();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
MethodParameter methodParam = new MethodParameter(handlerMethod, i);
|
||||
GenericTypeResolver.resolveParameterType(methodParam, handlerType);
|
||||
Class paramType = methodParam.getParameterType();
|
||||
Class<?> paramType = methodParam.getParameterType();
|
||||
Object argValue = resolveCommonArgument(methodParam, webRequest, thrownException);
|
||||
if (argValue != WebArgumentResolver.UNRESOLVED) {
|
||||
args[i] = argValue;
|
||||
@@ -267,7 +267,7 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc
|
||||
}
|
||||
|
||||
// Resolution of standard parameter types...
|
||||
Class paramType = methodParameter.getParameterType();
|
||||
Class<?> paramType = methodParameter.getParameterType();
|
||||
Object value = resolveStandardArgument(paramType, webRequest, thrownException);
|
||||
if (value != WebArgumentResolver.UNRESOLVED && !ClassUtils.isAssignableValue(paramType, value)) {
|
||||
throw new IllegalStateException("Standard argument type [" + paramType.getName() +
|
||||
@@ -287,7 +287,7 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc
|
||||
* @param thrownException the exception thrown
|
||||
* @return the argument value, or {@link org.springframework.web.bind.support.WebArgumentResolver#UNRESOLVED}
|
||||
*/
|
||||
protected Object resolveStandardArgument(Class parameterType, NativeWebRequest webRequest,
|
||||
protected Object resolveStandardArgument(Class<?> parameterType, NativeWebRequest webRequest,
|
||||
Exception thrownException) throws Exception {
|
||||
|
||||
if (parameterType.isInstance(thrownException)) {
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -411,8 +411,8 @@ public class DefaultAnnotationHandlerMapping extends AbstractMapBasedHandlerMapp
|
||||
}
|
||||
else if (other instanceof RenderMappingPredicate) {
|
||||
RenderMappingPredicate otherRender = (RenderMappingPredicate) other;
|
||||
boolean hasWindowState = "".equals(this.windowState);
|
||||
boolean otherHasWindowState = "".equals(otherRender.windowState);
|
||||
boolean hasWindowState = (this.windowState != null);
|
||||
boolean otherHasWindowState = (otherRender.windowState != null);
|
||||
if (hasWindowState != otherHasWindowState) {
|
||||
return (hasWindowState ? -1 : 1);
|
||||
}
|
||||
|
||||
+87
-35
@@ -1,18 +1,18 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author 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.
|
||||
*/
|
||||
* 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.web.portlet;
|
||||
|
||||
@@ -24,7 +24,6 @@ import javax.portlet.PortletException;
|
||||
import javax.portlet.PortletMode;
|
||||
import javax.portlet.PortletSecurityException;
|
||||
import javax.portlet.PortletSession;
|
||||
import javax.portlet.UnavailableException;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
@@ -35,6 +34,9 @@ import org.springframework.context.i18n.LocaleContext;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.mock.web.portlet.MockActionRequest;
|
||||
import org.springframework.mock.web.portlet.MockActionResponse;
|
||||
import org.springframework.mock.web.portlet.MockEvent;
|
||||
import org.springframework.mock.web.portlet.MockEventRequest;
|
||||
import org.springframework.mock.web.portlet.MockEventResponse;
|
||||
import org.springframework.mock.web.portlet.MockPortletConfig;
|
||||
import org.springframework.mock.web.portlet.MockPortletContext;
|
||||
import org.springframework.mock.web.portlet.MockPortletSession;
|
||||
@@ -106,7 +108,7 @@ public class DispatcherPortletTests extends TestCase {
|
||||
(FrameworkPortlet.PORTLET_CONTEXT_PREFIX + "simple").equals(simpleDispatcherPortlet.getPortletContextAttributeName()));
|
||||
assertTrue("Context published",
|
||||
simpleDispatcherPortlet.getPortletApplicationContext() ==
|
||||
getPortletContext().getAttribute(FrameworkPortlet.PORTLET_CONTEXT_PREFIX + "simple"));
|
||||
getPortletContext().getAttribute(FrameworkPortlet.PORTLET_CONTEXT_PREFIX + "simple"));
|
||||
|
||||
assertTrue("Correct namespace", "test".equals(complexDispatcherPortlet.getNamespace()));
|
||||
assertTrue("Correct attribute",
|
||||
@@ -139,6 +141,56 @@ public class DispatcherPortletTests extends TestCase {
|
||||
assertTrue(exceptionParam.startsWith(NoHandlerFoundException.class.getName()));
|
||||
}
|
||||
|
||||
|
||||
public void testSimpleInvalidActionRequestWithoutHandling() throws Exception {
|
||||
MockActionRequest request = new MockActionRequest();
|
||||
MockActionResponse response = new MockActionResponse();
|
||||
request.setParameter("action", "invalid");
|
||||
simpleDispatcherPortlet.setForwardActionException(false);
|
||||
try {
|
||||
simpleDispatcherPortlet.processAction(request, response);
|
||||
fail("Should have thrown a " + NoHandlerFoundException.class);
|
||||
}
|
||||
catch (NoHandlerFoundException ex) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void testSimpleValidEventRequest() throws Exception {
|
||||
MockEvent event = new MockEvent("test-event");
|
||||
MockEventRequest request = new MockEventRequest(event);
|
||||
MockEventResponse response = new MockEventResponse();
|
||||
request.setParameter("action", "form");
|
||||
simpleDispatcherPortlet.processEvent(request, response);
|
||||
assertEquals("test-event", response.getRenderParameter("event"));
|
||||
}
|
||||
|
||||
public void testSimpleInvalidEventRequest() throws Exception {
|
||||
MockEvent event = new MockEvent("test-event");
|
||||
MockEventRequest request = new MockEventRequest(event);
|
||||
MockEventResponse response = new MockEventResponse();
|
||||
request.setParameter("action", "invalid");
|
||||
try {
|
||||
simpleDispatcherPortlet.processEvent(request, response);
|
||||
fail("Should have thrown a " + NoHandlerFoundException.class);
|
||||
}
|
||||
catch (NoHandlerFoundException ex) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void testSimpleInvalidEventRequestWithHandling() throws Exception {
|
||||
MockEvent event = new MockEvent("event");
|
||||
MockEventRequest request = new MockEventRequest(event);
|
||||
MockEventResponse response = new MockEventResponse();
|
||||
request.setParameter("action", "invalid");
|
||||
simpleDispatcherPortlet.setForwardEventException(true);
|
||||
simpleDispatcherPortlet.processEvent(request, response);
|
||||
String exceptionParam = response.getRenderParameter(DispatcherPortlet.ACTION_EXCEPTION_RENDER_PARAMETER);
|
||||
assertNotNull(exceptionParam);
|
||||
assertTrue(exceptionParam.startsWith(NoHandlerFoundException.class.getName()));
|
||||
}
|
||||
|
||||
public void testSimpleFormViewNoBindOnNewForm() throws Exception {
|
||||
MockRenderRequest request = new MockRenderRequest();
|
||||
MockRenderResponse response = new MockRenderResponse();
|
||||
@@ -202,7 +254,7 @@ public class DispatcherPortletTests extends TestCase {
|
||||
fail("Should have thrown PortletSessionRequiredException");
|
||||
}
|
||||
catch (PortletSessionRequiredException ex) {
|
||||
// expected
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +299,7 @@ public class DispatcherPortletTests extends TestCase {
|
||||
fail("Should have thrown UnavailableException");
|
||||
}
|
||||
catch (NoHandlerFoundException ex) {
|
||||
// expected
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,7 +483,7 @@ public class DispatcherPortletTests extends TestCase {
|
||||
request.setPortletMode(PortletMode.EDIT);
|
||||
ComplexPortletApplicationContext.MockMultipartResolver multipartResolver =
|
||||
(ComplexPortletApplicationContext.MockMultipartResolver)
|
||||
complexDispatcherPortlet.getPortletApplicationContext().getBean("portletMultipartResolver");
|
||||
complexDispatcherPortlet.getPortletApplicationContext().getBean("portletMultipartResolver");
|
||||
MultipartActionRequest multipartRequest = multipartResolver.resolveMultipart(request);
|
||||
complexDispatcherPortlet.processAction(multipartRequest, response);
|
||||
multipartResolver.cleanupMultipart(multipartRequest);
|
||||
@@ -455,7 +507,7 @@ public class DispatcherPortletTests extends TestCase {
|
||||
complexDispatcherPortlet.processAction(request, response);
|
||||
ComplexPortletApplicationContext.TestApplicationListener listener =
|
||||
(ComplexPortletApplicationContext.TestApplicationListener)
|
||||
complexDispatcherPortlet.getPortletApplicationContext().getBean("testListener");
|
||||
complexDispatcherPortlet.getPortletApplicationContext().getBean("testListener");
|
||||
assertEquals(1, listener.counter);
|
||||
}
|
||||
|
||||
@@ -465,7 +517,7 @@ public class DispatcherPortletTests extends TestCase {
|
||||
complexDispatcherPortlet.doDispatch(request, response);
|
||||
ComplexPortletApplicationContext.TestApplicationListener listener =
|
||||
(ComplexPortletApplicationContext.TestApplicationListener)
|
||||
complexDispatcherPortlet.getPortletApplicationContext().getBean("testListener");
|
||||
complexDispatcherPortlet.getPortletApplicationContext().getBean("testListener");
|
||||
assertEquals(1, listener.counter);
|
||||
}
|
||||
|
||||
@@ -477,7 +529,7 @@ public class DispatcherPortletTests extends TestCase {
|
||||
complexDispatcherPortlet.processAction(request, response);
|
||||
ComplexPortletApplicationContext.TestApplicationListener listener =
|
||||
(ComplexPortletApplicationContext.TestApplicationListener)
|
||||
complexDispatcherPortlet.getPortletApplicationContext().getBean("testListener");
|
||||
complexDispatcherPortlet.getPortletApplicationContext().getBean("testListener");
|
||||
assertEquals(0, listener.counter);
|
||||
}
|
||||
|
||||
@@ -611,7 +663,7 @@ public class DispatcherPortletTests extends TestCase {
|
||||
complexDispatcherPortlet.processAction(request, response);
|
||||
assertEquals("myPortlet action called", response.getRenderParameter("result"));
|
||||
ComplexPortletApplicationContext.MyPortlet myPortlet =
|
||||
(ComplexPortletApplicationContext.MyPortlet) complexDispatcherPortlet.getPortletApplicationContext().getBean("myPortlet");
|
||||
(ComplexPortletApplicationContext.MyPortlet) complexDispatcherPortlet.getPortletApplicationContext().getBean("myPortlet");
|
||||
assertEquals("complex", myPortlet.getPortletConfig().getPortletName());
|
||||
assertEquals(getPortletContext(), myPortlet.getPortletConfig().getPortletContext());
|
||||
assertEquals(complexDispatcherPortlet.getPortletContext(), myPortlet.getPortletConfig().getPortletContext());
|
||||
@@ -626,12 +678,12 @@ public class DispatcherPortletTests extends TestCase {
|
||||
complexDispatcherPortlet.doDispatch(request, response);
|
||||
assertEquals("myPortlet was here", response.getContentAsString());
|
||||
ComplexPortletApplicationContext.MyPortlet myPortlet =
|
||||
(ComplexPortletApplicationContext.MyPortlet)
|
||||
complexDispatcherPortlet.getPortletApplicationContext().getBean("myPortlet");
|
||||
(ComplexPortletApplicationContext.MyPortlet)
|
||||
complexDispatcherPortlet.getPortletApplicationContext().getBean("myPortlet");
|
||||
assertEquals("complex", myPortlet.getPortletConfig().getPortletName());
|
||||
assertEquals(getPortletContext(), myPortlet.getPortletConfig().getPortletContext());
|
||||
assertEquals(complexDispatcherPortlet.getPortletContext(),
|
||||
myPortlet.getPortletConfig().getPortletContext());
|
||||
myPortlet.getPortletConfig().getPortletContext());
|
||||
complexDispatcherPortlet.destroy();
|
||||
assertNull(myPortlet.getPortletConfig());
|
||||
}
|
||||
@@ -769,18 +821,18 @@ public class DispatcherPortletTests extends TestCase {
|
||||
}
|
||||
|
||||
public void testGetMessage() {
|
||||
String message = complexDispatcherPortlet.getPortletApplicationContext().getMessage("test", null, Locale.ENGLISH);
|
||||
String message = complexDispatcherPortlet.getPortletApplicationContext().getMessage("test", null, Locale.ENGLISH);
|
||||
assertEquals("test message", message);
|
||||
}
|
||||
|
||||
public void testGetMessageOtherLocale() {
|
||||
String message = complexDispatcherPortlet.getPortletApplicationContext().getMessage("test", null, Locale.CANADA);
|
||||
String message = complexDispatcherPortlet.getPortletApplicationContext().getMessage("test", null, Locale.CANADA);
|
||||
assertEquals("Canadian & test message", message);
|
||||
}
|
||||
|
||||
public void testGetMessageWithArgs() {
|
||||
Object[] args = new String[] {"this", "that"};
|
||||
String message = complexDispatcherPortlet.getPortletApplicationContext().getMessage("test.args", args, Locale.ENGLISH);
|
||||
String message = complexDispatcherPortlet.getPortletApplicationContext().getMessage("test.args", args, Locale.ENGLISH);
|
||||
assertEquals("test this and that", message);
|
||||
}
|
||||
|
||||
@@ -793,7 +845,7 @@ public class DispatcherPortletTests extends TestCase {
|
||||
fail("Should have thrown IllegalStateException");
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
// expected
|
||||
// expected
|
||||
}
|
||||
portletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
|
||||
new StaticWebApplicationContext());
|
||||
@@ -813,7 +865,7 @@ public class DispatcherPortletTests extends TestCase {
|
||||
request.setParameter("action", "form");
|
||||
request.setParameter("age", "29");
|
||||
|
||||
// see RequestContextListener.requestInitialized()
|
||||
// see RequestContextListener.requestInitialized()
|
||||
try {
|
||||
LocaleContextHolder.setLocale(request.getLocale());
|
||||
RequestContextHolder.setRequestAttributes(new PortletRequestAttributes(request));
|
||||
@@ -837,7 +889,7 @@ public class DispatcherPortletTests extends TestCase {
|
||||
MockRenderResponse response = new MockRenderResponse();
|
||||
request.addPreferredLocale(Locale.GERMAN);
|
||||
|
||||
// see RequestContextListener.requestInitialized()
|
||||
// see RequestContextListener.requestInitialized()
|
||||
try {
|
||||
LocaleContextHolder.setLocale(request.getLocale());
|
||||
RequestContextHolder.setRequestAttributes(new PortletRequestAttributes(request));
|
||||
@@ -863,7 +915,7 @@ public class DispatcherPortletTests extends TestCase {
|
||||
MockActionResponse response = new MockActionResponse();
|
||||
request.addPreferredLocale(Locale.GERMAN);
|
||||
|
||||
// see RequestContextListener.requestInitialized()
|
||||
// see RequestContextListener.requestInitialized()
|
||||
try {
|
||||
LocaleContextHolder.setLocale(request.getLocale());
|
||||
RequestContextHolder.setRequestAttributes(new PortletRequestAttributes(request));
|
||||
@@ -890,7 +942,7 @@ public class DispatcherPortletTests extends TestCase {
|
||||
MockRenderResponse response = new MockRenderResponse();
|
||||
request.addPreferredLocale(Locale.GERMAN);
|
||||
|
||||
// see RequestContextListener.requestInitialized()
|
||||
// see RequestContextListener.requestInitialized()
|
||||
try {
|
||||
LocaleContextHolder.setLocale(request.getLocale());
|
||||
RequestContextHolder.setRequestAttributes(new PortletRequestAttributes(request));
|
||||
@@ -903,7 +955,7 @@ public class DispatcherPortletTests extends TestCase {
|
||||
fail("should have failed to find a handler and raised an NoHandlerFoundExceptionException");
|
||||
}
|
||||
catch (NoHandlerFoundException ex) {
|
||||
// expected
|
||||
// expected
|
||||
}
|
||||
|
||||
assertSame(servletLocaleContext, LocaleContextHolder.getLocaleContext());
|
||||
|
||||
+11
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
* 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.
|
||||
@@ -19,6 +19,8 @@ package org.springframework.web.portlet;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.portlet.EventRequest;
|
||||
import javax.portlet.EventResponse;
|
||||
import javax.portlet.RenderRequest;
|
||||
import javax.portlet.RenderResponse;
|
||||
|
||||
@@ -31,6 +33,7 @@ import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.portlet.context.StaticPortletApplicationContext;
|
||||
import org.springframework.web.portlet.handler.ParameterHandlerMapping;
|
||||
import org.springframework.web.portlet.mvc.EventAwareController;
|
||||
import org.springframework.web.portlet.mvc.SimpleFormController;
|
||||
|
||||
/**
|
||||
@@ -44,7 +47,7 @@ public class SimplePortletApplicationContext extends StaticPortletApplicationCon
|
||||
public void refresh() throws BeansException {
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
registerSingleton("controller1", TestFormController.class, pvs);
|
||||
|
||||
|
||||
pvs = new MutablePropertyValues();
|
||||
pvs.add("bindOnNewForm", "true");
|
||||
registerSingleton("controller2", TestFormController.class, pvs);
|
||||
@@ -62,7 +65,7 @@ public class SimplePortletApplicationContext extends StaticPortletApplicationCon
|
||||
registerSingleton("controller4", TestFormController.class, pvs);
|
||||
|
||||
pvs = new MutablePropertyValues();
|
||||
Map parameterMap = new ManagedMap();
|
||||
Map parameterMap = new ManagedMap();
|
||||
parameterMap.put("form", new RuntimeBeanReference("controller1"));
|
||||
parameterMap.put("form-bind", new RuntimeBeanReference("controller2"));
|
||||
parameterMap.put("form-session-bind", new RuntimeBeanReference("controller3"));
|
||||
@@ -86,7 +89,7 @@ public class SimplePortletApplicationContext extends StaticPortletApplicationCon
|
||||
}
|
||||
|
||||
|
||||
public static class TestFormController extends SimpleFormController {
|
||||
public static class TestFormController extends SimpleFormController implements EventAwareController {
|
||||
|
||||
TestFormController() {
|
||||
super();
|
||||
@@ -124,6 +127,10 @@ public class SimplePortletApplicationContext extends StaticPortletApplicationCon
|
||||
private void writeResponse(RenderResponse response, TestBean testBean, boolean finished) throws IOException {
|
||||
response.getWriter().write((finished ? "finished" : "") + (testBean.getAge() + 5));
|
||||
}
|
||||
|
||||
public void handleEventRequest(EventRequest request, EventResponse response) throws Exception {
|
||||
response.setRenderParameter("event", request.getEvent().getName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.apache.velocity/com.springsource.org.apache.velocity/1.5.0/com.springsource.org.apache.velocity-1.5.0.jar" sourcepath="/IVY_CACHE/org.apache.velocity/com.springsource.org.apache.velocity/1.5.0/com.springsource.org.apache.velocity-sources-1.5.0.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.codehaus.jackson/com.springsource.org.codehaus.jackson.mapper/1.4.2/com.springsource.org.codehaus.jackson.mapper-1.4.2.jar" sourcepath="/IVY_CACHE/org.codehaus.jackson/com.springsource.org.codehaus.jackson.mapper/1.4.2/com.springsource.org.codehaus.jackson.mapper-sources-1.4.2.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.codehaus.jackson/com.springsource.org.codehaus.jackson/1.4.2/com.springsource.org.codehaus.jackson-1.4.2.jar" sourcepath="/IVY_CACHE/org.codehaus.jackson/com.springsource.org.codehaus.jackson/1.4.2/com.springsource.org.codehaus.jackson-sources-1.4.2.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/com.fasterxml.jackson.core/com.springsource.com.fasterxml.jackson.core.jackson-databind/2.0.2/com.springsource.com.fasterxml.jackson.core.jackson-databind-2.0.2.jar" sourcepath="IVY_CACHE/com.fasterxml.jackson.core/com.springsource.com.fasterxml.jackson.core.jackson-databind/2.0.2/com.springsource.com.fasterxml.jackson.core.jackson-databind-2.0.2-sources.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/com.fasterxml.jackson.core/com.springsource.com.fasterxml.jackson.core.jackson-core/2.0.2/com.springsource.com.fasterxml.jackson.core.jackson-core-2.0.2.jar" sourcepath="IVY_CACHE/com.fasterxml.jackson.core/com.springsource.com.fasterxml.jackson.core.jackson-core/2.0.2/com.springsource.com.fasterxml.jackson.core.jackson-core-2.0.2-sources.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/com.fasterxml.jackson.core/com.springsource.com.fasterxml.jackson.core.jackson-annotations/2.0.2/com.springsource.com.fasterxml.jackson.core.jackson-annotations-2.0.2.jar" sourcepath="IVY_CACHE/com.fasterxml.jackson.core/com.springsource.com.fasterxml.jackson.core.jackson-annotations/2.0.2/com.springsource.com.fasterxml.jackson.core.jackson-annotations-2.0.2-sources.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.custommonkey.xmlunit/com.springsource.org.custommonkey.xmlunit/1.2.0/com.springsource.org.custommonkey.xmlunit-1.2.0.jar" sourcepath="/IVY_CACHE/org.custommonkey.xmlunit/com.springsource.org.custommonkey.xmlunit/1.2.0/com.springsource.org.custommonkey.xmlunit-sources-1.2.0.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.dom4j/com.springsource.org.dom4j/1.6.1/com.springsource.org.dom4j-1.6.1.jar" sourcepath="/IVY_CACHE/org.dom4j/com.springsource.org.dom4j/1.6.1/com.springsource.org.dom4j-sources-1.6.1.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.easymock/com.springsource.org.easymock/2.5.1/com.springsource.org.easymock-2.5.1.jar" sourcepath="/IVY_CACHE/org.easymock/com.springsource.org.easymock/2.5.1/com.springsource.org.easymock-sources-2.5.1.jar"/>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user