Compare commits

...

182 Commits

Author SHA1 Message Date
Spring Buildmaster 6c36240055 Release version 3.1.3.RELEASE 2012-10-31 18:12:45 +01:00
Chris Beams a844712908 Update release date in readme 2012-10-31 14:12:11 +01:00
Juergen Hoeller 03c3dc3dae Clarified ServletConfigAware behavior in case of no ServletConfig being available
Issue: SPR-9855
2012-10-31 09:02:48 +01:00
Juergen Hoeller 9caf672f21 Added proper synchronization and not-null check to SimpleMessageListenerContainer's doShutdown
Issue: SPR-9930
2012-10-31 08:53:29 +01:00
Juergen Hoeller 9d78f17176 Adapted PropertySourcesPropertyResolverTests for different error message format 2012-10-31 02:44:48 +01:00
Juergen Hoeller 245d3114a8 Final preparations for 3.1.3 release 2012-10-31 02:23:50 +01:00
Juergen Hoeller 4169898842 Fix circular placeholder prevention
Issue: SPR-5369
2012-10-31 02:18:43 +01:00
Juergen Hoeller e491d990de Minor polishing 2012-10-31 02:10:11 +01:00
Juergen Hoeller 6d6eaf8506 Added test for custom async annotation 2012-10-31 02:09:26 +01:00
Juergen Hoeller a6bcda25c2 Portlet mapping predicate compareTo implementations are transitive now
Also removed unused validateHandler code with dead cachedMappings HashMap.

Issue: SPR-9874
2012-10-31 01:41:19 +01:00
Juergen Hoeller 7702fb8513 Added "jtaDataSource" property to JPA LocalContainerEntityManagerFactoryBean (for default units)
Issue: SPR-9883
2012-10-31 01:08:03 +01:00
Juergen Hoeller b17d53ffae DispatcherPortlet uses a forward for rendering a view as resource response
Issue: SPR-9876
2012-10-31 00:50:46 +01:00
Phillip Webb 7f081a5eb4 Update changelog 2012-10-28 11:59:42 -07:00
Gareth Davis 983e2ce64f Allow 'arg-type' matches against element body
Allow the body of 'arg-type' XML elements to be used as an alternative to
'match' attribute when defining a 'replace-method' in XML configuration.

This change has been introduced primarily to support the samples printed
in the Apress 'Pro Spring' book.

Issue: SPR-9812
Backport-Issue: SPR-9929
Backport-Commit: 0709c033a0
2012-10-28 11:52:22 -07:00
Phillip Webb ea49c8b840 Polish whitespace 2012-10-28 11:13:46 -07:00
Chris Beams 2cfd15899d Fix split package introduced by @EnableSpringConfigured
Commit 6263c9abf9
@EnableSpringConfigured from beans.factory.aspectj =>
context.annotation within the spring-aspects module. This resolved a
package cycle but had the side-effect of causing a "split package" [1]
problem between spring-context and spring-aspects in OSGi-based
classloader environments because the context.annotation package now
exists in both modules.

The simplest and best solution from an OSGi perspective is to relocate
@EnableSpringConfigured and its supporting SpringConfiguredConfiguration
class into a new package. This commit moves both these types into
context.annotation.aspectj, following convention with other such
"aspectj"-qualified packages in the spring-aspects module.

As with the previous move, it is presumed this change will be low-impact
as the "spring-configured" approach to domain object injection is a
niche feature to begin with, and @EnableSpringConfigured has existed in
its current location only since 3.1.2 and this change is being made in
time for 3.1.3.

[1]: http://wiki.osgi.org/wiki/Split_Packages

Issue: SPR-9811, SPR-9441
Backport-Commit: 54db7387de
2012-10-26 14:06:47 +02:00
Sam Brannen d8dc4a257a Support comments in SQL scripts in JdbcTestUtils
Prior to this commit, utility methods in JdbcTestUtils interpreted SQL
comments as separate statements, resulting in an exception when such a
script is executed.

This commit addresses this issue by introducing a
readScript(lineNumberReader, String) method that accepts a comment
prefix. Comment lines are therefore no longer returned in the parsed
script. Furthermore, the existing readScript(lineNumberReader) method
now delegates to this new readScript() method, supplying "--" as the
default comment prefix.

Issue: SPR-9593
Backport-Commit: 4aaf014cc6
2012-10-26 10:45:56 +02:00
Chris Beams 00ed17dca0 Update Apache license header date
Issue: SPR-9853
Backport-Commit: 8a38891e15
2012-10-25 09:24:36 +02:00
Phillip Webb 410b9f933e Update changelog 2012-10-24 11:00:49 -07:00
Phillip Webb a0745c0c0a Support SQL exception translation for ORA-06550
Oracle error ORA-06550 will now raise a BadSqlGrammarException.

Issue: SPR-9853
Backport-Commit: ba5a148d8d
2012-10-24 10:46:06 -07:00
Phillip Webb cfdd50d8ab Polish whitespace 2012-10-24 10:45:29 -07:00
Phillip Webb e3136e65f4 Allow MapToMap conversion even w/out default ctor
MapToMap conversion now works when the target map does not have a
default constructor (as long as a new map copy is not required).

Issue: SPR-9284
Backport-Commit: 38c4393592
2012-10-24 19:31:24 +02:00
Phillip Webb ced6bb4569 Prevent memory leaks with @Configuration beans
Refactor ConfigurationClassEnhancer to allow cglib caching of
generated classes.  Prior to this commit each enhanced @Configuration
class would consume permgen space when created.

The CallbackFilter and Callback Types are now defined as static final
members so that they can be shared by all enhancers.  Only the
callbackInstances remain specific to a @Configuration class and
these are not used by cglib as part of the cache key.

Issue: SPR-9851
Backport-Commit: c8061393fb
2012-10-24 19:12:03 +02:00
Phillip Webb 4525527794 SpEL support for methods and properties on class …
Update the ReflectiveMethodResolver and ReflectivePropertyAccessor
to allow methods and properties of java.lang.Class to be resolved
when the target object is a class.

Issue: SPR-9017
Backport-Commit: d28592a6c6
2012-10-24 18:04:10 +02:00
Phillip Webb 98075c33c6 Chain exception cause on create failure
Propogate root cause of exceptions thrown from createCollection
and createMap.

Issue: SPR-9285
Backport-Commit: 2e48656906
2012-10-24 15:47:11 +02:00
Chris Beams 4cee691d09 Remove accidentally introduced SpEL test method
SpringEL300Tests's #testArray method was originally introduced with
SPR-9203, which has not yet been backported to 3.1.x. During the process
of backporting SPR-9486 functionality, this method was accidentally
introduced, causing false negative test failures. This commit removes
the offending test method.

Issue: SPR-9203, SPR-9486
2012-10-24 14:32:48 +02:00
Chris Beams 347737290e Update SpEL test to reflect native float support
Issue: SPR-9486
Backport-Commit: 59fb67e38d
2012-10-24 14:31:37 +02:00
Satyapal Reddy a94677e7b6 Add SpEL support for float literals
This change ensures that SpEL expressions involving floats are
supported natively as opposed to the previous behavior which required
conversion to double, leading to potential downstream conversion
ambiguities.

Issue: SPR-9486
Backport-Commit: be8f23d756
2012-10-24 14:09:33 +02:00
Phillip Webb ca386c13fc Close 'beans' tag in MVC namespace documentation
Issue: SPR-9898
Backport Issue: SPR-9906
2012-10-22 10:07:51 -07:00
Rossen Stoyanchev f9eacf9c1f Update changelog 2012-10-22 12:41:27 -04:00
Rossen Stoyanchev 88906d271b Improve regex for parsing query params
Previously UriComponentsBuilder used a regular expression for parsing
query name-value pairs where both name and value were expected to not
contain neither '&', not '='. The idea is that the presence of reserved
characters makes it impossible to guess correctly how to parse the
query string (e.g. a=b&c).

This change relaxes the constraint on query param values, allowing them
to contain '='. In effect '&' is the ultimate separator of name-value
pairs, and any '=' in values is ignored. For example "q=1USD=?EUR" is
interpreted as "q equals '1USD=?EUR'".

Issue: SPR-9832
2012-10-22 11:08:56 -04:00
Rossen Stoyanchev d8166d6155 Decode target parameters prior to saving a FlashMap
The target parameters for a FlashMap must be decoded to be able to
match them to the parameters of incoming requests given that the
HttpServletRequest returns decoded request parameters.

Issue: SPR-9657
Backport Issue: SPR-9701
2012-10-14 21:37:29 -04:00
Rossen Stoyanchev 4fccd1799a Fix issue with resolving Errors controller argument
The ErrorsMethodArgumentResolver expects the preceding @ModelAttribute
in the controller method signature to be the last one added in the
model -- an assumption that can break if a model attribute is added
earlier (e.g. through a @ModelAttribute method) and more attributes
are added as well. This fix ensures when an @ModelAttribute is resolved
as a controller method argument it has the highest index in the model.

Issue: SPR-9378
Backport Issue: SPR-9687
2012-10-14 21:28:45 -04:00
Chris Beams 9187eeff27 Make required spring-web's dependence on spring-aop
Prior to this commit spring-web's dependence on spring-aop was marked
optional, however spring-web's required dependence on spring-context
transitively forces a required dependence on spring-aop anyway.

This change marks spring-aop as a required dependency of spring-web,
which a) reflects the poms created by the 3.2 Gradle build and b) helps
solve dependency issues when using higher-level Spring projects such as
Spring Security.

Issue: SPR-9573
2012-10-12 14:24:59 -07:00
Juergen Hoeller 2ab55414e5 Polishing 2012-10-12 23:07:10 +02:00
Juergen Hoeller c90e2c6f10 Made AutoProxyCreatorTests less dependent on container's own interrogation of FactoryBeans
Issue: SPR-9857
2012-10-12 23:06:15 +02:00
Juergen Hoeller 506e9f50d0 LoadTimeWeaverAware beans are consistently being created early for JPA weaving to work reliably
Reverted change for @Bean methods that declare FactoryBean as their return type: The effects of trying to create the FactoryBean to find out about its implementation type are too far-reaching. It's better to recommend declaring a specific return type in the method signature if you want the container to specifically react to your implementation type.

Issue: SPR-9857
2012-10-12 22:56:08 +02:00
Juergen Hoeller 2564db2bfd LoadTimeWeaverAware beans are consistently being created early for JPA weaving to work reliably
Includes a change for factory methods that declare their return type as FactoryBean: When asked for a specific type match (e.g. LoadTimeWeaverAware), we do check early singleton instances as well (reusing the instances that we create for getObjectType checks). This is necessary in order to make @Bean method introspection as capable as XML bean definition introspection, even in case of the @Bean method using a generic FactoryBean declaration for its return type (instead of the FactoryBean impl class).

Issue: SPR-9857
2012-10-12 17:13:07 +02:00
Juergen Hoeller 13edc93f78 Only cache by-type lookups if configuration has been marked as frozen
Issue: SPR-9448
2012-10-12 16:44:04 +02:00
Juergen Hoeller 1d26c37b87 Mention matching bean names in exception message in case of non-unique EntityManagerFactory lookup 2012-10-12 16:26:23 +02:00
Juergen Hoeller ee8e178434 Only cache by-type lookups if configuration has been marked as frozen
Issue: SPR-9448
2012-10-12 16:24:52 +02:00
Juergen Hoeller 679e122326 LoadTimeWeaverAware beans are consistently being created early for JPA weaving to work reliably
Includes a change for factory methods that declare their return type as FactoryBean: When asked for a specific type match (e.g. LoadTimeWeaverAware), we do check early singleton instances as well (reusing the instances that we create for getObjectType checks). This is necessary in order to make @Bean method introspection as capable as XML bean definition introspection, even in case of the @Bean method using a generic FactoryBean declaration for its return type (instead of the FactoryBean impl class).

Issue: SPR-9857
2012-10-12 16:22:11 +02:00
Rossen Stoyanchev 7c1a2f37f2 Fix issue in message conversion
This change ensures that when the Accept and the Producible media types
are equally specific, we use the one from the Accept header, which may
for example carry a different charset.
2012-10-12 09:20:55 -04:00
Chris Beams 5e318e73f8 Update spring-web pom dependency on hessian
Previously, the spring-web pom still depended on the EBR-style
com.springsource artifactId for hessian.

It now depends on Maven Central-style com.caucho:hessian:3.2.1 naming.

Note that Hessian 3.2.1 acutally does not exist in Maven Central, but is
present in the SpringSource repository at http://repo.springsource.org.
In any case, the dependency is optional, so should not cause issues at
build time for applications dependent on spring-web.

Issue: SPR-9854
2012-10-11 15:30:58 -07:00
Juergen Hoeller 682b0f2c63 Jaxb2Marshaller has non-synchronized access to the JAXBContext once initialized
Also backporting "checkForXmlRootElement" property from 3.2 M2

Issue: SPR-9867
Issue: SPR-9757
2012-10-11 22:31:48 +02:00
Juergen Hoeller ad81ec922b OpDivide does not return a TypedValue for its operate result (consistent with OpMultiply)
Issue: SPR-9869
2012-10-11 21:39:12 +02:00
Juergen Hoeller 9862fbfc01 BeanWrapper does not fall back to String constructor if ConversionService attempt failed before
Issue: SPR-9865
2012-10-11 19:42:58 +02:00
Juergen Hoeller d7825586dc Provider injection into prototype beans works for method parameters as well
Issue: SPR-9630
2012-10-11 19:39:37 +02:00
Juergen Hoeller 9338080916 ResourceDatabasePopulator etc 2012-10-11 00:31:06 +02:00
Juergen Hoeller fd10e149f6 Backport "Remove default profile during environment merge"
Issue: SPR-9761
Issue: SPR-9762
2012-10-11 00:30:00 +02:00
Juergen Hoeller 1dd01a7a82 Fixed test failure on CI server (classpath search failing for some reason)
Issue: SPR-9797
2012-10-11 00:23:07 +02:00
Juergen Hoeller 3788635f51 JPA persistence.xml files may use jar-file entries relative to the unit root (as per the JPA spec)
Issue: SPR-9797
2012-10-10 23:33:52 +02:00
Juergen Hoeller 86ef22db49 Backported "Work around JDK7 String#substring performance regression"
Issue: SPR-9781
Issue: SPR-9784
2012-10-10 23:33:51 +02:00
Juergen Hoeller b2fce2f254 Backported "Register environment in all bean factories in a hierarchy"
Issue: SPR-9756
Issue: SPR-9764
2012-10-10 23:33:50 +02:00
Juergen Hoeller be2c2e9a86 Backported "Support opaque URIs in UriComponentsBuilder"
Issue: SPR-9798
Issue: SPR-9804
2012-10-10 23:33:50 +02:00
Rossen Stoyanchev 0047200b98 Fix NPE in AbstractMessageConverterMethodProcessor
Issue: SPR-9868
2012-10-10 09:02:50 -04:00
Juergen Hoeller 1641e082eb Further changes for 3.1.3 2012-10-10 14:28:19 +02:00
Juergen Hoeller baeac9ea6b Polishing 2012-10-10 14:28:18 +02:00
Juergen Hoeller bac05f568a Hibernate 4 LocalSessionFactoryBuilder sets thread context ClassLoader (for JBoss 7 compatibility)
Issue: SPR-9846
2012-10-10 14:28:17 +02:00
Juergen Hoeller cb7d689f92 Consistent "this." reference to local variable 2012-10-10 14:28:16 +02:00
Juergen Hoeller da8e2d6736 AbstractRoutingDataSource consistently implements JDBC 4.0's Wrapper interface as well
Issue: SPR-9856
2012-10-10 14:28:15 +02:00
Juergen Hoeller 6ed589d94b HttpComponentsHttpInvokerRequestExecutor explicitly releases connection on HttpComponents 4.2
Issue: SPR-9833
2012-10-10 14:28:14 +02:00
Chris Beams e70119733a Avoid further 'type mismatch' errors in ExtendedBeanInfo
This change fixes further cases under JDK 6 in which setting a bridged
(e.g. String-returning) read method can conflict with an existing
corresponding bridge write method that accepts an Object parameter.

This appears to be a implementation difference between JDKs 6 and 7,
where the JDK 6 Introspector adds bridge methods and JDK 7 does not.

The solution here is to consistently null-out any existing write method
before setting the read method. We were doing this elsewhere in
ExtendedBeanInfo already, but these two changes make the approach
consistent throuhout.

Issue: SPR-8806
Backport-Commit: 0c0a563a24
2012-10-09 20:16:30 -07:00
Sam Brannen 0824746255 Update Javadoc in ExtendedBeanInfo
- updated the link to the "indexed properties" section of the JavaBeans
   tutorial
2012-10-09 20:16:30 -07:00
Chris Beams 30d0bd309c Address various ExtendedBeanInfo bugs
- Ensure that ExtendedBeanInfoTests succeeds when building under JDK 7

 - Improve handling of read and write method registration where
   generic interfaces are involved, per SPR-9453

 - Add repro test for SPR-9702, in which EBI fails to register
   an indexed read method under certain circumstances

Issue: SPR-9778
Backport-Issue: SPR-9702, SPR-9414, SPR-9453
Backport-Commit: b50bb5071a
2012-10-09 20:15:54 -07:00
Rossen Stoyanchev ec2603de63 Fix issue in AnnotationMethodHandlerExceptionResolver
Caching of resovled exceptions introduced in SPR-7703 also introduced a
side effect whereby if exactly one exception was previously cached, any
other exception would appear as a match to the previously matched
@ExceptionHandler method.

This change ensures use of a fresh map when determining matching
@ExceptionHandler methods while also updating the cache.

Issue: SPR-9209
2012-10-06 22:03:31 -04:00
Rossen Stoyanchev 470c85ade0 Update MediaType's includes method
An additional update (after the last commit) of the "includes" and
"isCompatibleWith" methods of MediaType to accomodate wildcards
in media types with a suffix.

Issue: SPR-9841
2012-10-06 11:07:47 -04:00
Rossen Stoyanchev 01d8d64200 Recognize wildcards in media types with a suffix
The "includes" and "isCompatibleWith" methods of MediaType take into
account media types with suffices (e.g. application/soap+xml) including
wildcards with suffices (e.g. application/*+xml). However before this
change, the isWildcardSubtype() method returned true only for subtype
"*". Now a media type such as application/*+xml is also recognized as
having a wildcard subtype.

Issue: SPR-9841
2012-10-06 10:20:14 -04:00
Juergen Hoeller 2c8b7fe093 ClassUtils.getMostSpecificMethod uses Class.getMethod code path in case of a public method
This should be significantly faster than our standard algorithm, for a very common case. Motivated by SPR-9802, even if the fix there uses a different approach, with transaction name determination not calling getMostSpecificMethod at all anymore.

Issue: SPR-9802
2012-09-26 20:04:43 +02:00
Juergen Hoeller bbfc807b0c TransactionInterceptor avoids reflective method search for method identification
As of Spring 3.0.4, we were trying to expose the target method signature as transaction name. Unfortunately, the algorithm called the ClassUtils.getMostSpecificMethod helper method which performs a quite expensive reflective search. As of this commit, we're simply concatenating the target class name with the method name, accepting the use of the concrete target class (which is arguably more meaningful for monitoring anyway) even when the method implementation actually sits on a base class.

Issue: SPR-9802
2012-09-26 19:31:05 +02:00
Juergen Hoeller c81543e1a4 ResourceBundleMessageSource supports "defaultEncoding", "fallbackToSystemLocale", "cacheSeconds"
These features require Java 6 or higher due to their dependency on the ResourceBundle.Control class. To some degree, ResourceBundleMessageSource catches up with ReloadableResourceBundleMessageSource now. However, as noted in the javadoc, there are still severe limitations in the standard ResourceBundle class that justify an ongoing investment in our own ReloadableResourceBundleMessageSource (based on the Spring resource abstraction, with manual parsing of properties files).

Issue: SPR-7392
2012-09-26 16:46:25 +02:00
Juergen Hoeller 437ce9bb0e Calling cancel on a Future returned by a TaskScheduler works reliably now
Issue: SPR-9821
2012-09-25 13:54:33 +02:00
Juergen Hoeller b79260a017 Fixed potential race condition in concurrent calling of autowired methods on a prototype bean
Autowired methods might have been skipped on subsequent creation of further bean instances due to the 'skip' flag set to false outside of the synchronized block, with another thread entering the block and setting the flag to true in the meantime.

Issue: SPR-9806
2012-09-25 12:50:10 +02:00
Juergen Hoeller 0f522cfe6a Calling cancel on a Future returned by a TaskScheduler works reliably now
Issue: SPR-9821
2012-09-25 12:07:06 +02:00
Juergen Hoeller da75eaedbf Updated changelog for 3.1.3 alongside 3.2 RC1 2012-09-24 23:17:48 +02:00
Juergen Hoeller 69f2496cd7 ImportAwareBeanPostProcessor registered with ROLE_INFRASTRUCTURE 2012-09-24 11:27:40 +02:00
Juergen Hoeller e670975fde CookieGenerator supports "cookieHttpOnly" flag for Servlet 3.0
Issue: SPR-9794
2012-09-24 11:27:39 +02:00
Rossen Stoyanchev 41d9d737ac Cache AntPathStringMatcher instances
AntPathMatcher now caches AntPathStringMatcher instances by pattern
thus avoiding java.util.regex.Pattern recompilation.

Issue: SPR-9749
2012-09-10 17:04:12 -04:00
Juergen Hoeller c25cbe5522 @Import'ed configuration classes get properly registered in case of same class name (second try)
Issue: SPR-9243
2012-09-10 22:13:32 +02:00
Rossen Stoyanchev 8c64b47473 Update changelog 2012-09-10 15:19:47 -04:00
Rossen Stoyanchev 229537288a Backport fixes in HandlerMethod and sub-classes
Issue: SPR-9747, SPR-9748, SPR-9218, SPR-8946, SPR-9159
Backport Issue: SPR-9622
2012-09-10 15:01:41 -04:00
Juergen Hoeller bec5463640 @Import'ed configuration classes get properly registered in case of same class name
Issue: SPR-9243
2012-09-10 15:43:20 +02:00
Juergen Hoeller 7ebfd9e6a8 BeanFactoryAnnotationUtils throws NoSuchBeanDefinitionExceptions instead of IllegalStateExceptions
Issue: SPR-9652
2012-09-10 15:26:27 +02:00
Juergen Hoeller f9147d375f Expression detection properly handles cached String literals
Issue: SPR-9670
2012-09-10 11:10:16 +02:00
Juergen Hoeller 03dd7a57f0 MutablePropertySources log statements guarded by isDebugEnabled
Issue: SPR-9670
2012-09-10 11:05:29 +02:00
Juergen Hoeller 43bc3f19d9 Spring-backed DataSources consistently implement JDBC 4.0's Wrapper interface
Issue: SPR-9770
2012-09-10 10:41:11 +02:00
Juergen Hoeller 0cf4a2626b same-named unit from persistence.xml overrides in case of conflict with default unit
Issue: SPR-9741
2012-09-06 19:22:50 +02:00
Juergen Hoeller a0d0b37d10 polishing 2012-09-06 19:00:01 +02:00
Juergen Hoeller 5a149e4600 @Import'ed configuration classes get properly registered in case of same class name
Issue: SPR-9243
2012-09-06 18:59:45 +02:00
Juergen Hoeller 25a806a54e LinkedCaseInsensitiveMap checks for uniqueness of case-insensitive keys now
Issue: SPR-9754
2012-09-05 13:44:36 +02:00
Juergen Hoeller 33463a94e3 refined TypeConverterDelegate's ConversionService exception handling
Issue: SPR-9498
2012-09-05 01:00:10 +02:00
Juergen Hoeller a6f5d9eb93 initial changelog for 3.1.3 release 2012-09-04 22:39:03 +02:00
Juergen Hoeller f11ef28a4d refined IntelliJ formatting settings 2012-09-04 22:37:48 +02:00
Juergen Hoeller 52c10c52cd CollectionFactory should be aware of MultiValueMap
Issue: SPR-9498
2012-09-04 18:04:54 +02:00
Juergen Hoeller cb13353c1d don't make assumptions about equality if ConversionService has failed
Issue: SPR-9498
2012-09-04 18:04:02 +02:00
Juergen Hoeller 8b09b52720 media types in HTTP Accept headers can be parsed with single quotes (-> Android 2.x)
Issue: SPR-9734
2012-08-31 17:03:28 +02:00
Juergen Hoeller d7fd70fa08 deprecated SimpleJdbcTestUtils in favor of JdbcTestUtils; introduced "countRowsInTableWhere()" and "dropTables()" in JdbcTestUtils
Issue: SPR-9235
Issue: SPR-9664
2012-08-31 16:51:34 +02:00
Juergen Hoeller fbe955ab28 @Autowired-driven ObjectFactory/Provider resolution works in non-singleton beans as well
Issue: SPR-9181
2012-08-31 15:58:41 +02:00
Juergen Hoeller 26ee0c4842 aligned with recent changes in CommonAnnotationBeanPostProcessor; backported NPE check
Issue: SPR-9176
Issue: SPR-9627
Issue: SPR-9316
2012-08-31 11:55:10 +02:00
Juergen Hoeller 19718700cf @Resource processing properly works with scoped beans and prototypes again
Issue: SPR-9627
2012-08-31 11:49:32 +02:00
Juergen Hoeller 8599dfd74e added Hamcrest dependency to core module 2012-08-30 16:30:46 +02:00
Juergen Hoeller a3bfa754fd MessageSourceSupport uses locale-specific MessageFormat cache for default messages
Issue: SPR-9607
2012-08-30 16:30:08 +02:00
Juergen Hoeller 8e3a0070dc backported "toCode"/"toCodeForSuffix" fixes for handling of null prefix/suffix
Issue: SPR-9608
2012-08-29 22:22:22 +02:00
Juergen Hoeller f9cf5d1aa6 fixed Portlet request mapping priorities in cross-controller case
Issue: SPR-9303
Issue: SPR-9605
2012-08-29 22:22:21 +02:00
Rossen Stoyanchev f78bef906d Improve no-match handling for @RequestMapping methods
Issue: SPR-9603
2012-08-28 16:09:46 -04:00
Juergen Hoeller 7704d76144 minor IntelliJ settings update 2012-08-28 19:45:42 +02:00
Juergen Hoeller 3f48c71c77 Backported recent Jaxb2Marshaller changes
Issue: SPR-9152
2012-08-28 19:41:39 +02:00
Juergen Hoeller 576f6fd25c StaxEventContentHandler uses static inner class for Location adapter, in order to avoid leaks when caching events
Issue: SPR-9305
2012-08-28 19:22:20 +02:00
Juergen Hoeller 5204e11d9c Backported recent "cannotAcquireLockCodes" additions
Issue: SPR-9141
Issue: SPR-9681
2012-08-28 18:56:20 +02:00
Juergen Hoeller 30166981e2 ResourceUtils.useCachesIfNecessary() not correct handle JNLP connections
Issue: SPR-9547
2012-08-28 18:35:56 +02:00
Juergen Hoeller 617f0ef542 Fix typo in new-in-3.1.html page
Issue: SPR-9596
2012-08-28 18:26:28 +02:00
Juergen Hoeller dc400c7124 Typo in ApplicationContextAware and BeanNameAware section
Issue: SPR-9609
2012-08-28 18:25:29 +02:00
Rossen Stoyanchev a7e2297432 Use a default for INTROSPECT_TYPE_LEVEL_MAPPING
Usually this request attribute is set for all sub-classes of
AbstractUrlHandlerMapping and therefore whenever
AnnotationMethodHandlerAdapter is used. However, having a
default value to fall back on in AnnotationMethodHandlerAdapter
is still appropriate in general and also considering the Javadoc
of HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING.

Issue: SPR-9629
Backport-Issue: SPR-9633
2012-07-25 19:45:11 -04:00
Spring Buildmaster 671f97721f Increment version to 3.1.3.BUILD-SNAPSHOT 2012-07-07 20:05:06 +02:00
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
302 changed files with 11513 additions and 3949 deletions
@@ -3,6 +3,83 @@ SPRING FRAMEWORK CHANGELOG
http://www.springsource.org
Changes in version 3.1.3 (2012-10-31)
-------------------------------------
* fixed cache handling for JNLP connections
* LinkedCaseInsensitiveMap checks for uniqueness of case-insensitive keys now
* ResourceBundleMessageSource supports "defaultEncoding", "fallbackToSystemLocale", "cacheSeconds"
* BeanWrapper does not fall back to String constructor if ConversionService attempt failed before
* @Import'ed configuration classes get properly registered in case of same class name
* @Autowired-driven ObjectFactory/Provider resolution works in non-singleton beans as well
* @Resource processing properly works with scoped beans and prototypes again
* allow replaced-method 'arg-type' matches against element body as well as 'match' attribute
* fixed potential race condition in concurrent calling of autowired methods on a prototype bean
* cancel on a Future returned by a TaskScheduler works reliably
* LoadTimeWeaverAware beans are consistently being created early for JPA weaving to work reliably
* Spring-backed DataSources consistently implement JDBC 4.0's Wrapper interface
* optimized ResourceDatabasePopulator to work around JDK 1.7 substring performance regression
* deprecated SimpleJdbcTestUtils in favor of JdbcTestUtils
* introduced "countRowsInTableWhere()" and "dropTables()" in JdbcTestUtils
* same-named unit from persistence.xml overrides in case of conflict with default unit
* JPA persistence.xml files may use jar-file entries relative to the unit root (as per the JPA spec)
* added "jtaDataSource" property to JPA LocalContainerEntityManagerFactoryBean (for default units)
* Hibernate 4 LocalSessionFactoryBuilder sets thread context ClassLoader (for JBoss 7 compatibility)
* Jaxb2Marshaller performs proper "supports" check for scanned packages
* media types in HTTP Accept headers can be parsed with single quotes (-> Android 2.x)
* MediaType's include method now recognizes wildcards in media types with a suffix
* added support for recognizing wildcards in media types with a suffix
* UriComponentsBuilder is capable of handling opaque URIs as well
* CookieGenerator supports "cookieHttpOnly" flag for Servlet 3.0
* HttpComponentsHttpInvokerRequestExecutor explicitly releases connection on HttpComponents 4.2
* improved no-match handling for @RequestMapping methods
* dynamic return value types for @MVC methods
* fixed issue with resolution of WebDataBinder argument
* fixed issue with resolving Errors controller argument
* now decoding target parameters prior to saving a FlashMap
* prevent response updates if @ResponseStatus has a reason
* improved regex for parsing query params
* optimized use of AntPathStringMatcher
* optimized use of HandlerMethod and subclasses
* fixed issue in message conversion where producible and acceptable headers are equally specific
* fixed NullPointerException in AbstractMessageConverterMethodProcessor
* fixed side effect from caching in AnnotationMethodHandlerExceptionResolver
* DispatcherPortlet uses a forward for rendering a view as resource response
* fixed Portlet request mapping priorities in cross-controller case
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.3 (October 2012)
---------------------------------------
http://www.springsource.org
1. INTRODUCTION
+8 -5
View File
@@ -1,4 +1,4 @@
version=3.1.1
version=3.1.3
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.3.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.3.RELEASE</version>
</parent>
<licenses>
<license>
@@ -0,0 +1,125 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.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;
}
/**
* 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 != null) {
this.executors.put(method, new TaskExecutorAdapter(executor));
}
}
return this.executors.get(method);
}
/**
* 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);
}
@@ -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.3.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.3.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.3.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.3.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.aspectj;
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.aspectj;
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,19 @@
* limitations under the License.
*/
package org.springframework.beans.factory.aspectj;
package org.springframework.context.annotation.aspectj;
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;
import org.springframework.context.annotation.aspectj.EnableSpringConfigured;
/**
* 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.3.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.3.RELEASE</version>
</parent>
<licenses>
<license>
@@ -34,6 +34,7 @@ import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
@@ -44,7 +45,7 @@ import org.springframework.util.StringUtils;
* {@link Introspector#getBeanInfo(Class)}) by including non-void returning setter
* methods in the collection of {@link #getPropertyDescriptors() property descriptors}.
* Both regular and
* <a href="http://download.oracle.com/javase/tutorial/javabeans/properties/indexed.html">
* <a href="http://docs.oracle.com/javase/tutorial/javabeans/writing/properties.html">
* indexed properties</a> are fully supported.
*
* <p>The wrapped {@code BeanInfo} object is not modified in any way.
@@ -77,7 +78,7 @@ class ExtendedBeanInfo implements BeanInfo {
ALL_METHODS:
for (MethodDescriptor md : delegate.getMethodDescriptors()) {
Method method = md.getMethod();
Method method = resolveMethod(md.getMethod());
// bypass non-getter java.lang.Class methods for efficiency
if (ReflectionUtils.isObjectMethod(method) && !method.getName().startsWith("get")) {
@@ -91,8 +92,8 @@ class ExtendedBeanInfo implements BeanInfo {
continue ALL_METHODS;
}
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
Method readMethod = pd.getReadMethod();
Method writeMethod = pd.getWriteMethod();
Method readMethod = readMethodFor(pd);
Method writeMethod = writeMethodFor(pd);
// has the setter already been found by the wrapped BeanInfo?
if (writeMethod != null
&& writeMethod.getName().equals(method.getName())) {
@@ -126,10 +127,10 @@ class ExtendedBeanInfo implements BeanInfo {
continue DELEGATE_PD;
}
IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
Method readMethod = ipd.getReadMethod();
Method writeMethod = ipd.getWriteMethod();
Method indexedReadMethod = ipd.getIndexedReadMethod();
Method indexedWriteMethod = ipd.getIndexedWriteMethod();
Method readMethod = readMethodFor(ipd);
Method writeMethod = writeMethodFor(ipd);
Method indexedReadMethod = indexedReadMethodFor(ipd);
Method indexedWriteMethod = indexedWriteMethodFor(ipd);
// has the setter already been found by the wrapped BeanInfo?
if (!(indexedWriteMethod != null
&& indexedWriteMethod.getName().equals(method.getName()))) {
@@ -149,26 +150,26 @@ class ExtendedBeanInfo implements BeanInfo {
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
// have we already copied this read method to a property descriptor locally?
String propertyName = pd.getName();
Method readMethod = pd.getReadMethod();
Method readMethod = readMethodFor(pd);
Method mostSpecificReadMethod = ClassUtils.getMostSpecificMethod(readMethod, method.getDeclaringClass());
for (PropertyDescriptor existingPD : this.propertyDescriptors) {
if (method.equals(mostSpecificReadMethod)
&& existingPD.getName().equals(propertyName)) {
if (existingPD.getReadMethod() == null) {
if (readMethodFor(existingPD) == null) {
// no -> add it now
this.addOrUpdatePropertyDescriptor(pd, propertyName, method, pd.getWriteMethod());
this.addOrUpdatePropertyDescriptor(pd, propertyName, method, writeMethodFor(pd));
}
// yes -> do not add a duplicate
continue ALL_METHODS;
}
}
if (method.equals(mostSpecificReadMethod)
|| (pd instanceof IndexedPropertyDescriptor && method.equals(((IndexedPropertyDescriptor) pd).getIndexedReadMethod()))) {
|| (pd instanceof IndexedPropertyDescriptor && method.equals(indexedReadMethodFor((IndexedPropertyDescriptor) pd)))) {
// yes -> copy it, including corresponding setter method (if any -- may be null)
if (pd instanceof IndexedPropertyDescriptor) {
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, pd.getWriteMethod(), ((IndexedPropertyDescriptor)pd).getIndexedReadMethod(), ((IndexedPropertyDescriptor)pd).getIndexedWriteMethod());
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethodFor(pd), indexedReadMethodFor((IndexedPropertyDescriptor)pd), indexedWriteMethodFor((IndexedPropertyDescriptor)pd));
} else {
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, pd.getWriteMethod());
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethodFor(pd));
}
continue ALL_METHODS;
}
@@ -176,6 +177,27 @@ class ExtendedBeanInfo implements BeanInfo {
}
}
private static Method resolveMethod(Method method) {
return BridgeMethodResolver.findBridgedMethod(method);
}
private static Method readMethodFor(PropertyDescriptor pd) {
return resolveMethod(pd.getReadMethod());
}
private static Method writeMethodFor(PropertyDescriptor pd) {
return resolveMethod(pd.getWriteMethod());
}
private static Method indexedReadMethodFor(IndexedPropertyDescriptor ipd) {
return resolveMethod(ipd.getIndexedReadMethod());
}
private static Method indexedWriteMethodFor(IndexedPropertyDescriptor ipd) {
return resolveMethod(ipd.getIndexedWriteMethod());
}
private void addOrUpdatePropertyDescriptor(PropertyDescriptor pd, String propertyName, Method readMethod, Method writeMethod) throws IntrospectionException {
addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethod, null, null);
}
@@ -186,9 +208,9 @@ class ExtendedBeanInfo implements BeanInfo {
for (PropertyDescriptor existingPD : this.propertyDescriptors) {
if (existingPD.getName().equals(propertyName)) {
// is there already a descriptor that captures this read method or its corresponding write method?
if (existingPD.getReadMethod() != null) {
if (readMethod != null && existingPD.getReadMethod().getReturnType() != readMethod.getReturnType()
|| writeMethod != null && existingPD.getReadMethod().getReturnType() != writeMethod.getParameterTypes()[0]) {
if (readMethodFor(existingPD) != null) {
if (readMethod != null && readMethodFor(existingPD).getReturnType() != readMethod.getReturnType()
|| writeMethod != null && readMethodFor(existingPD).getReturnType() != writeMethod.getParameterTypes()[0]) {
// no -> add a new descriptor for it below
break;
}
@@ -205,9 +227,9 @@ class ExtendedBeanInfo implements BeanInfo {
}
// is there already a descriptor that captures this write method or its corresponding read method?
if (existingPD.getWriteMethod() != null) {
if (readMethod != null && existingPD.getWriteMethod().getParameterTypes()[0] != readMethod.getReturnType()
|| writeMethod != null && existingPD.getWriteMethod().getParameterTypes()[0] != writeMethod.getParameterTypes()[0]) {
if (writeMethodFor(existingPD) != null) {
if (readMethod != null && writeMethodFor(existingPD).getParameterTypes()[0] != readMethod.getReturnType()
|| writeMethod != null && writeMethodFor(existingPD).getParameterTypes()[0] != writeMethod.getParameterTypes()[0]) {
// no -> add a new descriptor for it below
break;
}
@@ -224,9 +246,9 @@ class ExtendedBeanInfo implements BeanInfo {
IndexedPropertyDescriptor existingIPD = (IndexedPropertyDescriptor) existingPD;
// is there already a descriptor that captures this indexed read method or its corresponding indexed write method?
if (existingIPD.getIndexedReadMethod() != null) {
if (indexedReadMethod != null && existingIPD.getIndexedReadMethod().getReturnType() != indexedReadMethod.getReturnType()
|| indexedWriteMethod != null && existingIPD.getIndexedReadMethod().getReturnType() != indexedWriteMethod.getParameterTypes()[1]) {
if (indexedReadMethodFor(existingIPD) != null) {
if (indexedReadMethod != null && indexedReadMethodFor(existingIPD).getReturnType() != indexedReadMethod.getReturnType()
|| indexedWriteMethod != null && indexedReadMethodFor(existingIPD).getReturnType() != indexedWriteMethod.getParameterTypes()[1]) {
// no -> add a new descriptor for it below
break;
}
@@ -243,9 +265,9 @@ class ExtendedBeanInfo implements BeanInfo {
}
// is there already a descriptor that captures this indexed write method or its corresponding indexed read method?
if (existingIPD.getIndexedWriteMethod() != null) {
if (indexedReadMethod != null && existingIPD.getIndexedWriteMethod().getParameterTypes()[1] != indexedReadMethod.getReturnType()
|| indexedWriteMethod != null && existingIPD.getIndexedWriteMethod().getParameterTypes()[1] != indexedWriteMethod.getParameterTypes()[1]) {
if (indexedWriteMethodFor(existingIPD) != null) {
if (indexedReadMethod != null && indexedWriteMethodFor(existingIPD).getParameterTypes()[1] != indexedReadMethod.getReturnType()
|| indexedWriteMethod != null && indexedWriteMethodFor(existingIPD).getParameterTypes()[1] != indexedWriteMethod.getParameterTypes()[1]) {
// no -> add a new descriptor for it below
break;
}
@@ -279,6 +301,7 @@ class ExtendedBeanInfo implements BeanInfo {
}
}
else {
pd.setWriteMethod(null);
pd.setReadMethod(readMethod);
try {
pd.setWriteMethod(writeMethod);
@@ -288,6 +311,7 @@ class ExtendedBeanInfo implements BeanInfo {
// fall through -> add property descriptor as best we can
}
if (pd instanceof IndexedPropertyDescriptor) {
((IndexedPropertyDescriptor)pd).setIndexedWriteMethod(null);
((IndexedPropertyDescriptor)pd).setIndexedReadMethod(indexedReadMethod);
try {
((IndexedPropertyDescriptor)pd).setIndexedWriteMethod(indexedWriteMethod);
@@ -349,7 +373,8 @@ class ExtendedBeanInfo implements BeanInfo {
/**
* Sorts PropertyDescriptor instances alphanumerically to emulate the behavior of {@link java.beans.BeanInfo#getPropertyDescriptors()}.
* Sorts PropertyDescriptor instances alpha-numerically to emulate the behavior of
* {@link java.beans.BeanInfo#getPropertyDescriptors()}.
*
* @see ExtendedBeanInfo#propertyDescriptors
*/
@@ -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.
@@ -44,6 +44,7 @@ import org.springframework.util.StringUtils;
*
* @author Juergen Hoeller
* @author Rob Harrop
* @author Dave Syer
* @since 2.0
* @see BeanWrapperImpl
* @see SimpleTypeConverter
@@ -158,11 +159,13 @@ class TypeConverterDelegate {
}
}
if (editor == null) {
editor = findDefaultEditor(requiredType, typeDescriptor);
editor = findDefaultEditor(requiredType);
}
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}
boolean standardConversion = false;
if (requiredType != null) {
// Try to apply some standard type conversion rules if appropriate.
@@ -178,21 +181,24 @@ class TypeConverterDelegate {
// Convert elements to target type, if determined.
convertedValue = convertToTypedCollection(
(Collection) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
else if (convertedValue instanceof Map) {
// Convert keys and values to respective target type, if determined.
convertedValue = convertToTypedMap(
(Map) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
convertedValue = Array.get(convertedValue, 0);
standardConversion = true;
}
if (String.class.equals(requiredType) && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
// We can stringify any primitive value...
return (T) convertedValue.toString();
}
else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
if (!requiredType.isInterface() && !requiredType.isEnum()) {
if (firstAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) {
try {
Constructor strCtor = requiredType.getConstructor(String.class);
return (T) BeanUtils.instantiateClass(strCtor, convertedValue);
@@ -214,8 +220,8 @@ class TypeConverterDelegate {
// It's an empty enum identifier: reset the enum value to null.
return null;
}
convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
standardConversion = true;
}
}
@@ -244,7 +250,7 @@ class TypeConverterDelegate {
}
if (firstAttemptEx != null) {
if (editor == null && convertedValue == newValue) {
if (editor == null && !standardConversion && requiredType != null && !Object.class.equals(requiredType)) {
throw firstAttemptEx;
}
logger.debug("Original ConversionService attempt failed - ignored since " +
@@ -303,16 +309,11 @@ class TypeConverterDelegate {
/**
* Find a default editor for the given type.
* @param requiredType the type to find an editor for
* @param descriptor the JavaBeans descriptor for the property
* @return the corresponding editor, or <code>null</code> if none
*/
protected PropertyEditor findDefaultEditor(Class requiredType, TypeDescriptor typeDescriptor) {
private PropertyEditor findDefaultEditor(Class requiredType) {
PropertyEditor editor = null;
//if (typeDescriptor instanceof PropertyTypeDescriptor) {
//PropertyDescriptor pd = ((PropertyTypeDescriptor) typeDescriptor).getPropertyDescriptor();
//editor = pd.createPropertyEditor(this.targetObject);
//}
if (editor == null && requiredType != null) {
if (requiredType != null) {
// No custom editor -> check BeanWrapperImpl's default editors.
editor = this.propertyEditorRegistry.getDefaultEditor(requiredType);
if (editor == null && !String.class.equals(requiredType)) {
@@ -334,7 +335,7 @@ class TypeConverterDelegate {
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
protected Object doConvertValue(Object oldValue, Object newValue, Class<?> requiredType, PropertyEditor editor) {
private Object doConvertValue(Object oldValue, Object newValue, Class<?> requiredType, PropertyEditor editor) {
Object convertedValue = newValue;
boolean sharedEditor = false;
@@ -421,7 +422,7 @@ class TypeConverterDelegate {
* @param editor the PropertyEditor to use
* @return the converted value
*/
protected Object doConvertTextValue(Object oldValue, String newTextValue, PropertyEditor editor) {
private Object doConvertTextValue(Object oldValue, String newTextValue, PropertyEditor editor) {
try {
editor.setValue(oldValue);
}
@@ -435,7 +436,7 @@ class TypeConverterDelegate {
return editor.getValue();
}
protected Object convertToTypedArray(Object input, String propertyName, Class<?> componentType) {
private Object convertToTypedArray(Object input, String propertyName, Class<?> componentType) {
if (input instanceof Collection) {
// Convert Collection elements to array elements.
Collection coll = (Collection) input;
@@ -474,7 +475,7 @@ class TypeConverterDelegate {
}
@SuppressWarnings("unchecked")
protected Collection convertToTypedCollection(
private Collection convertToTypedCollection(
Collection original, String propertyName, Class requiredType, TypeDescriptor typeDescriptor) {
if (!Collection.class.isAssignableFrom(requiredType)) {
@@ -556,7 +557,7 @@ class TypeConverterDelegate {
}
@SuppressWarnings("unchecked")
protected Map convertToTypedMap(
private Map convertToTypedMap(
Map original, String propertyName, Class requiredType, TypeDescriptor typeDescriptor) {
if (!Map.class.isAssignableFrom(requiredType)) {
@@ -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 {
@@ -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.
@@ -405,10 +405,16 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
protected boolean determineRequiredStatus(Annotation annotation) {
try {
Method method = ReflectionUtils.findMethod(annotation.annotationType(), this.requiredParameterName);
if (method == null) {
// annotations like @Inject and @Value don't have a method (attribute) named "required"
// -> default to required status
return true;
}
return (this.requiredParameterValue == (Boolean) ReflectionUtils.invokeMethod(method, annotation));
}
catch (Exception ex) {
// required by default
// an exception was thrown during reflective invocation of the required attribute
// -> default to required status
return true;
}
}
@@ -419,11 +425,12 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
private void registerDependentBeans(String beanName, Set<String> autowiredBeanNames) {
if (beanName != null) {
for (String autowiredBeanName : autowiredBeanNames) {
beanFactory.registerDependentBean(autowiredBeanName, beanName);
if (this.beanFactory.containsBean(autowiredBeanName)) {
this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
}
if (logger.isDebugEnabled()) {
logger.debug(
"Autowiring by type from bean name '" + beanName + "' to bean named '" + autowiredBeanName +
"'");
logger.debug("Autowiring by type from bean name '" + beanName +
"' to bean named '" + autowiredBeanName + "'");
}
}
}
@@ -435,11 +442,11 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
private Object resolvedCachedArgument(String beanName, Object cachedArgument) {
if (cachedArgument instanceof DependencyDescriptor) {
DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument;
TypeConverter typeConverter = beanFactory.getTypeConverter();
return beanFactory.resolveDependency(descriptor, beanName, null, typeConverter);
TypeConverter typeConverter = this.beanFactory.getTypeConverter();
return this.beanFactory.resolveDependency(descriptor, beanName, null, typeConverter);
}
else if (cachedArgument instanceof RuntimeBeanReference) {
return beanFactory.getBean(((RuntimeBeanReference) cachedArgument).getBeanName());
return this.beanFactory.getBean(((RuntimeBeanReference) cachedArgument).getBeanName());
}
else {
return cachedArgument;
@@ -0,0 +1,137 @@
/*
* 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 beanFactory 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 NoSuchBeanDefinitionException 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 NoSuchBeanDefinitionException(qualifier, "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 NoSuchBeanDefinitionException 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 NoSuchBeanDefinitionException(qualifier, "No unique " + beanType.getSimpleName() +
" bean found for qualifier '" + qualifier + "'");
}
matchingBean = candidateBeans.get(beanName);
}
}
if (matchingBean != null) {
return matchingBean;
}
else {
throw new NoSuchBeanDefinitionException(qualifier, "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;
}
}
@@ -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.
@@ -54,17 +54,22 @@ public class InjectionMetadata {
public InjectionMetadata(Class targetClass, Collection<InjectedElement> elements) {
this.injectedElements = Collections.synchronizedSet(new LinkedHashSet<InjectedElement>());
for (InjectedElement element : elements) {
if (logger.isDebugEnabled()) {
logger.debug("Found injected element on class [" + targetClass.getName() + "]: " + element);
if (!elements.isEmpty()) {
this.injectedElements = Collections.synchronizedSet(new LinkedHashSet<InjectedElement>(elements.size()));
for (InjectedElement element : elements) {
if (logger.isDebugEnabled()) {
logger.debug("Found injected element on class [" + targetClass.getName() + "]: " + element);
}
this.injectedElements.add(element);
}
this.injectedElements.add(element);
}
else {
this.injectedElements = Collections.emptySet();
}
}
public void checkConfigMembers(RootBeanDefinition beanDefinition) {
synchronized(this.injectedElements) {
synchronized (this.injectedElements) {
for (Iterator<InjectedElement> it = this.injectedElements.iterator(); it.hasNext();) {
Member member = it.next().getMember();
if (!beanDefinition.isExternallyManagedConfigMember(member)) {
@@ -170,26 +175,30 @@ public class InjectionMetadata {
* affected property as processed for other processors to ignore it.
*/
protected boolean checkPropertySkipping(PropertyValues pvs) {
if (this.skip == null) {
if (pvs != null) {
synchronized (pvs) {
if (this.skip == null) {
if (this.pd != null) {
if (pvs.contains(this.pd.getName())) {
// Explicit value provided as part of the bean definition.
this.skip = true;
return true;
}
else if (pvs instanceof MutablePropertyValues) {
((MutablePropertyValues) pvs).registerProcessedProperty(this.pd.getName());
}
}
}
if (this.skip != null) {
return this.skip;
}
if (pvs == null) {
this.skip = false;
return false;
}
synchronized (pvs) {
if (this.skip != null) {
return this.skip;
}
if (this.pd != null) {
if (pvs.contains(this.pd.getName())) {
// Explicit value provided as part of the bean definition.
this.skip = true;
return true;
}
else if (pvs instanceof MutablePropertyValues) {
((MutablePropertyValues) pvs).registerProcessedProperty(this.pd.getName());
}
}
this.skip = false;
return false;
}
return this.skip;
}
/**
@@ -121,6 +121,24 @@ public class DependencyDescriptor implements Serializable {
this.eager = eager;
}
/**
* Copy constructor.
* @param original the original descriptor to create a copy from
*/
public DependencyDescriptor(DependencyDescriptor original) {
this.methodParameter = (original.methodParameter != null ? new MethodParameter(original.methodParameter) : null);
this.field = original.field;
this.declaringClass = original.declaringClass;
this.methodName = original.methodName;
this.parameterTypes = original.parameterTypes;
this.parameterIndex = original.parameterIndex;
this.fieldName = original.fieldName;
this.required = original.required;
this.eager = original.eager;
this.nestingLevel = original.nestingLevel;
this.fieldAnnotations = original.fieldAnnotations;
}
/**
* Return the wrapped MethodParameter, if any.
@@ -156,6 +174,10 @@ public class DependencyDescriptor implements Serializable {
}
/**
* Increase this descriptor's nesting level.
* @see MethodParameter#increaseNestingLevel()
*/
public void increaseNestingLevel() {
this.nestingLevel++;
if (this.methodParameter != null) {
@@ -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.
@@ -672,12 +672,12 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
// Try to obtain the FactoryBean's object type without instantiating it at all.
BeanDefinition fbDef = getBeanDefinition(factoryBeanName);
if (fbDef instanceof AbstractBeanDefinition) {
Class<?> fbClass = ((AbstractBeanDefinition)fbDef).getBeanClass();
Class<?> fbClass = ((AbstractBeanDefinition) fbDef).getBeanClass();
if (ClassUtils.isCglibProxyClass(fbClass)) {
// CGLIB subclass methods hide generic parameters. look at the superclass.
fbClass = fbClass.getSuperclass();
}
// find the given factory method, taking into account that in the case of
// Find the given factory method, taking into account that in the case of
// @Bean methods, there may be parameters present.
ReflectionUtils.doWithMethods(fbClass,
new ReflectionUtils.MethodCallback() {
@@ -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.
@@ -38,6 +38,7 @@ import org.springframework.beans.factory.config.RuntimeBeanNameReference;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
@@ -208,7 +209,7 @@ class BeanDefinitionValueResolver {
*/
protected Object evaluate(TypedStringValue value) {
Object result = this.beanFactory.evaluateBeanDefinitionString(value.getValue(), this.beanDefinition);
if (result != value.getValue()) {
if (!ObjectUtils.nullSafeEquals(result, value.getValue())) {
value.setDynamic();
}
return result;
@@ -24,8 +24,6 @@ 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;
@@ -90,6 +88,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 +97,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 +127,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 +299,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 (!isConfigurationFrozen() || type == null || !allowEagerInit) {
return doGetBeanNamesForType(type, includeNonSingletons, allowEagerInit);
}
Map<Class<?>, String[]> cache = includeNonSingletons ?
this.nonSingletonBeanNamesByType : this.singletonBeanNamesByType;
String[] resolvedBeanNames = cache.get(type);
if (resolvedBeanNames != null) {
return resolvedBeanNames;
}
resolvedBeanNames = 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 +461,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 +584,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 +689,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 +747,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 +762,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 +783,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 +791,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 +852,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 +942,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 +991,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 +1001,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,16 +1019,17 @@ 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;
private final String beanName;
public DependencyObjectFactory(DependencyDescriptor descriptor, String beanName) {
this.descriptor = descriptor;
this.beanName = beanName;
this.descriptor = new DependencyDescriptor(descriptor);
this.descriptor.increaseNestingLevel();
this.beanName = beanName;
}
public Object getObject() throws BeansException {
@@ -1015,7 +1041,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-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.
@@ -303,7 +303,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
/**
* Callback after singleton creation.
* <p>Default implementation marks the singleton as not in creation anymore.
* <p>The default implementation marks the singleton as not in creation anymore.
* @param beanName the name of the singleton that has been created
* @see #isSingletonCurrentlyInCreation
*/
@@ -316,7 +316,8 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
public final void setCurrentlyInCreation(String beanName, boolean inCreation) {
if (!inCreation) {
this.inCreationCheckExclusions.add(beanName);
} else {
}
else {
this.inCreationCheckExclusions.remove(beanName);
}
}
@@ -333,7 +334,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
/**
* Add the given bean to the list of disposable beans in this registry.
* Disposable beans usually correspond to registered singletons,
* <p>Disposable beans usually correspond to registered singletons,
* matching the bean name but potentially being a different instance
* (for example, a DisposableBean adapter for a singleton that does not
* naturally implement Spring's DisposableBean interface).
@@ -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.
@@ -44,6 +44,17 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
private static final ThreadLocal<Method> currentlyInvokedFactoryMethod = new ThreadLocal<Method>();
/**
* Return the factory method currently being invoked or {@code null} if none.
* <p>Allows factory method implementations to determine whether the current
* caller is the container itself as opposed to user code.
*/
public static Method getCurrentlyInvokedFactoryMethod() {
return currentlyInvokedFactoryMethod.get();
}
public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides.
if (beanDefinition.getMethodOverrides().isEmpty()) {
@@ -147,7 +158,8 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
try {
currentlyInvokedFactoryMethod.set(factoryMethod);
return factoryMethod.invoke(factoryBean, args);
} finally {
}
finally {
if (priorInvokedFactoryMethod != null) {
currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
}
@@ -171,12 +183,4 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
}
}
/**
* Return the factory method currently being invoked or {@code null} if none.
* Allows factory method implementations to determine whether the current
* caller is the container itself as opposed to user code.
*/
public static Method getCurrentlyInvokedFactoryMethod() {
return currentlyInvokedFactoryMethod.get();
}
}
@@ -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.
@@ -826,7 +826,11 @@ public class BeanDefinitionParserDelegate {
// Look for arg-type match elements.
List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
for (Element argTypeEle : argTypeEles) {
replaceOverride.addTypeIdentifier(argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE));
String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
if (StringUtils.hasText(match)) {
replaceOverride.addTypeIdentifier(match);
}
}
replaceOverride.setSource(extractSource(replacedMethodEle));
overrides.addOverride(replaceOverride);
@@ -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.
@@ -43,14 +43,16 @@ import java.util.TreeMap;
import java.util.TreeSet;
import org.apache.commons.logging.LogFactory;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.propertyeditors.CustomNumberEditor;
import org.springframework.beans.propertyeditors.StringArrayPropertyEditor;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.beans.support.DerivedFromProtectedBaseBean;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
@@ -66,6 +68,7 @@ import test.beans.TestBean;
* @author Alef Arendsen
* @author Arjen Poutsma
* @author Chris Beams
* @author Dave Syer
*/
public final class BeanWrapperTests {
@@ -78,7 +81,7 @@ public final class BeanWrapperTests {
wrapper.setPropertyValue("listOfMaps[0]['luckyNumber']", "9");
assertEquals("9", foo.listOfMaps.get(0).get("luckyNumber"));
}
@Test
public void testNullNestedTypeDescriptor2() {
Foo foo = new Foo();
@@ -90,31 +93,31 @@ public final class BeanWrapperTests {
wrapper.setPropertyValue("list[0]", map);
assertEquals(map, foo.list.get(0));
}
static class Foo {
private List list;
private List<Map> listOfMaps;
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
public List<Map> getListOfMaps() {
return listOfMaps;
}
public void setListOfMaps(List<Map> listOfMaps) {
this.listOfMaps = listOfMaps;
}
@Test
public void testNullNestedTypeDescriptorWithNoConversionService() {
Foo foo = new Foo();
BeanWrapperImpl wrapper = new BeanWrapperImpl(foo);
wrapper.setAutoGrowNestedPaths(true);
wrapper.setPropertyValue("listOfMaps[0]['luckyNumber']", "9");
assertEquals("9", foo.listOfMaps.get(0).get("luckyNumber"));
}
@Test
public void testNullNestedTypeDescriptorWithBadConversionService() {
Foo foo = new Foo();
BeanWrapperImpl wrapper = new BeanWrapperImpl(foo);
wrapper.setConversionService(new GenericConversionService() {
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
throw new ConversionFailedException(sourceType, targetType, source, null);
}
});
wrapper.setAutoGrowNestedPaths(true);
wrapper.setPropertyValue("listOfMaps[0]['luckyNumber']", "9");
assertEquals("9", foo.listOfMaps.get(0).get("luckyNumber"));
}
@Test
public void testIsReadablePropertyNotReadable() {
NoRead nr = new NoRead();
@@ -1448,7 +1451,7 @@ public final class BeanWrapperTests {
@Test
public void testSetNumberProperties() {
NumberPropertyBean bean = new NumberPropertyBean();
NumberPropertyBean bean = new NumberPropertyBean();
BeanWrapper bw = new BeanWrapperImpl(bean);
String byteValue = " " + Byte.MAX_VALUE + " ";
@@ -1538,6 +1541,31 @@ public final class BeanWrapperTests {
assertEquals(TestEnum.TEST_VALUE, consumer.getEnumValue());
}
private static class Foo {
private List list;
private List<Map> listOfMaps;
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
public List<Map> getListOfMaps() {
return listOfMaps;
}
public void setListOfMaps(List<Map> listOfMaps) {
this.listOfMaps = listOfMaps;
}
}
private static class DifferentTestBean extends TestBean {
// class to test naming of beans in a BeanWrapper error message
}
@@ -16,29 +16,30 @@
package org.springframework.beans;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.beans.BeanInfo;
import java.beans.IndexedPropertyDescriptor;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.ExtendedBeanInfo.PropertyDescriptorComparator;
import org.springframework.core.JdkVersion;
import org.springframework.util.ClassUtils;
import test.beans.TestBean;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.lessThan;
/**
* Unit tests for {@link ExtendedBeanInfo}.
*
@@ -149,7 +150,7 @@ public class ExtendedBeanInfoTests {
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(trueUntilJdk17()));
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
@@ -163,6 +164,50 @@ public class ExtendedBeanInfoTests {
fail("never matched write method");
}
@Test
public void cornerSpr9414() throws IntrospectionException {
@SuppressWarnings("unused") class Parent {
public Number getProperty1() {
return 1;
}
}
class Child extends Parent {
@Override
public Integer getProperty1() {
return 2;
}
}
{ // always passes
ExtendedBeanInfo bi = new ExtendedBeanInfo(Introspector.getBeanInfo(Parent.class));
assertThat(hasReadMethodForProperty(bi, "property1"), is(true));
}
{ // failed prior to fix for SPR-9414
ExtendedBeanInfo bi = new ExtendedBeanInfo(Introspector.getBeanInfo(Child.class));
assertThat(hasReadMethodForProperty(bi, "property1"), is(true));
}
}
interface Spr9453<T> {
T getProp();
}
@Test
public void cornerSpr9453() throws IntrospectionException {
final class Bean implements Spr9453<Class<?>> {
public Class<?> getProp() {
return null;
}
}
{ // always passes
BeanInfo info = Introspector.getBeanInfo(Bean.class);
assertThat(info.getPropertyDescriptors().length, equalTo(2));
}
{ // failed prior to fix for SPR-9453
BeanInfo info = new ExtendedBeanInfo(Introspector.getBeanInfo(Bean.class));
assertThat(info.getPropertyDescriptors().length, equalTo(2));
}
}
@Test
public void standardReadMethodInSuperclassAndNonStandardWriteMethodInSubclass() throws Exception {
@SuppressWarnings("unused") class B {
@@ -471,6 +516,53 @@ public class ExtendedBeanInfoTests {
assertThat(hasIndexedWriteMethodForProperty(ebi, "foos"), is(true));
}
@Ignore // see comments at SPR-9702
@Test
public void cornerSpr9702() throws IntrospectionException {
{ // baseline with standard write method
@SuppressWarnings("unused")
class C {
// VOID-RETURNING, NON-INDEXED write method
public void setFoos(String[] foos) { }
// indexed read method
public String getFoos(int i) { return null; }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
assertThat(hasReadMethodForProperty(bi, "foos"), is(false));
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foos"), is(true));
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(false));
BeanInfo ebi = Introspector.getBeanInfo(C.class);
assertThat(hasReadMethodForProperty(ebi, "foos"), is(false));
assertThat(hasIndexedReadMethodForProperty(ebi, "foos"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "foos"), is(true));
assertThat(hasIndexedWriteMethodForProperty(ebi, "foos"), is(false));
}
{ // variant with non-standard write method
@SuppressWarnings("unused")
class C {
// NON-VOID-RETURNING, NON-INDEXED write method
public C setFoos(String[] foos) { return this; }
// indexed read method
public String getFoos(int i) { return null; }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
assertThat(hasReadMethodForProperty(bi, "foos"), is(false));
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foos"), is(false));
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(false));
BeanInfo ebi = new ExtendedBeanInfo(Introspector.getBeanInfo(C.class));
assertThat(hasReadMethodForProperty(ebi, "foos"), is(false));
assertThat(hasIndexedReadMethodForProperty(ebi, "foos"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "foos"), is(true));
assertThat(hasIndexedWriteMethodForProperty(ebi, "foos"), is(false));
}
}
@Test
public void subclassWriteMethodWithCovariantReturnType() throws IntrospectionException {
@SuppressWarnings("unused") class B {
@@ -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,18 +16,6 @@
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;
@@ -43,13 +31,19 @@ import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
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 test.beans.DerivedTestBean;
import test.beans.DummyFactory;
import test.beans.ITestBean;
import test.beans.LifecycleBean;
import test.beans.NestedTestBean;
import test.beans.TestBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.NotWritablePropertyException;
@@ -85,12 +79,8 @@ import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.util.StopWatch;
import test.beans.DerivedTestBean;
import test.beans.DummyFactory;
import test.beans.ITestBean;
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 +2149,43 @@ 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));
bf.freezeConfiguration();
for (int i=0; i<10000; i++) {
bf.getBean(B.class);
}
}
public static class NoDependencies {
private NoDependencies() {
@@ -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.
@@ -24,7 +24,6 @@ import java.lang.annotation.Target;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.*;
import org.junit.Test;
import test.beans.ITestBean;
import test.beans.IndexedTestBean;
@@ -42,6 +41,8 @@ import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.util.SerializationTestUtils;
import static org.junit.Assert.*;
/**
* Unit tests for {@link AutowiredAnnotationBeanPostProcessor}.
*
@@ -118,6 +119,11 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
assertSame(tb, bean.getTestBean4());
assertSame(ntb, bean.getNestedTestBean());
assertSame(bf, bean.getBeanFactory());
String[] depBeans = bf.getDependenciesForBean("annotatedBean");
assertEquals(2, depBeans.length);
assertEquals("testBean", depBeans[0]);
assertEquals("nestedTestBean", depBeans[1]);
}
@Test
@@ -596,6 +602,22 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
bf.destroySingletons();
}
@Test
public void testObjectFactoryInjectionIntoPrototypeBean() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryInjectionBean.class, false));
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
ObjectFactoryInjectionBean bean = (ObjectFactoryInjectionBean) bf.getBean("annotatedBean");
assertSame(bf.getBean("testBean"), bean.getTestBean());
ObjectFactoryInjectionBean anotherBean = (ObjectFactoryInjectionBean) bf.getBean("annotatedBean");
assertNotSame(anotherBean, bean);
assertSame(bf.getBean("testBean"), anotherBean.getTestBean());
}
@Test
public void testObjectFactoryQualifierInjection() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -372,13 +372,13 @@ public class InjectAnnotationBeanPostProcessorTests {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierInjectionBean.class));
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierFieldInjectionBean.class));
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
bd.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "testBean"));
bf.registerBeanDefinition("testBean", bd);
bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class));
ObjectFactoryQualifierInjectionBean bean = (ObjectFactoryQualifierInjectionBean) bf.getBean("annotatedBean");
ObjectFactoryQualifierFieldInjectionBean bean = (ObjectFactoryQualifierFieldInjectionBean) bf.getBean("annotatedBean");
assertSame(bf.getBean("testBean"), bean.getTestBean());
bf.destroySingletons();
}
@@ -389,29 +389,86 @@ public class InjectAnnotationBeanPostProcessorTests {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierInjectionBean.class));
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierFieldInjectionBean.class));
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
bd.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "testBean"));
bf.registerBeanDefinition("testBean", bd);
ObjectFactoryQualifierInjectionBean bean = (ObjectFactoryQualifierInjectionBean) bf.getBean("annotatedBean");
ObjectFactoryQualifierFieldInjectionBean bean = (ObjectFactoryQualifierFieldInjectionBean) bf.getBean("annotatedBean");
assertSame(bf.getBean("testBean"), bean.getTestBean());
bf.destroySingletons();
}
@Test
public void testObjectFactorySerialization() throws Exception {
public void testObjectFactoryFieldInjectionIntoPrototypeBean() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierFieldInjectionBean.class, false));
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
bd.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "testBean"));
bf.registerBeanDefinition("testBean", bd);
bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class));
ObjectFactoryQualifierFieldInjectionBean bean = (ObjectFactoryQualifierFieldInjectionBean) bf.getBean("annotatedBean");
assertSame(bf.getBean("testBean"), bean.getTestBean());
ObjectFactoryQualifierFieldInjectionBean anotherBean = (ObjectFactoryQualifierFieldInjectionBean) bf.getBean("annotatedBean");
assertNotSame(anotherBean, bean);
assertSame(bf.getBean("testBean"), bean.getTestBean());
}
@Test
public void testObjectFactoryMethodInjectionIntoPrototypeBean() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierMethodInjectionBean.class, false));
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
bd.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "testBean"));
bf.registerBeanDefinition("testBean", bd);
bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class));
ObjectFactoryQualifierMethodInjectionBean bean = (ObjectFactoryQualifierMethodInjectionBean) bf.getBean("annotatedBean");
assertSame(bf.getBean("testBean"), bean.getTestBean());
ObjectFactoryQualifierMethodInjectionBean anotherBean = (ObjectFactoryQualifierMethodInjectionBean) bf.getBean("annotatedBean");
assertNotSame(anotherBean, bean);
assertSame(bf.getBean("testBean"), bean.getTestBean());
}
@Test
public void testObjectFactoryWithBeanField() throws Exception {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryInjectionBean.class));
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryFieldInjectionBean.class));
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
bf.setSerializationId("test");
ObjectFactoryInjectionBean bean = (ObjectFactoryInjectionBean) bf.getBean("annotatedBean");
ObjectFactoryFieldInjectionBean bean = (ObjectFactoryFieldInjectionBean) bf.getBean("annotatedBean");
assertSame(bf.getBean("testBean"), bean.getTestBean());
bean = (ObjectFactoryInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean);
bean = (ObjectFactoryFieldInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean);
assertSame(bf.getBean("testBean"), bean.getTestBean());
bf.destroySingletons();
}
@Test
public void testObjectFactoryWithBeanMethod() throws Exception {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryMethodInjectionBean.class));
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
bf.setSerializationId("test");
ObjectFactoryMethodInjectionBean bean = (ObjectFactoryMethodInjectionBean) bf.getBean("annotatedBean");
assertSame(bf.getBean("testBean"), bean.getTestBean());
bean = (ObjectFactoryMethodInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean);
assertSame(bf.getBean("testBean"), bean.getTestBean());
bf.destroySingletons();
}
@@ -881,7 +938,7 @@ public class InjectAnnotationBeanPostProcessorTests {
}
public static class ObjectFactoryInjectionBean implements Serializable {
public static class ObjectFactoryFieldInjectionBean implements Serializable {
@Inject
private Provider<TestBean> testBeanFactory;
@@ -892,7 +949,22 @@ public class InjectAnnotationBeanPostProcessorTests {
}
public static class ObjectFactoryQualifierInjectionBean {
public static class ObjectFactoryMethodInjectionBean implements Serializable {
private Provider<TestBean> testBeanFactory;
@Inject
public void setTestBeanFactory(Provider<TestBean> testBeanFactory) {
this.testBeanFactory = testBeanFactory;
}
public TestBean getTestBean() {
return this.testBeanFactory.get();
}
}
public static class ObjectFactoryQualifierFieldInjectionBean {
@Inject
@Named("testBean")
@@ -904,6 +976,22 @@ public class InjectAnnotationBeanPostProcessorTests {
}
public static class ObjectFactoryQualifierMethodInjectionBean {
private Provider<?> testBeanFactory;
@Inject
@Named("testBean")
public void setTestBeanFactory(Provider<?> testBeanFactory) {
this.testBeanFactory = testBeanFactory;
}
public TestBean getTestBean() {
return (TestBean) this.testBeanFactory.get();
}
}
public static class ObjectFactoryListFieldInjectionBean implements Serializable {
@Inject
+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.3.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.3.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.3.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.3.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;
@@ -52,6 +52,7 @@ public class AnnotatedBeanDefinitionReader {
private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
/**
* Create a new {@code AnnotatedBeanDefinitionReader} for the given registry.
* If the registry is {@link EnvironmentCapable}, e.g. is an {@code ApplicationContext},
@@ -78,10 +79,8 @@ public class AnnotatedBeanDefinitionReader {
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.environment = environment;
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
@@ -115,8 +114,8 @@ public class AnnotatedBeanDefinitionReader {
* <p>The default is an {@link AnnotationScopeMetadataResolver}.
*/
public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
this.scopeMetadataResolver = (scopeMetadataResolver != null ? scopeMetadataResolver
: new AnnotationScopeMetadataResolver());
this.scopeMetadataResolver =
(scopeMetadataResolver != null ? scopeMetadataResolver : new AnnotationScopeMetadataResolver());
}
public void register(Class<?>... annotatedClasses) {
@@ -136,7 +135,6 @@ public class AnnotatedBeanDefinitionReader {
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
AnnotationMetadata metadata = abd.getMetadata();
if (metadata.isAnnotated(Profile.class.getName())) {
AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
if (!this.environment.acceptsProfiles(profile.getStringArray("value"))) {
@@ -151,9 +149,11 @@ public class AnnotatedBeanDefinitionReader {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class.equals(qualifier)) {
abd.setPrimary(true);
} else if (Lazy.class.equals(qualifier)) {
}
else if (Lazy.class.equals(qualifier)) {
abd.setLazyInit(true);
} else {
}
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
@@ -73,7 +73,7 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator {
}
}
// Fallback: generate a unique default bean name.
return buildDefaultBeanName(definition);
return buildDefaultBeanName(definition, registry);
}
/**
@@ -119,6 +119,17 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator {
return (isStereotype && attributes != null && attributes.containsKey("value"));
}
/**
* Derive a default bean name from the given bean definition.
* <p>The default implementation delegates to {@link #buildDefaultBeanName(BeanDefinition)}.
* @param definition the bean definition to build a bean name for
* @param registry the registry that the given bean definition is being registered with
* @return the default bean name (never <code>null</code>)
*/
protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
return buildDefaultBeanName(definition);
}
/**
* Derive a default bean name from the given bean definition.
* <p>The default implementation simply builds a decapitalized version
@@ -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.
@@ -446,7 +446,9 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
if (factory instanceof ConfigurableBeanFactory) {
ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
for (String autowiredBeanName : autowiredBeanNames) {
beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
if (beanFactory.containsBean(autowiredBeanName)) {
beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
}
}
}
@@ -509,9 +511,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);
}
@@ -724,4 +729,4 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
}
}
}
}
@@ -49,7 +49,6 @@ import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import static org.springframework.context.annotation.MetadataUtils.*;
@@ -83,29 +82,25 @@ class ConfigurationClassBeanDefinitionReader {
private final Environment environment;
private final BeanNameGenerator beanNameGenerator;
private final BeanNameGenerator importBeanNameGenerator;
/**
* Create a new {@link ConfigurationClassBeanDefinitionReader} instance that will be used
* to populate the given {@link BeanDefinitionRegistry}.
* @param problemReporter
* @param metadataReaderFactory
*/
public ConfigurationClassBeanDefinitionReader(
BeanDefinitionRegistry registry, SourceExtractor sourceExtractor,
ProblemReporter problemReporter, MetadataReaderFactory metadataReaderFactory,
ResourceLoader resourceLoader, Environment environment,
BeanNameGenerator beanNameGenerator) {
ResourceLoader resourceLoader, Environment environment, BeanNameGenerator importBeanNameGenerator) {
Assert.notNull(beanNameGenerator, "BeanNameGenerator must not be null");
this.registry = registry;
this.sourceExtractor = sourceExtractor;
this.problemReporter = problemReporter;
this.metadataReaderFactory = metadataReaderFactory;
this.resourceLoader = resourceLoader;
this.environment = environment;
this.beanNameGenerator = beanNameGenerator;
this.importBeanNameGenerator = importBeanNameGenerator;
}
@@ -124,7 +119,9 @@ class ConfigurationClassBeanDefinitionReader {
* class itself, all its {@link Bean} methods
*/
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
doLoadBeanDefinitionForConfigurationClassIfNecessary(configClass);
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
@@ -134,17 +131,13 @@ class ConfigurationClassBeanDefinitionReader {
/**
* Register the {@link Configuration} class itself as a bean definition.
*/
private void doLoadBeanDefinitionForConfigurationClassIfNecessary(ConfigurationClass configClass) {
if (!configClass.isImported()) {
return;
}
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
AnnotationMetadata metadata = configClass.getMetadata();
BeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
String className = metadata.getClassName();
configBeanDef.setBeanClassName(className);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(configBeanDef, this.metadataReaderFactory)) {
String configBeanName = this.beanNameGenerator.generateBeanName(configBeanDef, this.registry);
String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
this.registry.registerBeanDefinition(configBeanName, configBeanDef);
configClass.setBeanName(configBeanName);
if (logger.isDebugEnabled()) {
@@ -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,8 +17,6 @@
package org.springframework.context.annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
@@ -29,6 +27,7 @@ import net.sf.cglib.proxy.NoOp;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.scope.ScopedProxyFactoryBean;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.DisposableBean;
@@ -53,11 +52,28 @@ class ConfigurationClassEnhancer {
private static final Log logger = LogFactory.getLog(ConfigurationClassEnhancer.class);
private final List<Callback> callbackInstances = new ArrayList<Callback>();
private static final Class<?>[] CALLBACK_TYPES = { BeanMethodInterceptor.class,
DisposableBeanMethodInterceptor.class, NoOp.class };
private final List<Class<? extends Callback>> callbackTypes = new ArrayList<Class<? extends Callback>>();
private static final CallbackFilter CALLBACK_FILTER = new CallbackFilter() {
private final CallbackFilter callbackFilter;
public int accept(Method candidateMethod) {
// Set up the callback filter to return the index of the BeanMethodInterceptor when
// handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
if (BeanAnnotationHelper.isBeanAnnotated(candidateMethod)) {
return 0;
}
if (DisposableBeanMethodInterceptor.isDestroyMethod(candidateMethod)) {
return 1;
}
return 2;
}
};
private static final Callback DISPOSABLE_BEAN_METHOD_INTERCEPTOR = new DisposableBeanMethodInterceptor();
private final Callback[] callbackInstances;
/**
@@ -65,28 +81,11 @@ class ConfigurationClassEnhancer {
*/
public ConfigurationClassEnhancer(ConfigurableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "BeanFactory must not be null");
this.callbackInstances.add(new BeanMethodInterceptor(beanFactory));
this.callbackInstances.add(new DisposableBeanMethodInterceptor());
this.callbackInstances.add(NoOp.INSTANCE);
for (Callback callback : this.callbackInstances) {
this.callbackTypes.add(callback.getClass());
}
// Set up the callback filter to return the index of the BeanMethodInterceptor when
// handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
callbackFilter = new CallbackFilter() {
public int accept(Method candidateMethod) {
if (BeanAnnotationHelper.isBeanAnnotated(candidateMethod)) {
return 0;
}
if (DisposableBeanMethodInterceptor.isDestroyMethod(candidateMethod)) {
return 1;
}
return 2;
}
};
// Callback instances must be ordered in the same way as CALLBACK_TYPES and CALLBACK_FILTER
this.callbackInstances = new Callback[] {
new BeanMethodInterceptor(beanFactory),
DISPOSABLE_BEAN_METHOD_INTERCEPTOR,
NoOp.INSTANCE };
}
/**
@@ -134,15 +133,11 @@ class ConfigurationClassEnhancer {
*/
private Enhancer newEnhancer(Class<?> superclass) {
Enhancer enhancer = new Enhancer();
// Because callbackFilter and callbackTypes are dynamically populated
// there's no opportunity for caching. This does not appear to be causing
// any performance problem.
enhancer.setUseCache(false);
enhancer.setSuperclass(superclass);
enhancer.setInterfaces(new Class[] {EnhancedConfiguration.class});
enhancer.setUseFactory(false);
enhancer.setCallbackFilter(this.callbackFilter);
enhancer.setCallbackTypes(this.callbackTypes.toArray(new Class[this.callbackTypes.size()]));
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_TYPES);
return enhancer;
}
@@ -153,7 +148,7 @@ class ConfigurationClassEnhancer {
private Class<?> createClass(Enhancer enhancer) {
Class<?> subclass = enhancer.createClass();
// registering callbacks statically (as opposed to threadlocal) is critical for usage in an OSGi env (SPR-5932)
Enhancer.registerStaticCallbacks(subclass, this.callbackInstances.toArray(new Callback[this.callbackInstances.size()]));
Enhancer.registerStaticCallbacks(subclass, this.callbackInstances);
return subclass;
}
@@ -165,7 +160,7 @@ class ConfigurationClassEnhancer {
* @see BeanMethodInterceptor#enhanceFactoryBean(Class, String)
*/
private static class GetObjectMethodInterceptor implements MethodInterceptor {
private final ConfigurableBeanFactory beanFactory;
private final String beanName;
@@ -177,7 +172,7 @@ class ConfigurationClassEnhancer {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return beanFactory.getBean(beanName);
}
}
@@ -215,8 +210,19 @@ class ConfigurationClassEnhancer {
*/
private static class BeanMethodInterceptor implements MethodInterceptor {
private static final Class<?>[] CALLBACK_TYPES = {
GetObjectMethodInterceptor.class, NoOp.class };
private static final CallbackFilter CALLBACK_FITLER = new CallbackFilter() {
public int accept(Method method) {
return method.getName().equals("getObject") ? 0 : 1;
}
};
private final ConfigurableBeanFactory beanFactory;
public BeanMethodInterceptor(ConfigurableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@@ -245,7 +251,7 @@ class ConfigurationClassEnhancer {
// to handle the case of an inter-bean method reference, we must explicitly check the
// container for already cached instances
// first, check to see if the requested bean is a FactoryBean. If so, create a subclass
// proxy that intercepts calls to getObject() and returns any cached bean instance.
// this ensures that the semantics of calling a FactoryBean from within @Bean methods
@@ -327,26 +333,18 @@ class ConfigurationClassEnhancer {
*/
private Object enhanceFactoryBean(Class<?> fbClass, String beanName) throws InstantiationException, IllegalAccessException {
Enhancer enhancer = new Enhancer();
enhancer.setUseCache(false);
enhancer.setSuperclass(fbClass);
enhancer.setUseFactory(false);
enhancer.setCallbackFilter(new CallbackFilter() {
public int accept(Method method) {
return method.getName().equals("getObject") ? 0 : 1;
}
});
List<Callback> callbackInstances = new ArrayList<Callback>();
callbackInstances.add(new GetObjectMethodInterceptor(this.beanFactory, beanName));
callbackInstances.add(NoOp.INSTANCE);
List<Class<? extends Callback>> callbackTypes = new ArrayList<Class<? extends Callback>>();
for (Callback callback : callbackInstances) {
callbackTypes.add(callback.getClass());
}
enhancer.setCallbackTypes(callbackTypes.toArray(new Class[callbackTypes.size()]));
enhancer.setCallbackFilter(CALLBACK_FITLER);
// Callback instances must be ordered in the same way as CALLBACK_TYPES and CALLBACK_FILTER
Callback[] callbackInstances = new Callback[] {
new GetObjectMethodInterceptor(this.beanFactory, beanName),
NoOp.INSTANCE
};
enhancer.setCallbackTypes(CALLBACK_TYPES);
Class<?> fbSubclass = enhancer.createClass();
Enhancer.registerCallbacks(fbSubclass, callbackInstances.toArray(new Callback[callbackInstances.size()]));
Enhancer.registerCallbacks(fbSubclass, callbackInstances);
return fbSubclass.newInstance();
}
@@ -101,9 +101,8 @@ class ConfigurationClassParser {
* to populate the set of configuration classes.
*/
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
ProblemReporter problemReporter, Environment environment,
ResourceLoader resourceLoader, BeanNameGenerator beanNameGenerator,
BeanDefinitionRegistry registry) {
ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {
this.metadataReaderFactory = metadataReaderFactory;
this.problemReporter = problemReporter;
@@ -111,7 +110,7 @@ class ConfigurationClassParser {
this.resourceLoader = resourceLoader;
this.registry = registry;
this.componentScanParser = new ComponentScanAnnotationParser(
resourceLoader, environment, beanNameGenerator, registry);
resourceLoader, environment, componentScanBeanNameGenerator, registry);
}
@@ -185,7 +184,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)) {
@@ -315,12 +314,13 @@ class ConfigurationClassParser {
AnnotationMetadata importingClassMetadata = configClass.getMetadata();
for (String candidate : classesToImport) {
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(candidate);
if (new AssignableTypeFilter(ImportSelector.class).match(reader, metadataReaderFactory)) {
if (new AssignableTypeFilter(ImportSelector.class).match(reader, this.metadataReaderFactory)) {
// the candidate class is an ImportSelector -> delegate to it to determine imports
try {
ImportSelector selector = BeanUtils.instantiateClass(Class.forName(candidate), ImportSelector.class);
processImport(configClass, selector.selectImports(importingClassMetadata), false);
} catch (ClassNotFoundException ex) {
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(ex);
}
}
@@ -329,7 +329,8 @@ class ConfigurationClassParser {
try {
ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(Class.forName(candidate), ImportBeanDefinitionRegistrar.class);
registrar.registerBeanDefinitions(importingClassMetadata, registry);
} catch (ClassNotFoundException ex) {
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(ex);
}
}
@@ -367,6 +368,7 @@ class ConfigurationClassParser {
interface ImportRegistry {
String getImportingClassFor(String importedClass);
}
@@ -374,14 +376,14 @@ class ConfigurationClassParser {
@SuppressWarnings("serial")
private static class ImportStack extends Stack<ConfigurationClass> implements ImportRegistry {
private Map<String, String> imports = new HashMap<String, String>();
public String getImportingClassFor(String importedClass) {
return imports.get(importedClass);
}
private final Map<String, String> imports = new HashMap<String, String>();
public void registerImport(String importingClass, String importedClass) {
imports.put(importedClass, importingClass);
this.imports.put(importedClass, importingClass);
}
public String getImportingClassFor(String importedClass) {
return this.imports.get(importedClass);
}
/**
@@ -43,7 +43,6 @@ import org.springframework.beans.factory.parsing.PassThroughSourceExtractor;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.beans.factory.parsing.SourceExtractor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
@@ -92,6 +91,12 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
private static final boolean cglibAvailable = ClassUtils.isPresent(
"net.sf.cglib.proxy.Enhancer", ConfigurationClassPostProcessor.class.getClassLoader());
private static final String IMPORT_AWARE_PROCESSOR_BEAN_NAME =
ConfigurationClassPostProcessor.class.getName() + ".importAwareProcessor";
private static final String IMPORT_REGISTRY_BEAN_NAME =
ConfigurationClassPostProcessor.class.getName() + ".importRegistry";
private final Log logger = LogFactory.getLog(getClass());
@@ -115,7 +120,18 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
private ConfigurationClassBeanDefinitionReader reader;
private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
private boolean localBeanNameGeneratorSet = false;
/* using short class names as default bean names */
private BeanNameGenerator componentScanBeanNameGenerator = new AnnotationBeanNameGenerator();
/* using fully qualified class names as default bean names */
private BeanNameGenerator importBeanNameGenerator = new AnnotationBeanNameGenerator() {
@Override
protected String buildDefaultBeanName(BeanDefinition definition) {
return definition.getBeanClassName();
}
};
/**
@@ -148,23 +164,27 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
}
/**
* Set the {@link BeanNameGenerator} to be used when registering imported and nested
* {@link Configuration} classes. The default is {@link AnnotationBeanNameGenerator}.
* Set the {@link BeanNameGenerator} to be used when triggering component scanning
* from {@link Configuration} classes and when registering {@link Import}'ed
* configuration classes. The default is a standard {@link AnnotationBeanNameGenerator}
* for scanned components (compatible with the default in {@link ClassPathBeanDefinitionScanner})
* and a variant thereof for imported configuration classes (using unique fully-qualified
* class names instead of standard component overriding).
* <p>Note that this strategy does <em>not</em> apply to {@link Bean} methods.
* <p>This setter is typically only appropriate when configuring the post-processor as
* a standalone bean definition in XML, e.g. not using the dedicated
* {@code AnnotationConfig*} application contexts or the {@code
* <context:annotation-config>} element. Any bean name generator specified against
* the application context will take precedence over any value set here.
* @param beanNameGenerator the strategy to use when generating configuration class
* bean names
* @since 3.1.1
* @see AnnotationConfigApplicationContext#setBeanNameGenerator(BeanNameGenerator)
* @see AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR
*/
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
Assert.notNull(beanNameGenerator, "BeanNameGenerator must not be null");
this.beanNameGenerator = beanNameGenerator;
this.localBeanNameGeneratorSet = true;
this.componentScanBeanNameGenerator = beanNameGenerator;
this.importBeanNameGenerator = beanNameGenerator;
}
public void setEnvironment(Environment environment) {
@@ -189,7 +209,10 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
* Derive further bean definitions from the configuration classes in the registry.
*/
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
BeanDefinitionReaderUtils.registerWithGeneratedName(new RootBeanDefinition(ImportAwareBeanPostProcessor.class), registry);
RootBeanDefinition iabpp = new RootBeanDefinition(ImportAwareBeanPostProcessor.class);
iabpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(IMPORT_AWARE_PROCESSOR_BEAN_NAME, iabpp);
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
@@ -200,6 +223,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
"postProcessBeanFactory already called for this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
processConfigBeanDefinitions(registry);
}
@@ -244,15 +268,17 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
SingletonBeanRegistry singletonRegistry = null;
if (registry instanceof SingletonBeanRegistry) {
singletonRegistry = (SingletonBeanRegistry) registry;
if (singletonRegistry.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
this.beanNameGenerator = (BeanNameGenerator) singletonRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
if (!this.localBeanNameGeneratorSet && singletonRegistry.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
BeanNameGenerator generator = (BeanNameGenerator) singletonRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.beanNameGenerator, registry);
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
@@ -287,16 +313,15 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.problemReporter,
this.metadataReaderFactory, this.resourceLoader, this.environment,
this.beanNameGenerator);
registry, this.sourceExtractor, this.problemReporter, this.metadataReaderFactory,
this.resourceLoader, this.environment, this.importBeanNameGenerator);
}
this.reader.loadBeanDefinitions(parser.getConfigurationClasses());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (singletonRegistry != null) {
if (!singletonRegistry.containsSingleton("importRegistry")) {
singletonRegistry.registerSingleton("importRegistry", parser.getImportRegistry());
if (!singletonRegistry.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
singletonRegistry.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
}
}
@@ -359,7 +384,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ImportAware) {
ImportRegistry importRegistry = beanFactory.getBean(ImportRegistry.class);
ImportRegistry importRegistry = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);
String importingClass = importRegistry.getImportingClassFor(bean.getClass().getSuperclass().getName());
if (importingClass != null) {
try {
@@ -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.
@@ -32,6 +32,7 @@ import java.util.concurrent.ConcurrentHashMap;
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.DisposableBean;
@@ -67,12 +68,14 @@ import org.springframework.context.event.ContextStartedEvent;
import org.springframework.context.event.ContextStoppedEvent;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.expression.StandardBeanExpressionResolver;
import org.springframework.context.weaving.LoadTimeWeaverAware;
import org.springframework.context.weaving.LoadTimeWeaverAwareProcessor;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
@@ -224,7 +227,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
public AbstractApplicationContext(ApplicationContext parent) {
this.parent = parent;
this.resourcePatternResolver = getResourcePatternResolver();
this.environment = this.createEnvironment();
this.environment = createEnvironment();
}
@@ -378,14 +381,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) {
Environment parentEnvironment = parent.getEnvironment();
if (parentEnvironment instanceof ConfigurableEnvironment) {
getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
}
}
}
@@ -500,13 +508,13 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
// Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
this.environment.validateRequiredProperties();
getEnvironment().validateRequiredProperties();
}
/**
* <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.
@@ -536,7 +544,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver());
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, this.getEnvironment()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
@@ -561,15 +569,13 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
}
// Register default environment beans.
if (!beanFactory.containsBean(ENVIRONMENT_BEAN_NAME)) {
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
@@ -903,6 +909,12 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(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.
@@ -52,7 +52,8 @@ public abstract class MessageSourceSupport {
* Used for passed-in default messages. MessageFormats for resolved
* codes are cached on a specific basis in subclasses.
*/
private final Map<String, MessageFormat> cachedMessageFormats = new HashMap<String, MessageFormat>();
private final Map<String, Map<Locale, MessageFormat>> messageFormatsPerMessage =
new HashMap<String, Map<Locale, MessageFormat>>();
/**
@@ -114,9 +115,16 @@ public abstract class MessageSourceSupport {
if (msg == null || (!this.alwaysUseMessageFormat && ObjectUtils.isEmpty(args))) {
return msg;
}
MessageFormat messageFormat;
synchronized (this.cachedMessageFormats) {
messageFormat = this.cachedMessageFormats.get(msg);
MessageFormat messageFormat = null;
synchronized (this.messageFormatsPerMessage) {
Map<Locale, MessageFormat> messageFormatsPerLocale = this.messageFormatsPerMessage.get(msg);
if (messageFormatsPerLocale != null) {
messageFormat = messageFormatsPerLocale.get(locale);
}
else {
messageFormatsPerLocale = new HashMap<Locale, MessageFormat>();
this.messageFormatsPerMessage.put(msg, messageFormatsPerLocale);
}
if (messageFormat == null) {
try {
messageFormat = createMessageFormat(msg, locale);
@@ -130,7 +138,7 @@ public abstract class MessageSourceSupport {
// silently proceed with raw message if format not enforced
messageFormat = INVALID_MESSAGE_FORMAT;
}
this.cachedMessageFormats.put(msg, messageFormat);
messageFormatsPerLocale.put(locale, messageFormat);
}
}
if (messageFormat == INVALID_MESSAGE_FORMAT) {
@@ -153,9 +161,8 @@ public abstract class MessageSourceSupport {
/**
* Template method for resolving argument objects.
* <p>The default implementation simply returns the given argument
* array as-is. Can be overridden in subclasses in order to resolve
* special argument types.
* <p>The default implementation simply returns the given argument array as-is.
* Can be overridden in subclasses in order to resolve special argument types.
* @param args the original argument array
* @param locale the Locale to resolve against
* @return the resolved argument array
@@ -37,12 +37,16 @@ import org.springframework.util.PropertiesPersister;
import org.springframework.util.StringUtils;
/**
* {@link org.springframework.context.MessageSource} implementation that
* accesses resource bundles using specified basenames. This class uses
* {@link java.util.Properties} instances as its custom data structure for
* messages, loading them via a {@link org.springframework.util.PropertiesPersister}
* strategy: The default strategy is capable of loading properties files
* with a specific character encoding, if desired.
* Spring-specific {@link org.springframework.context.MessageSource} implementation
* that accesses resource bundles using specified basenames, participating in the
* Spring {@link org.springframework.context.ApplicationContext}'s resource loading.
*
* <p>In contrast to the JDK-based {@link ResourceBundleMessageSource}, this class uses
* {@link java.util.Properties} instances as its custom data structure for messages,
* loading them via a {@link org.springframework.util.PropertiesPersister} strategy
* from Spring {@link Resource} handles. This strategy is not only capable of
* reloading files based on timestamp changes, but also of loading properties files
* with a specific character encoding. It will detect XML property files as well.
*
* <p>In contrast to {@link ResourceBundleMessageSource}, this class supports
* reloading of properties files through the {@link #setCacheSeconds "cacheSeconds"}
@@ -171,7 +175,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
* Set the default charset to use for parsing properties files.
* Used if no file-specific charset is specified for a file.
* <p>Default is none, using the <code>java.util.Properties</code>
* default encoding.
* default encoding: ISO-8859-1.
* <p>Only applies to classic properties files, not to XML files.
* @param defaultEncoding the default charset
* @see #setFileEncodings
@@ -201,10 +205,9 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
* fallback will be the default file (e.g. "messages.properties" for
* basename "messages").
* <p>Falling back to the system Locale is the default behavior of
* <code>java.util.ResourceBundle</code>. However, this is often not
* desirable in an application server environment, where the system Locale
* is not relevant to the application at all: Set this flag to "false"
* in such a scenario.
* <code>java.util.ResourceBundle</code>. However, this is often not desirable
* in an application server environment, where the system Locale is not relevant
* to the application at all: Set this flag to "false" in such a scenario.
*/
public void setFallbackToSystemLocale(boolean fallbackToSystemLocale) {
this.fallbackToSystemLocale = fallbackToSystemLocale;
@@ -448,7 +451,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
* @param propHolder the current PropertiesHolder for the bundle
*/
protected PropertiesHolder refreshProperties(String filename, PropertiesHolder propHolder) {
long refreshTimestamp = (this.cacheMillis < 0) ? -1 : System.currentTimeMillis();
long refreshTimestamp = (this.cacheMillis < 0 ? -1 : System.currentTimeMillis());
Resource resource = this.resourceLoader.getResource(filename + PROPERTIES_SUFFIX);
if (!resource.exists()) {
@@ -16,14 +16,24 @@
package org.springframework.context.support;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.core.JdkVersion;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@@ -58,6 +68,12 @@ public class ResourceBundleMessageSource extends AbstractMessageSource implement
private String[] basenames = new String[0];
private String defaultEncoding;
private boolean fallbackToSystemLocale = true;
private long cacheMillis = -1;
private ClassLoader bundleClassLoader;
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
@@ -133,6 +149,59 @@ public class ResourceBundleMessageSource extends AbstractMessageSource implement
}
}
/**
* Set the default charset to use for parsing resource bundle files.
* <p>Default is none, using the <code>java.util.ResourceBundle</code>
* default encoding: ISO-8859-1.
* <p><b>NOTE: Only works on JDK 1.6 and higher.</b> Consider using
* {@link ReloadableResourceBundleMessageSource} for JDK 1.5 support
* and more flexibility in setting of an encoding per file.
*/
public void setDefaultEncoding(String defaultEncoding) {
this.defaultEncoding = defaultEncoding;
}
/**
* Set whether to fall back to the system Locale if no files for a specific
* Locale have been found. Default is "true"; if this is turned off, the only
* fallback will be the default file (e.g. "messages.properties" for
* basename "messages").
* <p>Falling back to the system Locale is the default behavior of
* <code>java.util.ResourceBundle</code>. However, this is often not desirable
* in an application server environment, where the system Locale is not relevant
* to the application at all: Set this flag to "false" in such a scenario.
* <p><b>NOTE: Only works on JDK 1.6 and higher.</b> Consider using
* {@link ReloadableResourceBundleMessageSource} for JDK 1.5 support.
*/
public void setFallbackToSystemLocale(boolean fallbackToSystemLocale) {
this.fallbackToSystemLocale = fallbackToSystemLocale;
}
/**
* Set the number of seconds to cache loaded resource bundle files.
* <ul>
* <li>Default is "-1", indicating to cache forever.
* <li>A positive number will expire resource bundles after the given
* number of seconds. This is essentially the interval between refresh checks.
* Note that a refresh attempt will first check the last-modified timestamp
* of the file before actually reloading it; so if files don't change, this
* interval can be set rather low, as refresh attempts will not actually reload.
* <li>A value of "0" will check the last-modified timestamp of the file on
* every message access. <b>Do not use this in a production environment!</b>
* <li><b>Note that depending on your ClassLoader, expiration might not work reliably
* since the ClassLoader may hold on to a cached version of the bundle file.</b>
* Consider {@link ReloadableResourceBundleMessageSource} in combination
* with resource bundle files in a non-classpath location.
* </ul>
* <p><b>NOTE: Only works on JDK 1.6 and higher.</b> Consider using
* {@link ReloadableResourceBundleMessageSource} for JDK 1.5 support
* and more flexibility in terms of the kinds of resources to load from
* (in particular from outside of the classpath where expiration works reliably).
*/
public void setCacheSeconds(int cacheSeconds) {
this.cacheMillis = (cacheSeconds * 1000);
}
/**
* Set the ClassLoader to load resource bundles with.
* <p>Default is the containing BeanFactory's
@@ -201,30 +270,38 @@ public class ResourceBundleMessageSource extends AbstractMessageSource implement
* found for the given basename and Locale
*/
protected ResourceBundle getResourceBundle(String basename, Locale locale) {
synchronized (this.cachedResourceBundles) {
Map<Locale, ResourceBundle> localeMap = this.cachedResourceBundles.get(basename);
if (localeMap != null) {
ResourceBundle bundle = localeMap.get(locale);
if (bundle != null) {
if (this.cacheMillis >= 0) {
// Fresh ResourceBundle.getBundle call in order to let ResourceBundle
// do its native caching, at the expense of more extensive lookup steps.
return doGetBundle(basename, locale);
}
else {
// Cache forever: prefer locale cache over repeated getBundle calls.
synchronized (this.cachedResourceBundles) {
Map<Locale, ResourceBundle> localeMap = this.cachedResourceBundles.get(basename);
if (localeMap != null) {
ResourceBundle bundle = localeMap.get(locale);
if (bundle != null) {
return bundle;
}
}
try {
ResourceBundle bundle = doGetBundle(basename, locale);
if (localeMap == null) {
localeMap = new HashMap<Locale, ResourceBundle>();
this.cachedResourceBundles.put(basename, localeMap);
}
localeMap.put(locale, bundle);
return bundle;
}
}
try {
ResourceBundle bundle = doGetBundle(basename, locale);
if (localeMap == null) {
localeMap = new HashMap<Locale, ResourceBundle>();
this.cachedResourceBundles.put(basename, localeMap);
catch (MissingResourceException ex) {
if (logger.isWarnEnabled()) {
logger.warn("ResourceBundle [" + basename + "] not found for MessageSource: " + ex.getMessage());
}
// Assume bundle not found
// -> do NOT throw the exception to allow for checking parent message source.
return null;
}
localeMap.put(locale, bundle);
return bundle;
}
catch (MissingResourceException ex) {
if (logger.isWarnEnabled()) {
logger.warn("ResourceBundle [" + basename + "] not found for MessageSource: " + ex.getMessage());
}
// Assume bundle not found
// -> do NOT throw the exception to allow for checking parent message source.
return null;
}
}
}
@@ -239,7 +316,20 @@ public class ResourceBundleMessageSource extends AbstractMessageSource implement
* @see #getBundleClassLoader()
*/
protected ResourceBundle doGetBundle(String basename, Locale locale) throws MissingResourceException {
return ResourceBundle.getBundle(basename, locale, getBundleClassLoader());
if ((this.defaultEncoding != null && !"ISO-8859-1".equals(this.defaultEncoding)) ||
!this.fallbackToSystemLocale || this.cacheMillis >= 0) {
// Custom Control required...
if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_16) {
throw new IllegalStateException("Cannot use 'defaultEncoding', 'fallbackToSystemLocale' and " +
"'cacheSeconds' on the standard ResourceBundleMessageSource when running on Java 5. " +
"Consider using ReloadableResourceBundleMessageSource instead.");
}
return new ControlBasedResourceBundleFactory().getBundle(basename, locale);
}
else {
// Good old standard call...
return ResourceBundle.getBundle(basename, locale, getBundleClassLoader());
}
}
/**
@@ -298,7 +388,6 @@ public class ResourceBundleMessageSource extends AbstractMessageSource implement
}
}
/**
* Show the configuration of this MessageSource.
*/
@@ -308,4 +397,101 @@ public class ResourceBundleMessageSource extends AbstractMessageSource implement
StringUtils.arrayToCommaDelimitedString(this.basenames) + "]";
}
/**
* Factory indirection for runtime isolation of the optional dependencv on
* Java 6's Control class.
* @see ResourceBundle#getBundle(String, java.util.Locale, ClassLoader, java.util.ResourceBundle.Control)
* @see MessageSourceControl
*/
private class ControlBasedResourceBundleFactory {
public ResourceBundle getBundle(String basename, Locale locale) {
return ResourceBundle.getBundle(basename, locale, getBundleClassLoader(), new MessageSourceControl());
}
}
/**
* Custom implementation of Java 6's <code>ResourceBundle.Control</code>,
* adding support for custom file encodings, deactivating the fallback to the
* system locale and activating ResourceBundle's native cache, if desired.
*/
private class MessageSourceControl extends ResourceBundle.Control {
@Override
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException, IOException {
if (format.equals("java.properties")) {
String bundleName = toBundleName(baseName, locale);
final String resourceName = toResourceName(bundleName, "properties");
final ClassLoader classLoader = loader;
final boolean reloadFlag = reload;
InputStream stream;
try {
stream = AccessController.doPrivileged(
new PrivilegedExceptionAction<InputStream>() {
public InputStream run() throws IOException {
InputStream is = null;
if (reloadFlag) {
URL url = classLoader.getResource(resourceName);
if (url != null) {
URLConnection connection = url.openConnection();
if (connection != null) {
connection.setUseCaches(false);
is = connection.getInputStream();
}
}
}
else {
is = classLoader.getResourceAsStream(resourceName);
}
return is;
}
});
}
catch (PrivilegedActionException ex) {
throw (IOException) ex.getException();
}
if (stream != null) {
try {
return (defaultEncoding != null ?
new PropertyResourceBundle(new InputStreamReader(stream, defaultEncoding)) :
new PropertyResourceBundle(stream));
}
finally {
stream.close();
}
}
else {
return null;
}
}
else {
return super.newBundle(baseName, locale, format, loader, reload);
}
}
@Override
public Locale getFallbackLocale(String baseName, Locale locale) {
return (fallbackToSystemLocale ? super.getFallbackLocale(baseName, locale) : null);
}
@Override
public long getTimeToLive(String baseName, Locale locale) {
return (cacheMillis >= 0 ? cacheMillis : super.getTimeToLive(baseName, locale));
}
@Override
public boolean needsReload(String baseName, Locale locale, String format, ClassLoader loader, ResourceBundle bundle, long loadTime) {
if (super.needsReload(baseName, locale, format, loader, bundle, loadTime)) {
cachedBundleMessageFormats.remove(bundle);
return true;
}
else {
return false;
}
}
}
}
@@ -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.
@@ -164,6 +164,7 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean
*/
protected static final String FIELD_METRIC_CATEGORY = "metricCategory";
/**
* Default value for the JMX field "currencyTimeLimit".
*/
@@ -223,7 +224,7 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean
* Return whether strict casing for attributes is enabled.
*/
protected boolean isUseStrictCasing() {
return useStrictCasing;
return this.useStrictCasing;
}
/**
@@ -250,7 +251,7 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean
* Return whether to expose the JMX descriptor field "class" for managed operations.
*/
protected boolean isExposeClassDescriptor() {
return exposeClassDescriptor;
return this.exposeClassDescriptor;
}
@@ -447,7 +448,6 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean
*/
protected abstract boolean includeOperation(Method method, String beanKey);
/**
* Get the description for a particular attribute.
* <p>The default implementation returns a description for the operation
@@ -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,22 +28,37 @@ 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})
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@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);
* }
@@ -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.
@@ -49,9 +49,9 @@ class ReschedulingRunnable extends DelegatingErrorHandlingRunnable implements Sc
private final ScheduledExecutorService executor;
private volatile ScheduledFuture currentFuture;
private ScheduledFuture currentFuture;
private volatile Date scheduledExecutionTime;
private Date scheduledExecutionTime;
private final Object triggerContextMonitor = new Object();
@@ -82,35 +82,53 @@ class ReschedulingRunnable extends DelegatingErrorHandlingRunnable implements Sc
Date completionTime = new Date();
synchronized (this.triggerContextMonitor) {
this.triggerContext.update(this.scheduledExecutionTime, actualExecutionTime, completionTime);
}
if (!this.currentFuture.isCancelled()) {
schedule();
if (!this.currentFuture.isCancelled()) {
schedule();
}
}
}
public boolean cancel(boolean mayInterruptIfRunning) {
return this.currentFuture.cancel(mayInterruptIfRunning);
synchronized (this.triggerContextMonitor) {
return this.currentFuture.cancel(mayInterruptIfRunning);
}
}
public boolean isCancelled() {
return this.currentFuture.isCancelled();
synchronized (this.triggerContextMonitor) {
return this.currentFuture.isCancelled();
}
}
public boolean isDone() {
return this.currentFuture.isDone();
synchronized (this.triggerContextMonitor) {
return this.currentFuture.isDone();
}
}
public Object get() throws InterruptedException, ExecutionException {
return this.currentFuture.get();
ScheduledFuture curr;
synchronized (this.triggerContextMonitor) {
curr = this.currentFuture;
}
return curr.get();
}
public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return this.currentFuture.get(timeout, unit);
ScheduledFuture curr;
synchronized (this.triggerContextMonitor) {
curr = this.currentFuture;
}
return curr.get(timeout, unit);
}
public long getDelay(TimeUnit unit) {
return this.currentFuture.getDelay(unit);
ScheduledFuture curr;
synchronized (this.triggerContextMonitor) {
curr = this.currentFuture;
}
return curr.getDelay(unit);
}
public int compareTo(Delayed other) {
@@ -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>
@@ -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.
@@ -20,7 +20,6 @@ import java.lang.reflect.Proxy;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.aop.TargetSource;
@@ -37,10 +36,12 @@ import org.springframework.context.MessageSource;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.context.support.StaticMessageSource;
import static org.junit.Assert.*;
/**
* @since 09.12.2003
* @author Juergen Hoeller
* @author Chris Beams
* @since 09.12.2003
*/
public final class AutoProxyCreatorTests {
@@ -113,20 +114,18 @@ public final class AutoProxyCreatorTests {
ITestBean singletonToBeProxied = (ITestBean) sac.getBean("singletonToBeProxied");
assertTrue(Proxy.isProxyClass(singletonToBeProxied.getClass()));
// 2 invocations coming from FactoryBean inspection during lifecycle startup
TestInterceptor ti = (TestInterceptor) sac.getBean("testInterceptor");
assertEquals(2, ti.nrOfInvocations);
int initialNr = ti.nrOfInvocations;
singletonToBeProxied.getName();
assertEquals(3, ti.nrOfInvocations);
assertEquals(initialNr + 1, ti.nrOfInvocations);
FactoryBean<?> factory = (FactoryBean<?>) sac.getBean("&singletonFactoryToBeProxied");
assertTrue(Proxy.isProxyClass(factory.getClass()));
TestBean tb = (TestBean) sac.getBean("singletonFactoryToBeProxied");
assertFalse(AopUtils.isAopProxy(tb));
assertEquals(5, ti.nrOfInvocations);
assertEquals(initialNr + 3, ti.nrOfInvocations);
tb.getAge();
assertEquals(5, ti.nrOfInvocations);
assertEquals(initialNr + 3, ti.nrOfInvocations);
}
@Test
@@ -1,12 +1,12 @@
/*
* 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.
* 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.
@@ -41,7 +41,7 @@ import org.springframework.beans.factory.support.MethodReplacer;
/**
* Types used by {@link XmlBeanFactoryTests} and its attendant XML config files.
*
*
* @author Chris Beams
*/
final class XmlBeanFactoryTestTypes { }
@@ -161,20 +161,20 @@ class SimpleConstructorArgBean {
* @author Rod Johnson
*/
abstract class ConstructorInjectedOverrides {
private ITestBean tb;
private String setterString;
public ConstructorInjectedOverrides(ITestBean tb) {
this.tb = tb;
}
public ITestBean getTestBean() {
return this.tb;
}
protected abstract FactoryMethods createFactoryMethods();
/**
@@ -234,7 +234,7 @@ class DerivedConstructorDependenciesBean extends ConstructorDependenciesBean {
/**
*
*
* @author Rod Johnson
*/
interface DummyBo {
@@ -245,19 +245,19 @@ interface DummyBo {
/**
*
*
* @author Rod Johnson
*/
class DummyBoImpl implements DummyBo {
DummyDao dao;
public DummyBoImpl(DummyDao dao) {
this.dao = dao;
}
public void something() {
}
}
@@ -267,7 +267,7 @@ class DummyBoImpl implements DummyBo {
* @author Rod Johnson
*/
class DummyDao {
DataSource ds;
public DummyDao(DataSource ds) {
@@ -332,7 +332,7 @@ class DummyReferencer {
* @author Juergen Hoeller
*/
class FactoryMethods {
public static FactoryMethods nullInstance() {
return null;
}
@@ -342,21 +342,21 @@ class FactoryMethods {
tb.setName("defaultInstance");
return new FactoryMethods(tb, "default", 0);
}
/**
* Note that overloaded methods are supported.
*/
public static FactoryMethods newInstance(TestBean tb) {
return new FactoryMethods(tb, "default", 0);
}
protected static FactoryMethods newInstance(TestBean tb, int num, String name) {
if (name == null) {
throw new IllegalStateException("Should never be called with null value");
}
return new FactoryMethods(tb, name, num);
}
static FactoryMethods newInstance(TestBean tb, int num, Integer something) {
if (something != null) {
throw new IllegalStateException("Should never be called with non-null value");
@@ -384,35 +384,35 @@ class FactoryMethods {
this.name = name;
this.num = num;
}
public void setStringValue(String stringValue) {
this.stringValue = stringValue;
}
public String getStringValue() {
return this.stringValue;
}
public TestBean getTestBean() {
return this.tb;
}
protected TestBean protectedGetTestBean() {
return this.tb;
}
private TestBean privateGetTestBean() {
return this.tb;
}
public int getNum() {
return num;
}
public String getName() {
return name;
}
/**
* Set via Setter Injection once instance is created.
*/
@@ -540,6 +540,9 @@ abstract class OverrideOneMethod extends MethodReplaceCandidate implements Overr
return "replaceMe:" + someParam;
}
public String replaceMe(String someParam) {
return "replaceMe:" + someParam;
}
}
@@ -550,7 +553,7 @@ abstract class OverrideOneMethod extends MethodReplaceCandidate implements Overr
* @author Rod Johnson
*/
abstract class OverrideOneMethodSubclass extends OverrideOneMethod {
protected void doSomething(String arg) {
// This implementation does nothing!
// It's not overloaded
@@ -1,22 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<beans>
<!--
<!--
Not yet in use: illustration of possible approach
-->
<bean id="overrideOneMethod" class="org.springframework.beans.factory.xml.OverrideOneMethod">
<lookup-method name="getPrototypeDependency" bean="jenny"/>
<lookup-method name="protectedOverrideSingleton" bean="david"/>
<!-- Arbitrary method replacer -->
<replaced-method name="replaceMe" replacer="reverseReplacer">
<arg-type>String</arg-type>
</replaced-method>
<replaced-method name="replaceMe" replacer="fixedReplacer"/>
</bean>
@@ -39,25 +39,25 @@
</bean>
<bean id="reverseReplacer"
<bean id="reverseReplacer"
class="org.springframework.beans.factory.xml.ReverseMethodReplacer"/>
<bean id="fixedReplacer"
<bean id="fixedReplacer"
class="org.springframework.beans.factory.xml.FixedMethodReplacer"/>
<bean id="doSomethingReplacer"
<bean id="doSomethingReplacer"
class="org.springframework.beans.factory.xml.XmlBeanFactoryTests$DoSomethingReplacer"/>
<bean id="serializableReplacer"
<bean id="serializableReplacer"
class="org.springframework.beans.factory.xml.SerializableMethodReplacerCandidate">
<!-- Arbitrary method replacer -->
<replaced-method name="replaceMe" replacer="reverseReplacer">
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="jenny" class="org.springframework.beans.TestBean"
scope="prototype">
<property name="name"><value>Jenny</value></property>
@@ -67,7 +67,7 @@
<ref local="david"/>
</property>
</bean>
<bean id="david" class="org.springframework.beans.TestBean"
scope="singleton">
<description>
@@ -80,4 +80,16 @@
<property name="age"><value>27</value></property>
</bean>
<bean id="overrideOneMethodByAttribute" class="org.springframework.beans.factory.xml.OverrideOneMethod">
<replaced-method name="replaceMe" replacer="reverseReplacer">
<arg-type match="String"/>
</replaced-method>
</bean>
<bean id="overrideOneMethodByElement" class="org.springframework.beans.factory.xml.OverrideOneMethod">
<replaced-method name="replaceMe" replacer="reverseReplacer">
<arg-type>String</arg-type>
</replaced-method>
</bean>
</beans>
@@ -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.
@@ -83,10 +83,10 @@ import org.xml.sax.InputSource;
* @author Chris Beams
*/
public final class XmlBeanFactoryTests {
private static final Class<?> CLASS = XmlBeanFactoryTests.class;
private static final String CLASSNAME = CLASS.getSimpleName();
private static final ClassPathResource AUTOWIRE_CONTEXT = classPathResource("-autowire.xml");
private static final ClassPathResource CHILD_CONTEXT = classPathResource("-child.xml");
private static final ClassPathResource CLASS_NOT_FOUND_CONTEXT = classPathResource("-classNotFound.xml");
@@ -128,7 +128,7 @@ public final class XmlBeanFactoryTests {
private static final ClassPathResource REFTYPES_CONTEXT = classPathResource("-reftypes.xml");
private static final ClassPathResource DEFAULT_LAZY_CONTEXT = classPathResource("-defaultLazyInit.xml");
private static final ClassPathResource DEFAULT_AUTOWIRE_CONTEXT = classPathResource("-defaultAutowire.xml");
private static ClassPathResource classPathResource(String suffix) {
return new ClassPathResource(CLASSNAME + suffix, CLASS);
}
@@ -1489,6 +1489,24 @@ public final class XmlBeanFactoryTests {
}
}
public @Test void testOverrideMethodByArgTypeAttribute() {
DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(xbf);
reader.loadBeanDefinitions(DELEGATION_OVERRIDES_CONTEXT);
OverrideOneMethod oom = (OverrideOneMethod) xbf.getBean("overrideOneMethodByAttribute");
assertEquals("should not replace", "replaceMe:1", oom.replaceMe(1));
assertEquals("should replace", "cba", oom.replaceMe("abc"));
}
public @Test void testOverrideMethodByArgTypeElement() {
DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(xbf);
reader.loadBeanDefinitions(DELEGATION_OVERRIDES_CONTEXT);
OverrideOneMethod oom = (OverrideOneMethod) xbf.getBean("overrideOneMethodByElement");
assertEquals("should not replace", "replaceMe:1", oom.replaceMe(1));
assertEquals("should replace", "cba", oom.replaceMe("abc"));
}
public static class DoSomethingReplacer implements MethodReplacer {
public Object lastArg;
@@ -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,17 +16,17 @@
package org.springframework.context.annotation;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;
import example.scannable.DefaultNamedComponent;
import org.junit.Test;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import example.scannable.DefaultNamedComponent;
import static org.junit.Assert.*;
/**
* @author Rick Evans
@@ -41,64 +41,44 @@ public class AnnotationBeanNameGeneratorTests {
@Test
public void testGenerateBeanNameWithNamedComponent() {
BeanDefinitionRegistry registry = createMock(BeanDefinitionRegistry.class);
replay(registry);
BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(ComponentWithName.class);
String beanName = this.beanNameGenerator.generateBeanName(bd, registry);
assertNotNull("The generated beanName must *never* be null.", beanName);
assertTrue("The generated beanName must *never* be blank.", StringUtils.hasText(beanName));
assertEquals("walden", beanName);
verify(registry);
}
@Test
public void testGenerateBeanNameWithDefaultNamedComponent() {
BeanDefinitionRegistry registry = createMock(BeanDefinitionRegistry.class);
replay(registry);
BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(DefaultNamedComponent.class);
String beanName = this.beanNameGenerator.generateBeanName(bd, registry);
assertNotNull("The generated beanName must *never* be null.", beanName);
assertTrue("The generated beanName must *never* be blank.", StringUtils.hasText(beanName));
assertEquals("thoreau", beanName);
verify(registry);
}
@Test
public void testGenerateBeanNameWithNamedComponentWhereTheNameIsBlank() {
BeanDefinitionRegistry registry = createMock(BeanDefinitionRegistry.class);
replay(registry);
BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(ComponentWithBlankName.class);
String beanName = this.beanNameGenerator.generateBeanName(bd, registry);
assertNotNull("The generated beanName must *never* be null.", beanName);
assertTrue("The generated beanName must *never* be blank.", StringUtils.hasText(beanName));
String expectedGeneratedBeanName = this.beanNameGenerator.buildDefaultBeanName(bd);
assertEquals(expectedGeneratedBeanName, beanName);
verify(registry);
}
@Test
public void testGenerateBeanNameWithAnonymousComponentYieldsGeneratedBeanName() {
BeanDefinitionRegistry registry = createMock(BeanDefinitionRegistry.class);
replay(registry);
BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(AnonymousComponent.class);
String beanName = this.beanNameGenerator.generateBeanName(bd, registry);
assertNotNull("The generated beanName must *never* be null.", beanName);
assertTrue("The generated beanName must *never* be blank.", StringUtils.hasText(beanName));
String expectedGeneratedBeanName = this.beanNameGenerator.buildDefaultBeanName(bd);
assertEquals(expectedGeneratedBeanName, beanName);
verify(registry);
}
@@ -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.
@@ -32,6 +32,7 @@ import org.springframework.beans.TestBean;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
@@ -164,6 +165,72 @@ public class CommonAnnotationBeanPostProcessorTests {
assertTrue(bean.destroy3Called);
}
@Test
public void testResourceInjectionWithPrototypes() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
CommonAnnotationBeanPostProcessor bpp = new CommonAnnotationBeanPostProcessor();
bpp.setResourceFactory(bf);
bf.addBeanPostProcessor(bpp);
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ResourceInjectionBean.class, false));
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class, false));
bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class, false));
ResourceInjectionBean bean = (ResourceInjectionBean) bf.getBean("annotatedBean");
assertTrue(bean.initCalled);
assertTrue(bean.init2Called);
assertTrue(bean.init3Called);
TestBean tb = bean.getTestBean();
TestBean tb2 = bean.getTestBean2();
assertNotNull(tb);
assertNotNull(tb2);
ResourceInjectionBean anotherBean = (ResourceInjectionBean) bf.getBean("annotatedBean");
assertNotSame(anotherBean, bean);
assertNotSame(anotherBean.getTestBean(), tb);
assertNotSame(anotherBean.getTestBean2(), tb2);
bf.destroyBean("annotatedBean", bean);
assertTrue(bean.destroyCalled);
assertTrue(bean.destroy2Called);
assertTrue(bean.destroy3Called);
}
@Test
public void testResourceInjectionWithResolvableDependencyType() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
CommonAnnotationBeanPostProcessor bpp = new CommonAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ExtendedResourceInjectionBean.class, false));
bf.registerBeanDefinition("testBean4", new RootBeanDefinition(TestBean.class, false));
bf.registerResolvableDependency(BeanFactory.class, bf);
bf.registerResolvableDependency(INestedTestBean.class, new ObjectFactory() {
public Object getObject() throws BeansException {
return new NestedTestBean();
}
});
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
Properties props = new Properties();
props.setProperty("tb", "testBean4");
ppc.setProperties(props);
ppc.postProcessBeanFactory(bf);
ExtendedResourceInjectionBean bean = (ExtendedResourceInjectionBean) bf.getBean("annotatedBean");
INestedTestBean tb = bean.getTestBean6();
assertNotNull(tb);
ExtendedResourceInjectionBean anotherBean = (ExtendedResourceInjectionBean) bf.getBean("annotatedBean");
assertNotSame(anotherBean, bean);
assertNotSame(anotherBean.getTestBean6(), tb);
String[] depBeans = bf.getDependenciesForBean("annotatedBean");
assertEquals(1, depBeans.length);
assertEquals("testBean4", depBeans[0]);
}
@Test
public void testResourceInjectionWithTwoProcessors() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -522,6 +589,14 @@ public class CommonAnnotationBeanPostProcessorTests {
return testBean4;
}
public INestedTestBean getTestBean5() {
return testBean5;
}
public INestedTestBean getTestBean6() {
return testBean6;
}
@PostConstruct
protected void init2() {
if (this.testBean3 == null || this.testBean4 == 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.
@@ -16,14 +16,17 @@
package org.springframework.context.annotation.configuration;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import test.beans.ITestBean;
import test.beans.TestBean;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor;
@@ -32,6 +35,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.ListFactoryBean;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
@@ -46,8 +50,7 @@ import org.springframework.context.annotation.Scope;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.support.GenericApplicationContext;
import test.beans.ITestBean;
import test.beans.TestBean;
import static org.junit.Assert.*;
/**
* Miscellaneous system tests covering {@link Bean} naming, aliases, scoping and error
@@ -65,7 +68,7 @@ public class ConfigurationClassProcessingTests {
* When complete, the factory is ready to service requests for any {@link Bean} methods
* declared by <var>configClasses</var>.
*/
private BeanFactory initBeanFactory(Class<?>... configClasses) {
private ListableBeanFactory initBeanFactory(Class<?>... configClasses) {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
for (Class<?> configClass : configClasses) {
String configBeanName = configClass.getName();
@@ -126,6 +129,42 @@ public class ConfigurationClassProcessingTests {
assertEquals(stringBean, "foo");
}
@Test
public void configWithObjectReturnType() {
BeanFactory factory = initBeanFactory(ConfigWithNonSpecificReturnTypes.class);
assertEquals(Object.class, factory.getType("stringBean"));
assertFalse(factory.isTypeMatch("stringBean", String.class));
String stringBean = factory.getBean("stringBean", String.class);
assertEquals(stringBean, "foo");
}
@Test
public void configWithFactoryBeanReturnType() {
ListableBeanFactory factory = initBeanFactory(ConfigWithNonSpecificReturnTypes.class);
assertEquals(List.class, factory.getType("factoryBean"));
assertTrue(factory.isTypeMatch("factoryBean", List.class));
assertEquals(FactoryBean.class, factory.getType("&factoryBean"));
assertTrue(factory.isTypeMatch("&factoryBean", FactoryBean.class));
assertFalse(factory.isTypeMatch("&factoryBean", BeanClassLoaderAware.class));
assertFalse(factory.isTypeMatch("&factoryBean", ListFactoryBean.class));
assertTrue(factory.getBean("factoryBean") instanceof List);
String[] beanNames = factory.getBeanNamesForType(FactoryBean.class);
assertEquals(1, beanNames.length);
assertEquals("&factoryBean", beanNames[0]);
beanNames = factory.getBeanNamesForType(BeanClassLoaderAware.class);
assertEquals(1, beanNames.length);
assertEquals("&factoryBean", beanNames[0]);
beanNames = factory.getBeanNamesForType(ListFactoryBean.class);
assertEquals(1, beanNames.length);
assertEquals("&factoryBean", beanNames[0]);
beanNames = factory.getBeanNamesForType(List.class);
assertEquals("factoryBean", beanNames[0]);
}
@Test
public void configurationWithPrototypeScopedBeans() {
BeanFactory factory = initBeanFactory(ConfigWithPrototypeBean.class);
@@ -186,6 +225,19 @@ public class ConfigurationClassProcessingTests {
}
@Configuration
static class ConfigWithNonSpecificReturnTypes {
public @Bean Object stringBean() {
return "foo";
}
public @Bean FactoryBean factoryBean() {
ListFactoryBean fb = new ListFactoryBean();
fb.setSourceList(Arrays.asList("element1", "element2"));
return fb;
}
}
@Configuration
static class ConfigWithBeanWithAliases {
@@ -0,0 +1,52 @@
/*
* 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.context.support;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
/**
* Tests covering the integration of {@link Environment} into {@link ApplicationContext} hierarchies.
*
* @author Chris Beams
*/
public class EnvironmentIntegrationTests {
@SuppressWarnings("unchecked")
@Test
public void repro() {
ConfigurableApplicationContext parent = new GenericApplicationContext();
parent.refresh();
AnnotationConfigApplicationContext child = new AnnotationConfigApplicationContext();
child.setParent(parent);
child.refresh();
ConfigurableEnvironment env = child.getBean(ConfigurableEnvironment.class);
assertThat("unknown env", env, anyOf(
sameInstance(parent.getEnvironment()),
sameInstance(child.getEnvironment())));
assertThat("expected child ctx env", env, sameInstance(child.getEnvironment()));
}
}
@@ -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.
@@ -19,6 +19,7 @@ package org.springframework.context.support;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.ResourceBundle;
import junit.framework.TestCase;
@@ -46,6 +47,20 @@ public class ResourceBundleMessageSourceTests extends TestCase {
doTestMessageAccess(false, true, true, true, false);
}
public void testMessageAccessWithDefaultMessageSourceAndFallbackTurnedOff() {
if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_16) {
return;
}
doTestMessageAccess(false, false, false, false, false);
}
public void testMessageAccessWithDefaultMessageSourceAndFallbackTurnedOffAndFallbackToGerman() {
if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_16) {
return;
}
doTestMessageAccess(false, false, true, true, false);
}
public void testMessageAccessWithReloadableMessageSource() {
doTestMessageAccess(true, true, false, false, false);
}
@@ -82,13 +97,13 @@ public class ResourceBundleMessageSourceTests extends TestCase {
String[] basenames = null;
if (reloadable) {
basenames = new String[] {
"classpath:" + basepath + "messages",
"classpath:" + basepath + "more-messages"};
"classpath:" + basepath + "messages",
"classpath:" + basepath + "more-messages"};
}
else {
basenames = new String[] {
basepath + "messages",
basepath + "more-messages"};
basepath + "messages",
basepath + "more-messages"};
}
pvs.add("basenames", basenames);
if (!fallbackToSystemLocale) {
@@ -202,6 +217,31 @@ public class ResourceBundleMessageSourceTests extends TestCase {
assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN));
}
public void testResourceBundleMessageSourceWithDefaultCharset() {
ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
ms.setBasename("org/springframework/context/support/messages");
ms.setDefaultEncoding("ISO-8859-1");
assertEquals("message1", ms.getMessage("code1", null, Locale.ENGLISH));
assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN));
}
public void testResourceBundleMessageSourceWithInappropriateDefaultCharset() {
if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_16) {
return;
}
ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
ms.setBasename("org/springframework/context/support/messages");
ms.setDefaultEncoding("argh");
ms.setFallbackToSystemLocale(false);
try {
ms.getMessage("code1", null, Locale.ENGLISH);
fail("Should have thrown NoSuchMessageException");
}
catch (NoSuchMessageException ex) {
// expected
}
}
public void testReloadableResourceBundleMessageSourceStandalone() {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
ms.setBasename("org/springframework/context/support/messages");
@@ -300,4 +340,11 @@ public class ResourceBundleMessageSourceTests extends TestCase {
assertEquals(0, filenames.size());
}
@Override
protected void tearDown() throws Exception {
if (JdkVersion.getMajorJavaVersion() >= JdkVersion.JAVA_16) {
ResourceBundle.clearCache();
}
}
}
@@ -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,8 +16,6 @@
package org.springframework.format.datetime.joda;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
@@ -34,6 +32,7 @@ import org.joda.time.MutableDateTime;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.convert.support.DefaultConversionService;
@@ -42,6 +41,8 @@ import org.springframework.format.annotation.DateTimeFormat.ISO;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.validation.DataBinder;
import static org.junit.Assert.*;
/**
* @author Keith Donald
* @author Juergen Hoeller
@@ -96,7 +97,7 @@ public class JodaTimeFormattingTests {
@Test
public void testBindLocalDateArray() {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("localDate", new String[] {"10/31/09"});
propertyValues.add("localDate", new String[]{"10/31/09"});
binder.bind(propertyValues);
assertEquals(0, binder.getBindingResult().getErrorCount());
}
@@ -240,6 +241,24 @@ public class JodaTimeFormattingTests {
assertEquals("10/31/09 12:00 PM", binder.getBindingResult().getFieldValue("date"));
}
@Test
public void testBindDateWithErrorAvoidingDateConstructor() {
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("date", "Sat, 12 Aug 1995 13:30:00 GMT");
binder.bind(propertyValues);
assertEquals(1, binder.getBindingResult().getErrorCount());
assertEquals("Sat, 12 Aug 1995 13:30:00 GMT", binder.getBindingResult().getFieldValue("date"));
}
@Test
public void testBindDateWithoutErrorFallingBackToDateConstructor() {
DataBinder binder = new DataBinder(new JodaTimeBean());
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("date", "Sat, 12 Aug 1995 13:30:00 GMT");
binder.bind(propertyValues);
assertEquals(0, binder.getBindingResult().getErrorCount());
}
@Test
public void testBindDateAnnotated() {
MutablePropertyValues propertyValues = new MutablePropertyValues();
@@ -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.
@@ -16,10 +16,10 @@
package org.springframework.scheduling.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
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 +27,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 +59,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 +178,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 +189,44 @@ 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-"));
}
@MyAsync
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("e2")
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAsync {
}
@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 +249,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 +278,6 @@ public class AsyncExecutionTests {
}
public void doSomething(int i) {
System.out.println(Thread.currentThread().getName() + ": " + i);
assertTrue(!Thread.currentThread().getName().equals(originalThreadName));
}
@@ -235,7 +288,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 +299,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
@@ -14,6 +14,7 @@
<orderEntry type="library" name="AspectJ" level="project" />
<orderEntry type="library" name="Commons Logging" level="project" />
<orderEntry type="library" name="EasyMock" level="project" />
<orderEntry type="library" name="Hamcrest" level="project" />
<orderEntry type="library" name="JUnit" level="project" />
<orderEntry type="library" name="Log4j" level="project" />
<orderEntry type="module-library">
+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.3.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.3.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.
@@ -35,6 +35,8 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import org.springframework.util.LinkedCaseInsensitiveMap;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
* Factory for collections, being aware of Java 5 and Java 6 collections.
@@ -253,7 +255,8 @@ public abstract class CollectionFactory {
return (Collection) collectionType.newInstance();
}
catch (Exception ex) {
throw new IllegalArgumentException("Could not instantiate Collection type: " + collectionType.getName());
throw new IllegalArgumentException("Could not instantiate Collection type: " +
collectionType.getName(), ex);
}
}
}
@@ -305,6 +308,9 @@ public abstract class CollectionFactory {
else if (SortedMap.class.equals(mapType) || mapType.equals(navigableMapClass)) {
return new TreeMap();
}
else if (MultiValueMap.class.equals(mapType)) {
return new LinkedMultiValueMap();
}
else {
throw new IllegalArgumentException("Unsupported Map interface: " + mapType.getName());
}
@@ -317,7 +323,8 @@ public abstract class CollectionFactory {
return (Map) mapType.newInstance();
}
catch (Exception ex) {
throw new IllegalArgumentException("Could not instantiate Map type: " + mapType.getName());
throw new IllegalArgumentException("Could not instantiate Map type: " +
mapType.getName(), ex);
}
}
}
@@ -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.
@@ -58,7 +58,7 @@ public class Constants {
* @param clazz the class to analyze
* @throws IllegalArgumentException if the supplied <code>clazz</code> is <code>null</code>
*/
public Constants(Class clazz) {
public Constants(Class<?> clazz) {
Assert.notNull(clazz);
this.className = clazz.getName();
Field[] fields = clazz.getFields();
@@ -189,7 +189,7 @@ public class Constants {
* @param nameSuffix suffix of the constant names to search (may be <code>null</code>)
* @return the set of constant names
*/
public Set getNamesForSuffix(String nameSuffix) {
public Set<String> getNamesForSuffix(String nameSuffix) {
String suffixToUse = (nameSuffix != null ? nameSuffix.trim().toUpperCase(Locale.ENGLISH) : "");
Set<String> names = new HashSet<String>();
for (String code : this.fieldCache.keySet()) {
@@ -264,7 +264,7 @@ public class Constants {
* @throws ConstantException if the value wasn't found
*/
public String toCode(Object value, String namePrefix) throws ConstantException {
String prefixToUse = (namePrefix != null ? namePrefix.trim().toUpperCase(Locale.ENGLISH) : null);
String prefixToUse = (namePrefix != null ? namePrefix.trim().toUpperCase(Locale.ENGLISH) : "");
for (Map.Entry<String, Object> entry : this.fieldCache.entrySet()) {
if (entry.getKey().startsWith(prefixToUse) && entry.getValue().equals(value)) {
return entry.getKey();
@@ -295,7 +295,7 @@ public class Constants {
* @throws ConstantException if the value wasn't found
*/
public String toCodeForSuffix(Object value, String nameSuffix) throws ConstantException {
String suffixToUse = (nameSuffix != null ? nameSuffix.trim().toUpperCase(Locale.ENGLISH) : null);
String suffixToUse = (nameSuffix != null ? nameSuffix.trim().toUpperCase(Locale.ENGLISH) : "");
for (Map.Entry<String, Object> entry : this.fieldCache.entrySet()) {
if (entry.getKey().endsWith(suffixToUse) && entry.getValue().equals(value)) {
return entry.getKey();
@@ -319,17 +319,17 @@ public class Constants {
* @see #toCodeForProperty
*/
public String propertyToConstantNamePrefix(String propertyName) {
StringBuilder parsedPrefix = new StringBuilder();
for(int i = 0; i < propertyName.length(); i++) {
char c = propertyName.charAt(i);
if (Character.isUpperCase(c)) {
parsedPrefix.append("_");
parsedPrefix.append(c);
}
else {
parsedPrefix.append(Character.toUpperCase(c));
}
}
StringBuilder parsedPrefix = new StringBuilder();
for(int i = 0; i < propertyName.length(); i++) {
char c = propertyName.charAt(i);
if (Character.isUpperCase(c)) {
parsedPrefix.append("_");
parsedPrefix.append(c);
}
else {
parsedPrefix.append(Character.toUpperCase(c));
}
}
return parsedPrefix.toString();
}
@@ -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,7 +16,9 @@
package org.springframework.core.convert.support;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -62,18 +64,25 @@ final class MapToMapConverter implements ConditionalGenericConverter {
if (!copyRequired && sourceMap.isEmpty()) {
return sourceMap;
}
Map<Object, Object> targetMap = CollectionFactory.createMap(targetType.getType(), sourceMap.size());
List<MapEntry> targetEntries = new ArrayList<MapEntry>(sourceMap.size());
for (Map.Entry<Object, Object> entry : sourceMap.entrySet()) {
Object sourceKey = entry.getKey();
Object sourceValue = entry.getValue();
Object targetKey = convertKey(sourceKey, sourceType, targetType.getMapKeyTypeDescriptor());
Object targetValue = convertValue(sourceValue, sourceType, targetType.getMapValueTypeDescriptor());
targetMap.put(targetKey, targetValue);
targetEntries.add(new MapEntry(targetKey, targetValue));
if (sourceKey != targetKey || sourceValue != targetValue) {
copyRequired = true;
}
}
return (copyRequired ? targetMap : sourceMap);
if(!copyRequired) {
return sourceMap;
}
Map<Object, Object> targetMap = CollectionFactory.createMap(targetType.getType(), sourceMap.size());
for (MapEntry entry : targetEntries) {
entry.addToMap(targetMap);
}
return targetMap;
}
// internal helpers
@@ -102,4 +111,19 @@ final class MapToMapConverter implements ConditionalGenericConverter {
return this.conversionService.convert(sourceValue, sourceType.getMapValueTypeDescriptor(sourceValue), targetType);
}
private static class MapEntry {
private Object key;
private Object value;
public MapEntry(Object key, Object value) {
this.key = key;
this.value = value;
}
public void addToMap(Map<Object, Object> map) {
map.put(key, value);
}
}
}
@@ -17,7 +17,6 @@
package org.springframework.core.env;
import java.security.AccessControlException;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Map;
@@ -41,9 +40,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
@@ -87,15 +86,14 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
*/
protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";
protected final Log logger = LogFactory.getLog(getClass());
private Set<String> activeProfiles = new LinkedHashSet<String>();
private Set<String> defaultProfiles =
new LinkedHashSet<String>(this.getReservedDefaultProfiles());
private Set<String> defaultProfiles = new LinkedHashSet<String>(getReservedDefaultProfiles());
private final MutablePropertySources propertySources =
new MutablePropertySources(this.logger);
private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);
@@ -109,13 +107,11 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
* @see #customizePropertySources(MutablePropertySources)
*/
public AbstractEnvironment() {
String name = this.getClass().getSimpleName();
String name = getClass().getSimpleName();
if (this.logger.isDebugEnabled()) {
this.logger.debug(format("Initializing new %s", name));
}
this.customizePropertySources(this.propertySources);
customizePropertySources(this.propertySources);
if (this.logger.isDebugEnabled()) {
this.logger.debug(format(
"Initialized %s with PropertySources %s", name, this.propertySources));
@@ -244,7 +240,7 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
Assert.notNull(profiles, "Profile array must not be null");
this.activeProfiles.clear();
for (String profile : profiles) {
this.addActiveProfile(profile);
addActiveProfile(profile);
}
}
@@ -252,7 +248,7 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
if (this.logger.isDebugEnabled()) {
this.logger.debug(format("Activating profile '%s'", profile));
}
this.validateProfile(profile);
validateProfile(profile);
this.activeProfiles.add(profile);
}
@@ -273,10 +269,10 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
* @see #getReservedDefaultProfiles()
*/
protected Set<String> doGetDefaultProfiles() {
if (this.defaultProfiles.equals(this.getReservedDefaultProfiles())) {
if (this.defaultProfiles.equals(getReservedDefaultProfiles())) {
String profiles = this.getProperty(DEFAULT_PROFILES_PROPERTY_NAME);
if (StringUtils.hasText(profiles)) {
this.setDefaultProfiles(commaDelimitedListToStringArray(trimAllWhitespace(profiles)));
setDefaultProfiles(commaDelimitedListToStringArray(trimAllWhitespace(profiles)));
}
}
return this.defaultProfiles;
@@ -293,38 +289,49 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
Assert.notNull(profiles, "Profile array must not be null");
this.defaultProfiles.clear();
for (String profile : profiles) {
this.validateProfile(profile);
validateProfile(profile);
this.defaultProfiles.add(profile);
}
}
public boolean acceptsProfiles(String... profiles) {
Assert.notEmpty(profiles, "Must specify at least one profile");
boolean activeProfileFound = false;
Set<String> activeProfiles = this.doGetActiveProfiles();
Set<String> defaultProfiles = this.doGetDefaultProfiles();
for (String profile : profiles) {
this.validateProfile(profile);
if (activeProfiles.contains(profile)
|| (activeProfiles.isEmpty() && defaultProfiles.contains(profile))) {
activeProfileFound = true;
break;
if (profile != null && profile.length() > 0 && profile.charAt(0) == '!') {
return !isProfileActive(profile.substring(1));
}
if (isProfileActive(profile)) {
return true;
}
}
return activeProfileFound;
return false;
}
/**
* Return whether the given profile is active, or if active profiles are empty
* whether the profile should be active by default.
* @throws IllegalArgumentException per {@link #validateProfile(String)}
*/
protected boolean isProfileActive(String profile) {
validateProfile(profile);
return doGetActiveProfiles().contains(profile) ||
(doGetActiveProfiles().isEmpty() && doGetDefaultProfiles().contains(profile));
}
/**
* Validate the given profile, called internally prior to adding to the set of
* active or default profiles.
* <p>Subclasses may override to impose further restrictions on profile syntax.
* @throws IllegalArgumentException if the profile is null, empty or whitespace-only
* @throws IllegalArgumentException if the profile is null, empty, whitespace-only or
* begins with the profile NOT operator (!).
* @see #acceptsProfiles
* @see #addActiveProfile
* @see #setDefaultProfiles
*/
protected void validateProfile(String profile) {
Assert.hasText(profile, "Invalid profile [" + profile + "]: must contain text");
Assert.isTrue(profile.charAt(0) != '!',
"Invalid profile [" + profile + "]: must not begin with the ! operator");
}
public MutablePropertySources getPropertySources() {
@@ -387,6 +394,23 @@ 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);
}
if (parent.getDefaultProfiles().length > 0) {
this.defaultProfiles.remove(RESERVED_DEFAULT_PROFILE_NAME);
for (String profile : parent.getDefaultProfiles()) {
this.defaultProfiles.add(profile);
}
}
}
//---------------------------------------------------------------------
// Implementation of ConfigurablePropertyResolver interface
@@ -452,12 +476,10 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
this.propertyResolver.setPlaceholderPrefix(placeholderPrefix);
}
public void setPlaceholderSuffix(String placeholderSuffix) {
this.propertyResolver.setPlaceholderSuffix(placeholderSuffix);
}
public void setValueSeparator(String valueSeparator) {
this.propertyResolver.setValueSeparator(valueSeparator);
}
@@ -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() {
@@ -90,8 +91,10 @@ public class MutablePropertySources implements PropertySources {
* Add the given property source object with highest precedence.
*/
public void addFirst(PropertySource<?> propertySource) {
logger.debug(String.format("Adding [%s] PropertySource with highest search precedence",
propertySource.getName()));
if (logger.isDebugEnabled()) {
logger.debug(String.format("Adding [%s] PropertySource with highest search precedence",
propertySource.getName()));
}
removeIfPresent(propertySource);
this.propertySourceList.addFirst(propertySource);
}
@@ -100,8 +103,10 @@ public class MutablePropertySources implements PropertySources {
* Add the given property source object with lowest precedence.
*/
public void addLast(PropertySource<?> propertySource) {
logger.debug(String.format("Adding [%s] PropertySource with lowest search precedence",
propertySource.getName()));
if (logger.isDebugEnabled()) {
logger.debug(String.format("Adding [%s] PropertySource with lowest search precedence",
propertySource.getName()));
}
removeIfPresent(propertySource);
this.propertySourceList.addLast(propertySource);
}
@@ -111,8 +116,10 @@ public class MutablePropertySources implements PropertySources {
* than the named relative property source.
*/
public void addBefore(String relativePropertySourceName, PropertySource<?> propertySource) {
logger.debug(String.format("Adding [%s] PropertySource with search precedence immediately higher than [%s]",
propertySource.getName(), relativePropertySourceName));
if (logger.isDebugEnabled()) {
logger.debug(String.format("Adding [%s] PropertySource with search precedence immediately higher than [%s]",
propertySource.getName(), relativePropertySourceName));
}
assertLegalRelativeAddition(relativePropertySourceName, propertySource);
removeIfPresent(propertySource);
int index = assertPresentAndGetIndex(relativePropertySourceName);
@@ -124,8 +131,10 @@ public class MutablePropertySources implements PropertySources {
* than the named relative property source.
*/
public void addAfter(String relativePropertySourceName, PropertySource<?> propertySource) {
logger.debug(String.format("Adding [%s] PropertySource with search precedence immediately lower than [%s]",
propertySource.getName(), relativePropertySourceName));
if (logger.isDebugEnabled()) {
logger.debug(String.format("Adding [%s] PropertySource with search precedence immediately lower than [%s]",
propertySource.getName(), relativePropertySourceName));
}
assertLegalRelativeAddition(relativePropertySourceName, propertySource);
removeIfPresent(propertySource);
int index = assertPresentAndGetIndex(relativePropertySourceName);
@@ -144,12 +153,11 @@ public class MutablePropertySources implements PropertySources {
* @param name the name of the property source to find and remove
*/
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);
if (logger.isDebugEnabled()) {
logger.debug(String.format("Removing [%s] PropertySource", name));
}
return null;
int index = this.propertySourceList.indexOf(PropertySource.named(name));
return index == -1 ? null : this.propertySourceList.remove(index);
}
/**
@@ -160,8 +168,10 @@ public class MutablePropertySources implements PropertySources {
* @see #contains
*/
public void replace(String name, PropertySource<?> propertySource) {
logger.debug(String.format("Replacing [%s] PropertySource with [%s]",
name, propertySource.getName()));
if (logger.isDebugEnabled()) {
logger.debug(String.format("Replacing [%s] PropertySource with [%s]",
name, propertySource.getName()));
}
int index = assertPresentAndGetIndex(name);
this.propertySourceList.set(index, propertySource);
}
@@ -210,11 +220,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;
}
}
}

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