Compare commits

...

68 Commits

Author SHA1 Message Date
Spring Buildmaster 49f728eae8 Release version 3.1.2.RELEASE 2012-07-07 17:13:55 +02:00
Juergen Hoeller 16da15eca1 final preparations for 3.1.2 2012-07-06 22:48:43 +02:00
Juergen Hoeller efabc6bf18 added "jtaTransactionManager" property to Hibernate 4 LocalSessionFactoryBean/Builder
Issue: SPR-9480
2012-07-06 22:48:42 +02:00
Juergen Hoeller 98b281ea45 Add defaultCharset field to StringHttpMessageConverter
Before this change the StringHttpMessageConverter used a fixed charset "ISO-8859-1" if the requested content type did not specify one. This change adds a defaultCharset field and a constructor to configure it in StringHttpMessageConverter.

Issue: SPR-9487
2012-07-06 22:48:41 +02:00
Chris Beams 00a69394e6 Resolve nested placeholders via PropertyResolver
Prior to this change, PropertySourcesPropertyResolver (and therefore
all AbstractEnvironment) implementations failed to resolve nested
placeholders as in the following example:

    p1=v1
    p2=v2
    p3=${v1}:{$v2}

Calls to PropertySource#getProperty for keys 'p1' and 'v1' would
successfully return their respective values, but for 'p3' the return
value would be the unresolved placeholders. This behavior is
inconsistent with that of PropertyPlaceholderConfigurer.

PropertySourcesPropertyResolver #getProperty variants now resolve any
nested placeholders recursively, throwing IllegalArgumentException for
any unresolvable placeholders (as is the default behavior for
PropertyPlaceholderConfigurer). See SPR-9569 for an enhancement that
will intoduce an 'ignoreUnresolvablePlaceholders' switch to make this
behavior configurable.

This commit also improves error output in
PropertyPlaceholderHelper#parseStringValue by including the original
string in which an unresolvable placeholder was found.

Issue: SPR-9473, SPR-9569
2012-07-06 19:42:54 +02:00
Chris Beams 45f1438ef8 Add hamcrest to spring-aspects Eclipse classpath
Also remove spring project nature from spring-context to avoid error in
STS.
2012-07-06 15:52:00 +02:00
Juergen Hoeller e7b44e015a Cannot amend properties in RequestMappingHandlerMapping (e.g. useSuffixPatternMatch) using a bean post processor as ApplicationContextAwareProcessor always fires first initialising RequestMappingHandlerMapping
Issue: SPR-9371
2012-07-05 01:29:41 +02:00
Juergen Hoeller 23fe1e2934 Cannot amend properties in RequestMappingHandlerMapping (e.g. useSuffixPatternMatch) using a bean post processor as ApplicationContextAwareProcessor always fires first initialising RequestMappingHandlerMapping
Issue: SPR-9371
2012-07-05 01:20:43 +02:00
Juergen Hoeller 1f64b147b1 indentation configuration for further file types 2012-07-05 01:08:22 +02:00
Juergen Hoeller 5e4f3b3108 Cannot amend properties in RequestMappingHandlerMapping (e.g. useSuffixPatternMatch) using a bean post processor as ApplicationContextAwareProcessor always fires first initialising RequestMappingHandlerMapping
Issue: SPR-9371
2012-07-05 01:06:55 +02:00
Juergen Hoeller c87989477d preparations for 3.1.2 2012-07-05 00:51:17 +02:00
Juergen Hoeller 3b4210fa6f Cannot amend properties in RequestMappingHandlerMapping (e.g. useSuffixPatternMatch) using a bean post processor as ApplicationContextAwareProcessor always fires first initialising RequestMappingHandlerMapping
Issue: SPR-9371
2012-07-05 00:31:24 +02:00
Juergen Hoeller 79b86a7e3a DispatcherPortlet does not forward event exceptions to the render phase by default
Issue: SPR-9287
2012-07-05 00:19:32 +02:00
Juergen Hoeller b488ae6009 removed misleading "throws IllegalStateException" declaration
Issue: SPR-9561
2012-07-04 23:33:15 +02:00
Juergen Hoeller aeef8c87f6 added "repeatCount" bean property to Quartz SimpleTriggerFactoryBean
Issue: SPR-9521
2012-07-04 23:22:38 +02:00
Juergen Hoeller 556c6a79c5 Use BufferedInputStream in SimpleMetaDataReader to double performance
Issue: SPR-9528
2012-07-02 22:09:36 +02:00
Juergen Hoeller 0281158fcc Make 'Content-Disposition' header case insensitive
Issue: SPR-9149
2012-06-28 18:02:43 +02:00
Juergen Hoeller 100fa178dd XStreamMarshaller should convert XStream StreamException to Spring exception in case of unmarshalling an empty stream.
Issue: SPR-9536
2012-06-28 17:43:39 +02:00
Chris Beams a387d13d5f Reflect 3.2=>3.1.2 backports in @since tags etc
Issue: SPR-9443, SPR-6847, SPR-9446, SPR-9444, SPR-9439, SPR-9302,
       SPR-9507, SPR-9238, SPR-9397, SPR-9406, SPR-9502
2012-06-27 23:09:14 +02:00
Chris Beams 841d9533db Cache by-type lookups in DefaultListableBeanFactory
Prior to this change, by-type lookups using DLBF#getBeanNamesForType
required traversal of all bean definitions within the bean factory
in order to inspect their bean class for assignability to the target
type. These operations are comparatively expensive and when there are a
large number of beans registered within the container coupled with a
large number of by-type lookups at runtime, the performance impact can
be severe. The test introduced here demonstrates such a scenario clearly.

This performance problem is likely to manifest in large Spring-based
applications using non-singleton beans, particularly request-scoped
beans that may be created and wired many thousands of times per second.

This commit introduces a simple ConcurrentHashMap-based caching strategy
for by-type lookups; container-wide assignability checks happen only
once on the first by-type lookup and are afterwards cached by type
with the values in the map being an array of all bean names assignable
to that type. This means that at runtime when creating and autowiring
non-singleton beans, the cost of by-type lookups is reduced to that of
ConcurrentHashMap#get.

Issue: SPR-9448
Backport-Issue: SPR-6870
Backport-Commit: 4c7a1c0a54
2012-06-27 23:09:14 +02:00
Chris Beams 0d18deb6a4 Polish
Issue: SPR-9448
Backport-Issue: SPR-6870
Backport-Commit: db1cb13448
2012-06-27 23:09:14 +02:00
Sam Brannen 42204e5098 Fix regression in ClassPathResource descriptions
ClassPathResource.getDescription() now returns consistent, meaningful
results for all variants of ClassPathResource's constructors.

Issue: SPR-9415
Backport-Issue: SPR-9413
Backport-Commit: b50f6e19a6
2012-06-27 23:09:14 +02:00
Dridi Boukelmoune eed090e5f5 Fix scoped-proxy memory leak w/ @Resource injection
Prior to this change, request-scoped components having
@Resource-injected dependencies caused a memory leak in
DefaultListableBeanFactory#dependenciesForBeanMap.

Consider the following example:

    @Component
    @Scope(value="request", proxyMode=ScopedProxyMode.TARGET_CLASS)
    public class MyComponent {

        @Resource
        private HttpServletRequest request;

        // ...
    }

The bean name for "MyComponent" will end up being
'scopedTarget.myComponent', which will become a key in
the #dependenciesForBeanMap structure.

On the first request, the injected HttpServletRequest bean will be a
proxy and will internally have a bean name of the form
"$Proxy10@1a3a2a52". This name will be added to the Set value associated
with the 'scopedTarget.myComponent' entry in #dependenciesForBeanMap.

On the second request, the process will repeat, but the injected
HttpServletRequest will be a different proxy instance, thus having a
different identity hex string, e.g. "$Proxy10@5eba06ff". This name will
also be added to the Set value associated with the
'scopedTarget.myComponent' entry in #dependenciesForBeanMap, and this
is the source of the leak: a new entry is added to the set on each
request but should be added only once.

This commit fixes the leak by introducing caching to
CommonAnnotationBeanPostProcessor#ResourceElement similar to that already
present in AutowiredAnnotationBeanPostProcessor#AutowiredFieldElement
and #AutowiredMethodElement. Essentially, each ResourceElement instance
now tracks whether it has been created, caches the ultimate value to be
injected and returns it eagerly if necessary. Besides solving the memory
leak, this has the side effect of avoiding unnecessary proxy creation.

This fix also explains clearly why injection into request-scoped
components using @Autowired never suffered this memory leak: because the
correct caching was already in place. Because @Resource is considerably
less-frequently used than @Autowired, and given that this particular
injection arrangement is relatively infrequent, it becomes
understandable how this bug has been present without being reported
since the introduction of @Resource support in Spring 2.5: developers
were unlikely to encounter it in the first place; and if they did, the
leak was minor enough (adding strings to a Set), that it could
potentially go unnoticed indefinitely depending on request volumes and
available memory.

Issue: SPR-9363
Backport-Issue: SPR-9176
Backport-Commit: f779c199ea
2012-06-27 23:09:13 +02:00
Chris Beams 0320cc582b Introduce BeanFactoryAnnotationUtils
Commit 3f387eb9cf refactored and
deprecated TransactionAspectUtils, moving its #qualifiedBeanOfType
and related methods into BeanFactoryUtils. This created a package cycle
between beans.factory and beans.factory.annotation due to use of the
beans.factory.annotation.Qualifier annotation in these methods.

This commit breaks the package cycle by introducing
beans.factory.annotation.BeanFactoryAnnotationUtils and moving these
@Qualifier-related methods to it. It is intentionally similar in name
and style to the familiar BeanFactoryUtils class for purposes of
discoverability.

There are no backward-compatibilty concerns associated with this change
as the cycle was introduced, caught and now fixed before a release.

Issue: SPR-9443
Backport-Issue: SPR-6847
Backport-Commit: a4b00c732b
2012-06-27 23:09:06 +02:00
Chris Beams a76c7ca299 Test meta-@Async executor qualification
Prove that Async#value is respected even when using @Async as a meta
annotation.

Issue: SPR-9443
Backport-Issue: SPR-6847
Backport-Commit: 37e024c6eb
2012-06-27 23:06:54 +02:00
Chris Beams 8bab873107 Support executor qualification with @Async#value
Prior to this change, Spring's @Async annotation support was tied to a
single AsyncTaskExecutor bean, meaning that all methods marked with
@Async were forced to use the same executor. This is an undesirable
limitation, given that certain methods may have different priorities,
etc. This leads to the need to (optionally) qualify which executor
should handle each method.

This is similar to the way that Spring's @Transactional annotation was
originally tied to a single PlatformTransactionManager, but in Spring
3.0 was enhanced to allow for a qualifier via the #value attribute, e.g.

  @Transactional(ptm1)
  public void m() { ... }

where ptm1 is either the name of a PlatformTransactionManager bean or
a qualifier value associated with a PlatformTransactionManager bean,
e.g. via the <qualifier> element in XML or the @Qualifier annotation.

This commit introduces the same approach to @Async and its relationship
to underlying executor beans. As always, the following syntax remains
supported

  @Async
  public void m() { ... }

indicating that calls to #m will be delegated to the default executor,
i.e. the executor provided to

  <task:annotation-driven executor=.../>

or the executor specified when authoring a @Configuration class that
implements AsyncConfigurer and its #getAsyncExecutor method.

However, it now also possible to qualify which executor should be used
on a method-by-method basis, e.g.

  @Async(e1)
  public void m() { ... }

indicating that calls to #m will be delegated to the executor bean
named or otherwise qualified as e1. Unlike the default executor
which is specified up front at configuration time as described above,
the e1 executor bean is looked up within the container on the first
execution of #m and then cached in association with that method for the
lifetime of the container.

Class-level use of Async#value behaves as expected, indicating that all
methods within the annotated class should be executed with the named
executor. In the case of both method- and class-level annotations, any
method-level #value overrides any class level #value.

This commit introduces the following major changes:

 - Add @Async#value attribute for executor qualification

 - Introduce AsyncExecutionAspectSupport as a common base class for
   both MethodInterceptor- and AspectJ-based async aspects. This base
   class provides common structure for specifying the default executor
   (#setExecutor) as well as logic for determining (and caching) which
   executor should execute a given method (#determineAsyncExecutor) and
   an abstract method to allow subclasses to provide specific strategies
   for executor qualification (#getExecutorQualifier).

 - Introduce AnnotationAsyncExecutionInterceptor as a specialization of
   the existing AsyncExecutionInterceptor to allow for introspection of
   the @Async annotation and its #value attribute for a given method.
   Note that this new subclass was necessary for packaging reasons -
   the original AsyncExecutionInterceptor lives in
   org.springframework.aop and therefore does not have visibility to
   the @Async annotation in org.springframework.scheduling.annotation.
   This new subclass replaces usage of AsyncExecutionInterceptor
   throughout the framework, though the latter remains usable and
   undeprecated for compatibility with any existing third-party
   extensions.

 - Add documentation to spring-task-3.2.xsd and reference manual
   explaining @Async executor qualification

 - Add tests covering all new functionality

Note that the public API of all affected components remains backward-
compatible.

Issue: SPR-9443
Backport-Issue: SPR-6847
Backport-Commit: ed0576c181
2012-06-27 23:06:54 +02:00
Chris Beams e8006bdf78 Polish async method execution infrastructure
In anticipation of substantive changes required to implement @Async
executor qualification, the following updates have been made to the
components and infrastructure supporting @Async functionality:

 - Fix trailing whitespace and indentation errors
 - Fix generics warnings
 - Add Javadoc where missing, update to use {@code} tags, etc.
 - Avoid NPE in AopUtils#canApply
 - Organize imports to follow conventions
 - Remove System.out.println statements from tests
 - Correct various punctuation and grammar problems

Issue: SPR-9443
Backport-Issue: SPR-6847
Backport-Commit: 3fb11870d9
2012-06-27 23:06:05 +02:00
Chris Beams 3f387eb9cf Refactor and deprecate TransactionAspectUtils
TransactionAspectUtils contains a number of methods useful in
retrieving a bean by type+qualifier. These methods are functionally
general-purpose save for the hard coding of PlatformTransactionManager
class literals throughout.

This commit generifies these methods and moves them into
BeanFactoryUtils primarily in anticipation of their use by async method
execution interceptors and aspects when performing lookups for qualified
executor beans e.g. via @Async(qualifier).

The public API of TransactionAspectUtils remains backward compatible;
all methods within have been deprecated, and all calls to those methods
throughout the framework refactored to use the new BeanFactoryUtils
variants instead.

Issue: SPR-9443
Backport-Issue: SPR-6847
Backport-Commit: 096693c46f
2012-06-27 23:06:05 +02:00
Oliver Gierke 0769d53a6a Use transactional connection during db population
Previously, DatabasePopulatorUtils#execute looked up a Connection from
the given DataSource directly which resulted in the executed statements
not being executed against a transactional connection (if any) which in
turn resulted in the statements executed by the populator potentially
not being rolled back.

Now DataSourceUtils#getConnection is used to transparently take part in
any active transaction and #releaseConnection is used to ensure the
connection is closed if appropriate.

Issue: SPR-9465
Backport-Issue: SPR-9457
Backport-Commit: 49c9a2a915
2012-06-27 23:06:04 +02:00
Chris Beams 6263c9abf9 Fix package cycle in @EnableSpringConfigured
@EnableSpringConfigured and its @Import'ed
SpringConfiguredConfiguration @Configuration class inadvertently
established a package cycle between beans.factory.aspectj and
context.annotation due to SpringConfiguredConfiguration's
dependency on annotations such as @Configuration, @Bean and @Role.

This commit fixes this architecture bug by moving
@EnableSpringConfigured and SpringConfiguredConfiguration from the
beans.factory.aspectj package to the context.annotation package where
they belong.

This change is assumed to be very low impact as @EnableSpringConfigured
was introduced in 3.1.0 and relocation is happening as quickly as
possible in 3.1.2. @EnableSpringConfigured is assumed to be infrequently
used at this point, and for those that are the migration path
is straightforward. When upgrading from Spring 3.1.0 or 3.1.1, update
import statements in any affected @Configuration classes to reflect the
new packaging.

Backporter's note: this change causes Bundlor warnings in
org.springframework.aspect as its manifest now "imports and exports the
package org.springframework.context.annotation". To 'solve' this
problem, `fail.on.warnings=false` has been added to build.properties.
This means that future Bundlor-based warnings may go unnoticed.

Issue: SPR-9442
Backport-Issue: SPR-9441
Backport-Commit: 5327a7a37d
2012-06-27 23:06:04 +02:00
Chris Beams 1eb50297ad Introduce ConfigurableWebEnvironment
Changes introduced in Spring 3.1 for Environment support inadvertently
established a cyclic dependency between the
org.springframework.web.context and
org.springframework.web.context.support packages, specifically through
web.context.ContextLoader's invocation of
web.context.support.WebApplicationContextUtils#initServletPropertySources.

This commit introduces ConfigurableWebEnvironment to break this cyclic
dependency. All web.context.ConfigurableWebApplicationContext types now
return web.context.ConfigurableWebEnvironment from their #getEnvironment
methods; web.context.support.StandardServletEnvironment now implements
ConfigurableWebEnvironment and makes the call to
web.context.support.WebApplicationContextUtils#initServletPropertySources
within its implementation of #initPropertySources. This means that
web.context.ContextLoader now invokes
web.context.ConfigurableWebEnvironment#initPropertySources instead of
web.context.support.WebApplicationContextUtils#initServletPropertySources
and thus the cycle is broken.

Issue: SPR-9440
Backport-Issue: SPR-9439
Backport-Commit: 2a2b6eef91
2012-06-27 23:06:04 +02:00
Chris Beams 20f87ab98d Introduce ConfigurableEnvironment#merge
Prior to this change, AbstractApplicationContext#setParent replaced the
child context's Environment with the parent's Environment if available.
This has the negative effect of potentially changing the type of the
child context's Environment, and in any case causes property sources
added directly against the child environment to be ignored. This
situation could easily occur if a WebApplicationContext child had a
non-web ApplicationContext set as its parent. In this case the parent
Environment type would (likely) be StandardEnvironment, while the child
Environment type would (likely) be StandardServletEnvironment. By
directly inheriting the parent environment, critical property sources
such as ServletContextPropertySource are lost entirely.

This commit introduces the concept of merging an environment through
the new ConfigurableEnvironment#merge method. Instead of replacing the
child's environment with the parent's,
AbstractApplicationContext#setParent now merges property sources as
well as active and default profile names from the parent into the
child. In this way, distinct environment objects are maintained with
specific types and property sources preserved. See #merge Javadoc for
additional details.

Issue: SPR-9446
Backport-Issue: SPR-9444, SPR-9439
Backport-Commit: 9fcfd7e827
2012-06-27 23:06:04 +02:00
Chris Beams c25a1bcb60 Reduce log level for message re: missing annotation
Previously (since Spring 3.1.1) RecursiveAnnotationAttributesVisitor
logs at level WARN when ASM parsing encounters an annotation or an (enum
used within an annotation) that cannot be classloaded. This is not
necessarily indicative of an error, e.g. JSR-305 annotations such as
@Nonnull may be used only for static analysis purposes, but because
these annotations have runtime retention, they remain present in the
bytecode. Per section 9.6.1.2 of the JLS, "An annotation that is present
in the binary may or may not be available at run-time via the reflective
libraries of the Java platform."

This commit lowers the log level of these messages from warn to debug,
but leaves at warn level other messages dealing with the ability
reflectively read enum values from within annotations.

Issue: SPR-9447
Backport-Issue: SPR-9233
Backport-Commit: f55a4a1ac5
2012-06-27 23:06:04 +02:00
Rossen Stoyanchev e8deba2915 Fix issue with encoded params in UriComponentsBuilder
The fromUri method of UriComponentsBuilder used uri.getXxx() methods,
which decode the URI parts causing URI parsing issues. The same method
now uses uri.getRawXxx().

Issue: SPR-9317
Backport-Issue: SPR-9549
Backport-Commit: a33fe6fa0a
2012-06-27 10:51:45 -04:00
Rossen Stoyanchev fa33c4b857 Update changelog 2012-06-26 18:03:32 -04:00
Rossen Stoyanchev 24c30eac49 Raise RestClientException for unknown status codes
HttpStatus cannot be created with an unknown status code. If a server
returns a status code that's not in the HttpStatus enum values, an
IllegalArgumentException is raised. Rather than allowing it to
propagate as such, this change ensures the actual exception raised is
a RestClientException.

Issue: SPR-9406
Backport-Issue: SPR-9502
2012-06-26 17:56:31 -04:00
Rossen Stoyanchev 1af7575bde Merge rather than add URI vars to data binding values
As of Spring 3.1 URI variables can be used for data binding purposes in
addition to request parameters (including query string and form params)

In some cases URI variables and request params can overlap (e.g. form
contains a child entity with an entityId as hidden form input while the
URI contains the entityId of the parent entity) and that can lead to
surprises if the application already exists.

This change ensures that request parameters are used first and URI
vars are added only if they don't overlap. Ideally however an
application should not use the same uri variable name as the name of
a request parameter where they don't refer to the same value.

Issue: SPR-9349
Backport-Issue: SPR-9432
Backport-Commit: 4027b38903
2012-06-26 17:40:45 -04:00
Rossen Stoyanchev 8143d66e4c Pattern suffix issue in AnnotationMethodHandlerAdapter
Issue: SPR-9333
Backport-Issue: SPR-9419
Backport-Commit: cf5d55173b
2012-06-26 17:38:29 -04:00
Rossen Stoyanchev 9e370208d0 Fix content negotiation issue with sort by q-value
Before this fix the q-value of media types in the Accept header were
ignored when using the new RequestMappingHandlerAdapter in combination
with @ResponseBody and HttpMessageConverters.

Issue: SPR-9160
Backport-Issue: SPR-9168
Backport-Commit: 982cb2f258
2012-06-26 17:28:52 -04:00
Rossen Stoyanchev e66bc07c37 Translate EOF to HttpMessageNotReadableException
The MappingJacksonHttpMessageConverter now catches all IOException
types raised while reading JSON and translates them into
HttpMessageNotReadableException.

Issue: SPR-9238
Backport-Issue: SPR-9397
Backport-Commit: 816c1f47a4
2012-06-26 17:04:44 -04:00
Rossen Stoyanchev c9a2dbdbfd Add Jackson 2 HttpMessageConverter and View
Jackson 2 uses completely new package names and new maven artifact ids.
This change adds Jackson 2 as an optional dependency and also provides
MappingJackson2HttpMessageConverter and MappingJackson2JsonView for use
with the new version.

The MVC namespace and the MVC Java config detect and use
MappingJackson2HttpMessageConverter if Jackson 2 is present.
Otherwise if Jackson 1.x is present,
then MappingJacksonHttpMessageConverter is used.

Issue: SPR-9302
Backport-Issue: SPR-9507
Backport-Commit: e63ca04fdb
2012-06-26 16:52:51 -04:00
Rossen Stoyanchev 2fa0e63e5a Discover controllers based on type @RequestMapping
This was supported in DefaultAnnotationHandlerMapping but not in the
RequestMappingHandlerMapping. The specific scenario where this matters
is a controller decorated with a JDK proxy. In this scenario the
HandlerMapping looks at interfaces only to decide if the bean is a
controller. The @Controller annotation is better left (and required)
on the class.

Issue: SPR-9374
Backport-Issue: SPR-9384
Backport-Commit: f61f4a960e
2012-06-26 15:51:08 -04:00
Rossen Stoyanchev 83ac44d1e4 Fix issue with parsing media types
Invalid Content-Type or Accept header values previously resulted in the
IllegalArgumentException getting propagated. After this change such
errors are detected and generally treated as a "no match", which
may for example result in a 406 in the case of the Accept header.

Issue: SPR-9142
Backport-Issue: SPR-9148
Backport-Commit: ca8b98e947
2012-06-26 13:55:05 -04:00
Rossen Stoyanchev 0cf93711b9 Fix race condition in AnntationMethodHER
Issue: SPR-9138
Backport-Issue: SPR-9147
Backport-Commit: 0b02933938
2012-06-26 13:46:25 -04:00
Rossen Stoyanchev 8faa2e61ec Fix bug with custom RequestCondition
A custom RequestCondition which can be provided by overriding methods
in RequestMappingHandlerMapping worked only for conditions that match
and did not return null (as it should have) for conditions that don't
match.

Issue: SPR-9134
Backport-Issue: SPR-9146
Backport-Commit: 64ee5e579a
2012-06-26 13:40:00 -04:00
Chris Beams 71443efd90 Fix typo in MessageSource reference docs
Issue: SPR-5022
2012-06-19 11:58:41 +02:00
Juergen Hoeller bc243b5aa7 preparations for 3.1.2 release 2012-05-28 00:23:01 +02:00
Juergen Hoeller d36979bc80 fixed WindowState comparison 2012-05-28 00:12:55 +02:00
Juergen Hoeller 5875695d99 polishing 2012-05-28 00:11:55 +02:00
Juergen Hoeller dcb44264ad EhCacheFactoryBean applies listeners and enabled/disabled flags to existing cache regions as well (SPR-9392) 2012-05-28 00:11:19 +02:00
Juergen Hoeller 9fcb578187 fixed StandardServletMultipartResolver compatibility with Resin (SPR-9299) 2012-05-28 00:10:47 +02:00
Juergen Hoeller 8cdfbfe864 ServletContextResource's getFile implementation falls back to getRealPath for non-existent files (SPR-8461) 2012-05-28 00:10:23 +02:00
Oliver Gierke 9ca407d55e Add missing section ids in reference documentation
Add missing id attributes to <section> elements in the reference
documentation to ensure stable anchor links in HTML output.

Issue: SPR-9410
Backport-Issue: SPR-9346
Backport-Commit: 2a75c57d3c
2012-05-15 22:11:23 +03:00
Chris Beams 1380d053c4 Clarify @EnableScheduling javadoc
It is now advised that destroyMethod="shutdown" should be used
on @Bean methods returning an ExecutorService.

Backport-Issue: SPR-9280
Backport-Commit: 6da03a61b22696283c2c5c79f8f88b5c36480560
2012-05-15 22:10:54 +03:00
Chris Beams 6b76afd1ce Fix typo in reference documentation
Issue: SPR-9321
2012-04-13 20:40:21 +03:00
Chris Beams 2fe74a4ef0 Eliminate INDEX.LIST from Spring jar META-INF dirs
The contents of this file could be problematic as they were generated
by spring-build with "org.springframework.core.jar" EBR-style naming,
but this naming is incorrect when dealing with Maven-central style
artifacts, e.g. spring-core.jar.

While a well-formed INDEX.LIST may speed up classloading, the simplest
solution to the issues listed below is simply to eliminate the file.
This also means consistent treatment across 3.1.x and 3.2.x artifacts,
as the new Gradle build in 3.2.x does not create these index files.

Issue: SPR-6383, SPR-9208
2012-04-13 16:58:26 +03:00
Juergen Hoeller ee9b9d6a57 JDBC parameter binding uses JDBC 3.0 ParameterMetaData (if available) for type determination 2012-03-14 16:54:22 +01:00
Juergen Hoeller bf8fa6e268 moved getInputStream() not-null requirement to InputStreamSource itself 2012-03-14 16:40:47 +01:00
Juergen Hoeller c976fa329f initial fixes for 3.1.2 2012-03-14 16:18:06 +01:00
Juergen Hoeller d22c8aca49 moved getInputStream() not-null requirement to InputStreamSource itself 2012-03-14 16:12:57 +01:00
Juergen Hoeller 79d9f7a5f7 JDBC parameter binding uses JDBC 3.0 ParameterMetaData (if available) for type determination 2012-03-14 16:07:39 +01:00
Juergen Hoeller 9d3cfcd8e6 fixed OSGi metadata for optional "javax.inject" package import (SPR-9173) 2012-03-14 16:03:49 +01:00
Chris Beams 7d72ab59ae Return null correctly from MutablePropertySources#get
Prior to this commit, MutablePropertySources#get(String) would throw
IndexArrayOutOfBoundsException if the named property source does not
actually exist. This is a violation of the PropertySource#get contract
as described in its Javadoc.

The implementation now correctly checks for the existence of the named
property source, returning null if non-existent and otherwise returning
the associated PropertySource.

Other changes

 - Rename PropertySourcesTests => MutablePropertySourcesTests
 - Polish MutablePropertySourcesTests for style, formatting
 - Refactor MutablePropertySources for consistency

Issue: SPR-9185
Backport-Issue: SPR-9179
Backport-Commit: 15d1d824b5
2012-02-29 14:47:47 +01:00
Chris Beams 40a6769309 Avoid infinite loop in AbstractResource#contentLength
Due to changes made in commit 2fa87a71 for SPR-9118,
AbstractResource#contentLength would fall into an infinite loop unless
the method were overridden by a subclass (which it is in the majority of
use cases).

This commit:

 - fixes the infinite recursion by refactoring to a while loop

 - asserts that the value returned from #getInputStream is not null in
   order to avoid NullPointerException

 - tests both of the above

 - adds Javadoc to the Resource interface to clearly document that the
   contract for any implementation is that #getInputStream must not
   return null

Issue: SPR-9163
Backport-Issue: SPR-9161
Backport-Commit: 7ca5fba05f
2012-02-24 14:47:02 +01:00
Chris Beams e30d6104f3 Fix regression in @PropertySource placeholder resolution
Changes in commit 41ade68b50 introduced
a regression causing all but the first location in the
@PropertySource#value array to be ignored during ${...} placeholder
resolution. This change ensures that all locations are processed and
replaced as expected.

Issue: SPR-9133
Backport-Issue: SPR-9127
Backport-Commit: 4df2a14b13
2012-02-20 15:27:09 +01:00
Chris Beams 129b4b3136 Update reference doc copyright year to 2012 2012-02-17 01:24:14 +01:00
Spring Buildmaster b32a365f14 Increment version to 3.1.2.BUILD-SNAPSHOT 2012-02-16 15:38:16 -08:00
Spring Buildmaster 79c9ca1a26 Release version 3.1.1.RELEASE 2012-02-16 15:33:27 -08:00
180 changed files with 3929 additions and 1353 deletions
@@ -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)
-------------------------------------
+2 -2
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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>
@@ -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);
}
}
@@ -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;
}
+2 -2
View File
@@ -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>
+2
View File
@@ -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>
+1
View File
@@ -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>
+2 -2
View File
@@ -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>
@@ -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
@@ -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;
@@ -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();
}
@@ -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>&#64;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";
}
@@ -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
@@ -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());
}
}
}
+2 -2
View File
@@ -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>
@@ -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 {
@@ -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;
}
}
@@ -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);
@@ -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() {
+2 -2
View File
@@ -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>
@@ -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;
}
@@ -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);
-6
View File
@@ -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>
+2 -2
View File
@@ -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>
@@ -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;
@@ -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) {
@@ -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;
@@ -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();
}
}
@@ -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;
}
}
}
@@ -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;
}
/**
@@ -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;
@@ -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
}
}
}
}
@@ -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)) {
@@ -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.
@@ -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();
}
}
@@ -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 "";
}
@@ -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) {
@@ -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() {
@@ -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());
* }
*
* &#064;Bean
* &#064;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;
* );
* }
*
* &#064;Bean
* &#064;Bean(destroyMethod="shutdown")
* public Executor taskScheduler() {
* return Executors.newScheduledThreadPool(42);
* }
@@ -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
@@ -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
@@ -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>
@@ -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 { }
}
@@ -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++;
@@ -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;
}
}
}
+1 -1
View File
@@ -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,
+1
View File
@@ -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"/>
+1
View File
@@ -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"/>
+2 -2
View File
@@ -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>
@@ -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
@@ -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);
}
@@ -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;
}
@@ -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'",
@@ -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;
}
@@ -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;
}
@@ -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;
@@ -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);
}
}
@@ -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;
}
}
}
@@ -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);
@@ -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());
}
}
@@ -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 { }
}
@@ -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();
@@ -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);
}
}
@@ -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));
}
}
+2 -2
View File
@@ -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>
+2 -2
View File
@@ -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>
@@ -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
+2 -2
View File
@@ -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>
@@ -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) {
@@ -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) {
@@ -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);
}
}
}
@@ -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();
@@ -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);
}
}
+2 -2
View File
@@ -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>
+2 -2
View File
@@ -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>
@@ -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;
}
}
@@ -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);
@@ -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
+2 -2
View File
@@ -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>
@@ -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.
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+2 -2
View File
@@ -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>
@@ -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);
+2 -2
View File
@@ -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>
@@ -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);
@@ -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 &lt;qualifier<&gt; 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);
}
}
+2 -2
View File
@@ -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>
@@ -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) {
@@ -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)) {
@@ -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);
}
@@ -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());
@@ -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