Compare commits

...

799 Commits

Author SHA1 Message Date
Spring Buildmaster c125f44d17 Release version 4.1.0.RC2 2014-08-15 01:37:05 -07:00
Sam Brannen 8b86519b94 Polishing 2014-08-15 02:50:03 +02:00
Sam Brannen 66250b1f8e Support merging custom TELs with default TELs
Prior to this commit, if a custom TestExecutionListener was registered
via @TestExecutionListeners the defaults would not be registered. Thus,
if a user wanted to declare a custom listener and use the default
listeners, the user was forced to manually declare all default
listeners in addition to any custom listeners. This unfortunately
required that the user know exactly which listeners were registered by
default. Moreover, the set of default listeners can change from release
to release, and with the support for automatic discovery of default
listeners introduced in SPR-11466 it is no longer even possible to know
what the set of default TestExecutionListeners is before runtime.

This commit addresses this issue by introducing a mechanism for merging
custom declared listeners with the defaults for the current
environment. Specifically, @TestExecutionListeners supports a new
MergeMode that is used to control whether or not explicitly declared
listeners are merged with the default listeners when
@TestExecutionListeners is declared on a class that does not inherit
listeners from a superclass.

Issue: SPR-8854
2014-08-15 02:21:42 +02:00
Juergen Hoeller b3add794d7 Polishing 2014-08-15 00:55:40 +02:00
Juergen Hoeller d2e269e697 Fixed fragile tests for @Bean method overloading 2014-08-15 00:55:14 +02:00
Juergen Hoeller 69587f3fa4 Polishing 2014-08-14 23:49:29 +02:00
Juergen Hoeller eb0ab8431b Lookup methods can support arguments, find a target bean based on the return type, and be identified by an @Lookup annotation
Issue: SPR-7431
Issue: SPR-5192
2014-08-14 23:49:05 +02:00
Sam Brannen e753f23110 Merge from sbrannen/SPR-11466
* SPR-11466:
  Support automatic discovery of default TELs
2014-08-14 22:30:16 +02:00
Sam Brannen e6d16148e5 Support automatic discovery of default TELs
Prior to this commit, there was no declarative mechanism for a custom
TestExecutionListener to be registered as a default
TestExecutionListener.

This commit introduces support for discovering default
TestExecutionListener implementations via the SpringFactoriesLoader
mechanism. Specifically, the spring-test module declares all core
default TestExecutionListeners under the
org.springframework.test.context.TestExecutionListener key in its
META-INF/spring.factories properties file, and third-party frameworks
and developers can contribute to the list of default
TestExecutionListeners in the same manner.

 - AbstractTestContextBootstrapper uses the SpringFactoriesLoader to
   look up the class names of all registered default
   TestExecutionListeners and sorts the instantiated listeners using
   AnnotationAwareOrderComparator.

 - DefaultTestContextBootstrapper and WebTestContextBootstrapper now
   rely on the SpringFactoriesLoader mechanism for finding default
   TestExecutionListeners instead of hard coding fully qualified class
   names.

 - To ensure that default TestExecutionListeners are registered in the
   correct order, each can implement Ordered or declare @Order.

 - AbstractTestExecutionListener and all default TestExecutionListeners
   provided by Spring now implement Ordered with appropriate values.

 - Introduced "copy constructors" in MergedContextConfiguration and
   WebMergedContextConfiguration

 - SpringFactoriesLoader now uses AnnotationAwareOrderComparator
   instead of OrderComparator.

Issue: SPR-11466
2014-08-14 22:29:21 +02:00
Phillip Webb e142fd11e0 Add tests for ImportSelector meta-data
Issue: SPR-12059
2014-08-14 07:35:16 -07:00
Sebastien Deleuze ebc726a915 Allow null ObjectMapper in Jackson2ObjectMapperFactoryBean.getObjectType()
Issue: SPR-11785
2014-08-14 13:40:47 +02:00
Sam Brannen 181299cc6c Improve ex msg when locations & classes are declared in test hierarchy
Prior to this commit, if both locations and classes were declared via
@ContextConfiguration at differing levels in a test class hierarchy,
the exception message stated that neither of the default context
loaders was able to load an ApplicationContext from the merged context
configuration, but the message didn't explain why.

This commit adds an explicit check for such scenarios and provides a
more informative exception message similar to the following:

"Neither X nor Y supports loading an ApplicationContext from
[MergedContextConfiguration ...]: declare either 'locations' or
'classes' but not both."

Issue: SPR-12060
2014-08-14 01:23:11 +02:00
Sam Brannen f4c23d8715 Delete trailing white space in spring-test 2014-08-14 00:30:50 +02:00
Sam Brannen 2cbd5ba993 Clean up regarding deprecated HttpStatus.MOVED_TEMPORARILY 2014-08-14 00:29:09 +02:00
Sam Brannen 3e2138855a Add @Deprecated to WebMergedContextConfiguration constructor 2014-08-14 00:22:12 +02:00
Sam Brannen f2ac02566c Merge from sbrannen/SPR-12051
* SPR-12051:
  Introduce @TestPropertySource support in the TCF
2014-08-14 00:05:03 +02:00
Sam Brannen 2cf4147ba8 Introduce @TestPropertySource support in the TCF
Spring Framework 3.1 introduced an Environment abstraction with support
for hierarchical PropertySources that can be configured
programmatically as well as declaratively via the @PropertySource
annotation. However, prior to this commit, there was no way to
declaratively configure PropertySources in integration tests in the
Spring TestContext Framework (TCF).

This commit introduces declarative support for PropertySources in the
TCF via a new class-level @TestPropertySource annotation. This
annotation provides two options for declaring test property sources:

 - The 'locations' attribute allows developers to declare external
   resource locations for test properties files.

 - The 'properties' attribute allows developers to declare inlined
   properties in the form of key-value pairs.

Test properties files are added to the Environment before all other
property sources and can therefore override system and application
property sources. Similarly, inlined properties are added to the
Environment before all other property sources and can therefore
override system property sources, application property sources, and
test properties files.

Specifically, this commit introduces the following major changes:

 - Introduced @TestPropertySource annotation along with internal
   TestPropertySourceAttributes, MergedTestPropertySources, and
   TestPropertySourceUtils for working with test property sources
   within the TCF.

 - All TestContextBootstrappers have been modified to support the
   merged property resource locations and inlined properties from
   @TestPropertySource.

 - MergedContextConfiguration (and consequently the context caching
   key) is now additionally based on the merged property resource
   locations and inlined properties from @TestPropertySource. The same
   applies to WebMergedContextConfiguration.

 - AbstractContextLoader's prepareContext() method now adds
   PropertySources for all resource locations and inlined properties
   from the supplied MergedContextConfiguration to the Environment of
   the supplied ApplicationContext. All subclasses of
   AbstractGenericContextLoader and AbstractGenericWebContextLoader
   therefore automatically provide support for @TestPropertySource.

Issue: SPR-12051
2014-08-14 00:01:38 +02:00
Sebastien Deleuze 6665634675 Support Jackson based XML serialization/deserialization
This commit adds support for XML serialization/deserialization based on
the jackson-dataformat-xml extension. When using @EnableWebMvc or
<mvc:annotation-driven/>, Jackson will be used  by default instead of JAXB2
if jackson-dataformat-xml classes are found in the classpath.

This commit introduces MappingJackson2XmlHttpMessageConverter and
MappingJackson2XmlView classes, and common parts between JSON
and XML processing have been moved to AbstractJackson2HttpMessageConverter
and AbstractJackson2View classes.

MappingJackson2XmlView supports serialization of a single object. If the model
contains multiple entries, MappingJackson2XmlView.setModelKey() should be
used to specify the entry to serialize.

Pretty print works in XML, but tests are not included since a Woodstox dependency
is needed, and it is better to continue testing spring-web and spring-webmvc
against JAXB2.

Issue: SPR-11785
2014-08-13 16:22:58 +02:00
Juergen Hoeller 92bd240474 Polishing
Issue: SPR-12079
2014-08-13 15:04:58 +02:00
Juergen Hoeller 626a5fe4eb BeanNameViewResolver ignores non-View beans
Issue: SPR-12079
2014-08-13 14:57:40 +02:00
Phillip Webb 887815f4fa Update docbook dependency and generate epub
Issue: SPR-11167
2014-08-12 13:19:21 -07:00
Juergen Hoeller fd5dbddac4 BeanFactory supports bean creation arguments for by-type lookup as well
Issue: SPR-11235
2014-08-12 22:07:12 +02:00
Juergen Hoeller 8dbfa80b13 Unit test for circular reference in default profile property, plus related polishing
Issue: SPR-12078
2014-08-12 19:23:36 +02:00
Juergen Hoeller 71c6eb2bb5 Additional configuration classes get detected when imported through XML or registrars
Issue: SPR-11430
Issue: SPR-11723
2014-08-12 17:23:21 +02:00
Juergen Hoeller f1f1c4c980 Remove outdated IDE, JIRA and Tomcat references
Issue: SPR-7521
2014-08-12 16:32:20 +02:00
Sebastien Deleuze f56000b825 Replace http by https for spring.io links in the reference documentation 2014-08-12 11:15:55 +02:00
Juergen Hoeller 29cb7808f2 Merge pull request #621 from royclarkson/Gson_2_3
Upgrade to Gson 2.3
2014-08-12 10:52:33 +02:00
Roy Clarkson d6fd40d6dc Upgrade to Gson 2.3
This release contains the following new features:

- The new @JsonAdapter annotation to specify a Json TypeAdapter for a
  class field
- JsonPath support: JsonParser.getPath() method returns the JsonPath
  expression
- New public methods in JsonArray (similar to the java.util.List):
  contains(JsonElement), remove(JsonElement), remove(int index),
  set(int index, JsonElement element)
- Many other smaller bug fixes

See: https://groups.google.com/forum/#!topic/google-gson/MOqf5RGtIzk
2014-08-12 11:47:40 +03:00
Sebastien Deleuze 01cbf9b9ee Update references to SpringSource in the reference documentation 2014-08-12 10:31:41 +02:00
Phillip Webb ac8326d2df Polish mockito usage
Consistent use of BDDMockito rather than standard Mockito.
2014-08-11 16:23:11 -07:00
Juergen Hoeller 4db258b9e3 Polishing 2014-08-11 22:32:03 +02:00
Stephane Nicoll d100eef829 Cache documentation in multi-threaded environment
Issue: SPR-9588
2014-08-11 16:01:38 +02:00
Stephane Nicoll c0ea87010f Improve destination-type documentation
Issue: SPR-12073
2014-08-11 15:43:45 +02:00
Juergen Hoeller d6370965eb Polishing 2014-08-11 15:20:09 +02:00
Stephane Nicoll e2aba3d317 Add sonar integration.
This commit configures the SonarRunner plugin so that SonarQube can
be used against the project. A few customizations were applied to
namely exclude the asm and cglib repackaged classes.

Issue: SPR-10766
2014-08-11 09:38:00 +02:00
Juergen Hoeller 7c57424b48 Tiles 3 TilesViewResolver allows for specifying custom TilesView subclasses as well
Issue: SPR-12075
2014-08-10 01:08:27 +02:00
Juergen Hoeller a05b748d42 InternalResourceViewResolver's exposure of context beans is now available at UrlBasedViewResolver level
Issue: SPR-8064
2014-08-10 00:54:42 +02:00
Juergen Hoeller c06ac06bdd JmsMessagingTemplate uses local convertJmsException template method instead of generic MessagingExceptionTranslator interface
This commit also turns MessagingException into a NestedRuntimeException subclass which delivers a root message that has the cause message appended to it. That's a common expectation with the use of Spring exceptions since all of our exception hierarchies have historically been designed that way.

Issue: SPR-12064
Issue: SPR-12038
2014-08-09 22:06:50 +02:00
Juergen Hoeller 6b6ca4889b HibernateTransactionManagerTests for Hibernate 3 covers currentSession() as well
Issue: SPR-9020
2014-08-09 18:30:08 +02:00
Juergen Hoeller 5cbb1fc498 SpringSessionContext for Hibernate 4 supports lazily bound Session for propagation SUPPORTS
Issue: SPR-9020
2014-08-09 18:28:30 +02:00
Juergen Hoeller 562916b69c Consistent getJpaDialect() narrowing (along with HibernateJpaVendorAdapter)
Issue: SPR-8959
Issue: SPR-11942
2014-08-09 17:10:34 +02:00
Juergen Hoeller cbda722329 HibernateJpaDialect prepares JDBC Connection by default if on Hibernate EntityManager 4 (with its connection release mode ON_CLOSE)
Analogous to HibernateTransactionManager, there is a "prepareConnection" flag on HibernateJpaDialect which allows for overriding the actual mode of operation. This is easily accessible from HibernateJpaVendorAdapter now which declares HibernateJpaDialect from its getJpaDialect() method.

Issue: SPR-8959
Issue: SPR-11942
2014-08-09 17:09:42 +02:00
Juergen Hoeller e08c56fcae Linking to Java SE 8 javadocs, plus upgrade to Joda-Time 2.4 2014-08-09 00:30:10 +02:00
Juergen Hoeller 53192bb6f4 Polishing 2014-08-09 00:28:13 +02:00
Stephane Nicoll 5b35fc2d77 Add JmsTemplate accessor in JmsMessagingTemplate 2014-08-08 18:09:16 +02:00
Juergen Hoeller ad475ffadf Consistent vararg declarations for String array setters 2014-08-08 17:17:09 +02:00
Juergen Hoeller 47006eeff1 Fixed side effect on other tests
Issue: SPR-10459
2014-08-08 17:02:59 +02:00
Stephane Nicoll c1d149591a Add base header mapper implementation 2014-08-08 16:59:42 +02:00
Juergen Hoeller f2e4ad2364 ComponentScan annotation allows for registering beans with lazy initialization
Issue: SPR-10459
2014-08-08 15:39:06 +02:00
Juergen Hoeller ef51d4dbdb AnnotatedElementUtils adapts post-processed values to AnnotationAttributes as well
Issue: SPR-12065
2014-08-08 15:15:29 +02:00
Stephane Nicoll a635c36470 Fix doc typo 2014-08-08 13:17:55 +02:00
Stephane Nicoll 4a1a622004 polishing 2014-08-08 09:37:47 +02:00
Juergen Hoeller 3a419872c8 Polishing 2014-08-07 23:06:08 +02:00
Juergen Hoeller 5862ddc869 ResponseEntity provides static "notFound()" convenience method as well
Issue: SPR-12070
2014-08-07 22:28:35 +02:00
Juergen Hoeller 3227569a38 LocalSessionFactoryBean/Builder allows for specifying custom entity type filters
Issue: SPR-12049
2014-08-07 22:20:40 +02:00
Stephane Nicoll 0b2c0cfb4f Fix some warning 2014-08-07 15:44:39 +02:00
Stephane Nicoll e010309530 Use @Payload on method declaration
Issue: SPR-12071
2014-08-07 15:09:04 +02:00
Sebastien Deleuze 3922f6fc53 Update references to RFC 2616
Replace references to the old RFC 2616 (HTTP 1.1) with references
to the new RFCs 7230 to 7235.

This commit also deprecates:
 - HttpStatus.USE_PROXY
 - HttpStatus.REQUEST_ENTITY_TOO_LARGE in favor of HttpStatus.PAYLOAD_TOO_LARGE
 - HttpStatus.REQUEST_URI_TOO_LONG in favor of HttpStatus.URI_TOO_LONG

Issue: SPR-12067
2014-08-07 14:50:45 +02:00
Andy Clement 095bd99951 Add support for inline maps in SpEL expressions
This commit introduces the ability to specify an inline map in
an expression. The syntax is similar to inline lists and of
the form: "{key:value,key2:value}". The keys can optionally
be quoted. The documentation is also updated with information
on the syntax.

Issue: SPR-9472
2014-08-06 11:15:11 -07:00
Stephane Nicoll c29937196f Merge pull request #607 from dharaburda/SPR-12050
* SPR-12050:
  Change GenericConversionService to better handle enum
2014-08-06 10:45:12 +02:00
David Haraburda d0e6f0f73f Change GenericConversionService to better handle enum
Prior to this commit, given an enum which implements some interface,
GenericConversionService would select the String -> Enum converter even
if a converter for String -> SomeInterface was registered.  This also
affected converters that were registered for String ->
SomeBaseInterface, when SomeInterface extended SomeBaseInterface.

This change modifies the behavior of the private method
getClassHierarchy() by placing Enum.class as late as possible, pretty
much the same way as Object.class is handled.

Issue: SPR-12050
2014-08-06 10:42:28 +02:00
Sebastien Deleuze ebc5fea77b Add more HttpHeaders constants for standard HTTP headers
Issue: SPR-12063
2014-08-05 11:11:09 +02:00
Stephane Nicoll d6b1cdc55f Custom cache annotations and AspectJ
This commit clarifies what should be done in order to use custom cache
annotations with AspectJ.

Issue: SPR-11503
2014-08-04 17:26:01 +02:00
Stephane Nicoll 3c72849ee4 Fix broken link 2014-08-04 14:45:35 +02:00
Stephane Nicoll 59c5d7d1ca Add messaging module section
Issue: SPR-11847
2014-08-04 14:41:52 +02:00
Stephane Nicoll 32975be2a2 Avoid duplicate entries
Prior to this commit, the schema distribution archive contains
duplicate entries for the more recent XSD as it is reference both with
its version and as the "default" XSD (i.e. without a version).

This commit configures the task to apply a duplicatesStrategy so that
only the first entry remains.

Issue: SPR-12011
2014-08-04 14:21:00 +02:00
Stephane Nicoll 3da68cfe21 Remove unused imports 2014-08-04 14:13:40 +02:00
Sam Brannen 6cfe76eeec Polish Javadoc for MutablePropertySources 2014-08-02 12:17:57 +03:00
Sam Brannen 154cf5dc7e Polish Javadoc for AnnotationCacheKey 2014-08-02 12:17:15 +03:00
Rossen Stoyanchev b5e1198bd2 Fix white spaces 2014-08-01 17:12:43 -04:00
Jakub Narloch a11b62540a Support HTTP HEAD method in MockMvcRequestBuilders
Surprisingly until now the MockMvcRequestBuilders did not have methods
for HTTP HEAD. This change adds such methods to the  API making it
consistent with other HTTP method types.

Issue: SPR-12055
2014-08-01 17:03:12 -04:00
Rossen Stoyanchev fe88b6e054 Add to SockJsClient reference documentation
Issue: SPR-12006
2014-08-01 16:57:13 -04:00
Sebastien Deleuze 28c70baf61 Add documentation for Java SockJS client
Issue: SPR-12006
2014-08-01 16:57:13 -04:00
Brian Clozel 95e9b380d3 Update ResourceHandlerReg. API for Resource handling
This change adds new methods in the ResourceHandlerRegistration API
for registering ResourceResolvers and ResourceTransformers, allowing
to better handle server-side resources in web applications.i

Here is an example of configuration for an HTML5 web application
that uses JavaScript and HTML5 appcache manifests:

    registry.addResourceHandler("/**")
      .addResourceLocations("classpath:static/")
      .addTransformer(new AppCacheManifestTransfomer())
      .addVersion("v1", "/**/*.js")
      .addVersionHash("/**");

Issue: SPR-11982
2014-08-01 19:04:07 +02:00
Stephane Nicoll 7d1e33d88d Move JmsHandlerMethodFactory to spring-messaging
This commit moves JmsHandlerMethodFactory and its default
implementation to the messaging abstraction. Working on a similar
support for AMQP revealed that this factory has nothing that is JMS
specific and is exactly identical in the case of AMQP.

Issue: SPR-12053
2014-08-01 16:08:31 +02:00
Rossen Stoyanchev 25decee1a4 Polish 2014-07-31 17:47:24 -04:00
Sebastien Deleuze 87273d1b83 Enrich documentation with details on how to filter messages
Issue: SPR-11952
2014-07-31 17:47:24 -04:00
Rossen Stoyanchev 70ccbbfab1 Fix documentation issue for user destinations
Issue: SPR-11992
2014-07-31 17:12:32 -04:00
Rossen Stoyanchev 5859649af7 Update UriTemplate Javadoc
Issue: SPR-10497
2014-07-31 17:08:08 -04:00
Rossen Stoyanchev c4d7976c37 Update RestTemplate Javadoc
Issue: SPR-10497
2014-07-31 17:02:30 -04:00
Rossen Stoyanchev e4182da4eb Add section on path pattern comparison
Issue: SPR-10576
2014-07-31 16:28:00 -04:00
Rossen Stoyanchev 8751936b12 Fix issue with sub-protocol negotiation and SockJS
Before this change the SockJsWebSocketHandler precluded determination
of the sub-protocols by wrapping the actual target handler.

After this change SockJsWebSocketHandler implements SubProtocolCapable
and returns the list of sub-protocols from the wrapped handler.
2014-07-31 10:33:11 -04:00
Andy Clement 813cc3b339 Fix compilation of SpEL Indexer nodes involving map references
There is special handling for SpEL expressions involving a map
and an unquoted string literal key (e.g. mymap[key1]). SpEL does
not require key1 to be quoted. This special handling which is done
in Indexer getValueRef() was not being also done in the Indexer
generateCode() method that compiles the expression. Also fixed
a problem where the key was not being compiled in a new
sub scope. Without the new scope the key expression was failing
to reload the relevant context object when it needed it.

Issue: SPR-12045
2014-07-30 12:48:54 -07:00
Rossen Stoyanchev 135a3ff3d8 Use raw status code in RestTemplate logResponseStatus
Issue: SPR-12022
2014-07-30 15:43:43 -04:00
Rossen Stoyanchev 41cdc92fc3 Remove AbstractWebSocketClient from SockJsClient
SockJsClient now implements WebSocketClient directly without extending
AbstractWebSocketClient.

Issue: SPR-12030
2014-07-30 15:39:01 -04:00
Rossen Stoyanchev c68b4c01e1 Polish SockJsClient 2014-07-30 15:29:46 -04:00
Rossen Stoyanchev ba952ca331 Fix test condition 2014-07-30 09:02:23 -04:00
Stephane Nicoll 057efa0676 Align javadoc improvements 2014-07-30 09:29:24 +02:00
Stephane Nicoll 9fe7780865 Revert accidental class name change 2014-07-30 08:53:14 +02:00
Stephane Nicoll ce5ce77283 Merge pull request #605 from overmindx/master
* pull605:
  Fix javadoc typo
2014-07-30 08:47:20 +02:00
Kang Baek-kyu c7babab2dd Fix javadoc typo 2014-07-30 08:47:01 +02:00
Stephane Nicoll 515b9649fa Fix javadoc typo 2014-07-30 08:45:26 +02:00
Rossen Stoyanchev 16273473cb Update Javadoc
Issue: SPR-12028
2014-07-29 18:06:05 -04:00
Rossen Stoyanchev f50cb06dc2 Polish 2014-07-29 17:58:16 -04:00
Sebastien Deleuze 20c9a8bad4 Improve documentation for using "." as destination separator
Issue: SPR-12007
2014-07-29 17:17:40 -04:00
Rossen Stoyanchev 8e38b7ede6 Add section on web security
Issue: SPR-6125
2014-07-29 16:56:37 -04:00
Andy Clement d30174897d Support compilation of the SpEL operator OpModulus
This commit enables the modulus operator to be compiled when
it is used as part of a SpEL expression.

Issue: SPR-12041
2014-07-29 12:34:44 -07:00
Andy Clement 59080ff2b2 Cope with generic methods during SpEL compilation
This commit allows the SpEL compiler to cope with generic methods
being used in expressions involving numeric operands. Due to the
use of unbound type variables the methods may look like they
return Object but in fact they are returning objects of a numeric
type that are suitable for compilation. The changes here ensure
the runtime types are looked at if the discovered declared types
are not providing enough information. This impacts all the
operands involving numerics (mathematical and relational).

Issue: SPR-12040
2014-07-29 11:02:56 -07:00
Rossen Stoyanchev 968492c28b Update constructor for AbstractJsonpResponseBodyAdvice 2014-07-29 11:42:18 -04:00
Juergen Hoeller 1fe742addf MessagingExceptionTranslator lives in support subpackage now
Issue: SPR-12038
2014-07-29 16:22:47 +02:00
Juergen Hoeller 9be04b3883 Latest dependency updates (Groovy 2.3.6, H2 1.4.180) 2014-07-29 14:11:59 +02:00
Juergen Hoeller 3bcfc591e9 Polishing 2014-07-29 14:11:19 +02:00
Stephane Nicoll 89d63eb79b JmsMessagingTemplate exception management
This commit introduces MessagingExceptionTranslator, a messaging
exception translation infrastructure similar to what
PersistenceExceptionTranslator provides.

JmsMessagingTemplate does not throw raw JmsException anymore but
translates those to an instance of Spring's MessagingException
hierarchy.

Issue: SPR-12038
2014-07-29 12:29:43 +02:00
Juergen Hoeller 8f484d382e Polishing 2014-07-29 11:42:37 +02:00
Juergen Hoeller 7bc966032b Rearranged statement whitespace in Velocity macros 2014-07-29 11:42:29 +02:00
Rossen Stoyanchev d917b8a923 Extract base interface for VersionStrategy
This change splits out a base VersionPathStrategy interface that
focuses on where in the URL path the version is embedded.
2014-07-29 10:41:02 +02:00
Juergen Hoeller 9f3c1cf762 Polishing 2014-07-28 23:05:08 +02:00
Juergen Hoeller f732a441c8 Latest dependency updates (Groovy 2.3.5, Hibernate Validator 4.3.2 & 5.1.2, Jetty 9.2.2) 2014-07-28 22:07:55 +02:00
Juergen Hoeller 8cc0fa5ae1 Polishing 2014-07-28 22:05:40 +02:00
Juergen Hoeller a3163ceb88 AbstractTypeHierarchyTraversingFilter leniently ignores non-loadable super classes and interfaces
Issue: SPR-12042
2014-07-28 22:05:20 +02:00
Juergen Hoeller 036896a6b8 Split configuration constants into local XConfigUtils classes instead of piling them up in AnnotationConfigUtils 2014-07-28 21:58:21 +02:00
Sam Brannen 7971f6b638 Document that mock request doesn't support Accept-Language header
This commit updates the Javadoc for getLocale() and getLocales() in
MockHttpServletRequest to point out that the mock implementation does
not comply with the the Servlet specification with regard to the
Accept-Language header.

Issue: SPR-12043
2014-07-28 19:12:39 +03:00
Sam Brannen 9bf73f3ab8 Document default locale in MockHttpServletRequest
This commit updates the class-level Javadoc for MockHttpServletRequest
with information regarding the default locale for the mocked server.

Issue: SPR-11701
2014-07-28 19:07:26 +03:00
Sam Brannen f862a009a7 Groovy loader should delegate to XML loader in the TCF
If a resource location in the MergedContextConfiguration has a ".xml"
extension, the GenericGroovyXmlContextLoader now delegates to a
dedicated XmlBeanDefinitionReader for loading bean definitions from that
resource, thus preserving XML validation for all XML resource locations.
For all other extensions (presumably only ".groovy"), the
GenericGroovyXmlContextLoader delegates to a GroovyBeanDefinitionReader.

Issue: SPR-11233
2014-07-28 17:55:57 +03:00
Juergen Hoeller 9fa4dad13c Polishing 2014-07-28 15:58:22 +02:00
Juergen Hoeller 92c657e12d JmsListener/ScheduledAnnotationBeanPostProcessor uses SmartInitializingSingleton instead of ContextRefreshedEvent
Also reducing the container dependencies to BeanFactory instead of ApplicationContext, wherever possible.

Issue: SPR-12039
2014-07-28 15:58:13 +02:00
Stephane Nicoll b6389a6c66 Jms request/reply operations
This commit updates JmsMessagingTemplate to support the
MessageRequestReplyOperation interface that provides synchronous
request/reply operations.

As JmsMessagingTemplate delegates everything under the scenes to
JmsTemplate, the latter has been updated as well to offer such lower
level operation.

Issue: SPR-12037
2014-07-28 15:52:50 +02:00
Rossen Stoyanchev c89325b9ca Add WebLogicRequestUpgradeStrategy
This change creates an AbstractTyrusRequestUpgradeStrategy shared
between the WebLogic and GlassFish sub-classes.

The version of Tyrus is lowered to 1.3.5 to match the version used
in WebLogic (12.1.3) and that in turn requires a little extra effort
in the base AbstractTyrusRequestUpgradeStrategy to make up for
changes that have taken place from Tyrus 1.3.5 to 1.7.

Issue: SPR-11293
2014-07-25 17:16:00 -04:00
Sam Brannen 0ad5de3f77 Document default TELs in AbstTxTestNGSpringContextTests 2014-07-24 19:04:11 +02:00
Sam Brannen 5a483f347a Polishing 2014-07-24 19:02:10 +02:00
Sam Brannen d144e2918e Document default TELs in AbstTxJUnit4SpringContextTests 2014-07-24 18:58:59 +02:00
Sam Brannen 0d4526e050 Update Javadoc re: minimum JUnit 4.9 dependency 2014-07-24 18:54:13 +02:00
Sam Brannen 170d6c9fb9 Delete trailing whitespace in spring-test 2014-07-24 18:13:38 +02:00
Stephane Nicoll 0a46f38527 polishing 2014-07-24 17:12:31 +02:00
Sam Brannen 105a6023b3 Merge from sbrannen/SPR-11233
* SPR-11233:
  Support Groovy scripts in the TCF
2014-07-24 17:09:19 +02:00
Sam Brannen 35c372f200 Support Groovy scripts in the TCF
Spring Framework 4.0 introduced first-class support for a Groovy-based
DSL for defining the beans for an ApplicationContext. However, prior to
this commit, the Spring TestContext Framework (TCF) did not provide any
out-of-the-box support for using Groovy scripts as path-based resource
locations when loading an application context for tests.

This commit addresses this issue by introducing first-class support for
using Groovy scripts to load the ApplicationContext for integration
tests managed by the TCF. Specifically, the following changes have been
made in the TCF to support Groovy scripts.

 - Introduced getResourceSuffixes() in AbstractContextLoader in order
   to support multiple resource suffixes in the default detection
   process. This feature is used by the new Groovy/Xml context loaders.

 - Introduced GenericGroovyXmlContextLoader and
   GenericGroovyXmlWebContextLoader which support both Groovy scripts
   and XML config files for loading bean definitions. Furthermore,
   these loaders support "-context.xml" and "Context.groovy" as
   resource suffixes when detecting defaults. Note that a default XML
   config file will be detected before a default Groovy script.

 - DelegatingSmartContextLoader and WebDelegatingSmartContextLoader now
   use reflection to choose between using GenericGroovyXmlContextLoader
   and GenericGroovyXmlWebContextLoader vs. GenericXmlContextLoader and
   GenericXmlWebContextLoader as their XML loaders, depending on
   whether Groovy is present in the classpath.

 - Groovy scripts can be configured via the 'locations' or 'value'
   attributes of @ContextConfiguration and can be mixed seamlessly with
   XML config files.

Issue: SPR-11233
2014-07-24 17:04:40 +02:00
Juergen Hoeller d4fe732f46 Typo fixes and cleanup of outdated Java 5 references
Issue: SPR-12020
2014-07-24 17:00:44 +02:00
Rossen Stoyanchev 88d8dff3ac Upgrade to Tyrus 1.7
This change provides WebSocket support for the upcoming Glassfish 4.0.1
release while at the same dropping support for Glassfish 4.0 due to
incompatible changes.

Issue: SPR-11094
2014-07-24 09:58:22 -04:00
Rossen Stoyanchev afb56681ac Fix minor issue in StandardToWebSocketExtensionAdapter
This change ensures proper initialization of a WebSocketExtension
from a JSR-356 Extension.

This is in preparation for SPR-11094
2014-07-24 09:58:22 -04:00
Rossen Stoyanchev e74ac06733 Filter WebSocket extensions
Before this change the DefaultHandshakeHandler by default passed the
list of requested WebSocket extensions as-is relying on the WebSocket
engine to remove those not supported.

This change ensures that WebSocket extensions not supported by the
runtime are proactively removed instead.

This change is preparation for SPR-11094.
2014-07-24 09:58:22 -04:00
Juergen Hoeller 78484129f5 ConcurrentMapCacheManager recreates caches in case of setAllowNullValues change
Issue: SPR-12026
2014-07-24 15:06:36 +02:00
Sam Brannen b75eb60943 Clean up Spring MVC Test Framework code
- Formatting
 - Suppress warnings
 - Remove unused imports
2014-07-24 14:12:14 +02:00
Juergen Hoeller fe05e7e5b3 Self reference detection defensively handles null bean names
Issue: SPR-12018
2014-07-24 12:56:10 +02:00
Juergen Hoeller 0ce90f1d16 TypeDescriptor's getResolvableType() and getSource() are @since 4.0 2014-07-24 12:13:37 +02:00
Juergen Hoeller 496492b2d8 Autowiring rejects self references to a factory method on the same bean as well
Issue: SPR-12018
2014-07-24 12:13:25 +02:00
Stephane Nicoll df3b1f2565 Revert ea16ce0
The latest build preview of jdk 1.8.0_20 does not exhibit the problem
anymore.

Issue: SPR-11973
2014-07-23 19:40:56 +02:00
Stephane Nicoll 8d8d47fb4e Fix compiler warning 2014-07-23 19:24:52 +02:00
Stephane Nicoll e849bc3e90 Fix potential NPE
This commit fixes a potential NPE when determining the priority of a
bean instance in case multiple candidates exist and no bean was marked
as @Primary

Issue: SPR-12024
2014-07-23 18:51:09 +02:00
Stephane Nicoll 7ce709309d Switch to full gradle version 2014-07-23 17:30:48 +02:00
Stephane Nicoll db5cebc681 Fix typo 2014-07-23 17:30:15 +02:00
Stephane Nicoll d9e0b292ab polishing 2014-07-23 17:08:19 +02:00
Rossen Stoyanchev 5be1ff281c Polish 2014-07-22 16:56:59 -04:00
Brian Clozel b56703eadc Add Google Protobuf support with a MessageConverter
This change adds a new HttpMessageConverter supporting
Google protocol buffers (aka Protobuf).
This message converter supports the following media types:
* application/json
* application/xml
* text/plain
* text/html (output only)
* and by default application/x-protobuf

Note, in order to generate Proto Message classes, the protoc binary
must be available on your system.

Issue: SPR-5807/SPR-6259
2014-07-22 16:56:59 -04:00
Rossen Stoyanchev 105ea196e4 Polish 2014-07-22 15:40:49 -04:00
Rob Winch 1b53882f55 Generate bracketless tag id in Velocity forms
Before this change if Velocity Spring form macro was bound to a path
which contains square brackets, those brackets would also appear in id
of generated tag, making the id invalid.

As of this fix all Velocity Spring form macros generate tag with id
that does not contain square brackets.

Issue: SPR-5172
2014-07-22 15:33:23 -04:00
Rossen Stoyanchev 73267d7523 Ensure Undertow 1.1 compatibility
Issue: SPR-11914
2014-07-22 15:28:40 -04:00
Rossen Stoyanchev 71b63cd972 Update MockMvcConfigurer support
This is a follow-up on the commit introducing MockMvcConfigurer:
https://github.com/spring-projects/spring-framework/commit/c2b0fac852dd9f865699fb374ad78543767fec05

This commit refines the MockMvcConfigurer contract to use (the new)
ConfigurableMockMvcBuilder hence not requiring downcasting to
AbstractMockMvcBuilder.

The same also no longer passes the "default" RequestBuilder which would
also require a downcast, but rather allows a RequestPostProcessor to be
returned so that a 3rd party framework or application can modify any
property of every performed MockHttpServletRequest.

To make this possible the new SmartRequestBuilder interface separates
request building from request post processing while the new
ConfigurableSmartRequestBuilder allows adding a RequestPostProcessor
to a MockMvcBuilder.

Issue: SPR-11497
2014-07-22 13:41:22 -04:00
Rossen Stoyanchev 988499f7dc Remove awaitCloseStatus in SockJS integration tests
A CloseStatus may not be received if the connection is closed while
the client is between XHR polling requests.
2014-07-22 13:40:02 -04:00
Rossen Stoyanchev 6c4aff4c9d Polish 2014-07-22 11:21:39 -04:00
Sebastien Deleuze 9092102a83 Fix issue with @EnableWebMvc + existing ViewResolver
Issue: SPR-12013
2014-07-22 11:21:39 -04:00
Rossen Stoyanchev e41b14449e Restore method signature in ViewControllerRegistration
Issue: SPR-12016
2014-07-22 11:21:39 -04:00
Juergen Hoeller f0bcb773f9 ResourceHttpMessageConverter does not call contentLength() on InputStreamResource
Issue: SPR-12017
2014-07-22 17:00:57 +02:00
Juergen Hoeller c082220b34 AbstractMessageListenerContainer calls "Session.recover()" in case of rollback attempt on non-transacted Session
Issue: SPR-12015
2014-07-22 16:50:41 +02:00
Juergen Hoeller 5e2fbe7355 AbstractApplicationContext logs initialization exception before trying to cancel refresh attempt
Issue: SPR-12010
2014-07-22 16:33:46 +02:00
Juergen Hoeller 4b09fcc67c Revised exitTypeDescriptor handling to avoid NPEs
Issue: SPR-12014
2014-07-22 16:24:39 +02:00
Sam Brannen 3013558e3d Update Javadoc for SqlScriptsTestExecutionListener 2014-07-20 22:03:31 +02:00
Stephane Nicoll dd2bf28a4f Add missing XSDs
Commit b676c41805 should have added the 4.1 XSDs for the jdbc and oxm
modules.

Issue: SPR-12011
2014-07-19 18:42:05 +02:00
Sam Brannen a10537447e Polish Javadoc for TestTransaction 2014-07-19 10:59:10 +02:00
Sam Brannen 7d2ef69c6d Polish Javadoc for TransactionalTestExecutionListener 2014-07-19 10:52:46 +02:00
Sam Brannen 97dad7e2ea Polish Javadoc for TestExecutionListener 2014-07-19 10:45:53 +02:00
Sam Brannen b16e6cc44e Improve documentation for tx support in the TCF
Overhauled class-level Javadoc for TransactionalTestExecutionListener:

 - Reorganized content.
 - Added headings to paragraphs.
 - Documented TestTransaction support.

Issue: SPR-11941
2014-07-19 10:45:27 +02:00
Sam Brannen 526b4b647f Improve Javadoc for TransactionalTestExecutionListener 2014-07-18 17:50:12 +02:00
Juergen Hoeller da324e3984 Latest dependency updates (Groovy 2.3.4, Hibernate 4.3.6) 2014-07-18 17:22:20 +02:00
Juergen Hoeller 9d6c38bd54 Consistent bracket alignment 2014-07-18 17:21:58 +02:00
Juergen Hoeller 188e58c46a Fixed javadoc links 2014-07-18 17:21:44 +02:00
Juergen Hoeller 3be190df6a Consistent removal of JMS 1.0.2 references 2014-07-18 17:21:31 +02:00
Juergen Hoeller a9100c427c Revision of JMS annotated endpoint support, plus support for JMS 2.0's shared subscriptions
Issue: SPR-9882
Issue: SPR-11969
2014-07-18 17:21:21 +02:00
Sam Brannen 3e5946db37 Polish log statements for TransactionContext 2014-07-18 17:09:53 +02:00
Sam Brannen 7808996d71 Polishing 2014-07-18 17:02:45 +02:00
Rossen Stoyanchev 829d204663 MVC config doc updates 2014-07-18 09:18:34 -04:00
Sebastien Deleuze 7412d43acd Update documentation with MVC config view resolution examples
Issue: SPR-11989
2014-07-18 09:18:34 -04:00
Sam Brannen 682e8fb3ad Reduce code duplication in MergedSqlConfig 2014-07-18 14:39:23 +02:00
Stephane Nicoll dd713c70aa Fix typo 2014-07-18 11:50:49 +02:00
Rossen Stoyanchev 90f8e4b71f Add @Ignored temporarily to Jetty SockJS tests 2014-07-18 00:45:14 -04:00
Rossen Stoyanchev 29c6c9a375 Add guidance on URI vars and suffix pattern matching
Issue: SPR-11728
2014-07-18 00:41:38 -04:00
Rossen Stoyanchev 27153b2982 Polish interceptor registration MVC config 2014-07-17 23:47:56 -04:00
Marten Deinum 84c11a5cc7 Allow interceptors with excludePathPattern only
Issue: SPR-11130
2014-07-17 23:36:41 -04:00
Sebastien Deleuze 52f1be7ade Add "-configurer" suffix to view configurer element names
Issue: SPR-7093
2014-07-17 23:18:41 -04:00
Rossen Stoyanchev 1ad22b922f Enhance view controller MVC config
This change adds support for configuring redirect view controllers and
also status controllers to the MVC Java config and the MVC namespace.

Issue: SPR-11543
2014-07-17 23:04:08 -04:00
Rossen Stoyanchev 0bbb7704b5 Add propagateQueryParams property to RedirectView
This change makes it possible to configure RedirectView such that the
query string of the current request is added to the target URL.

This change is preparation for SPR-11543.
2014-07-17 23:04:08 -04:00
Rossen Stoyanchev 0e2c5ee96c Enhance ParameterizableViewController
This change two new capabilities to ParameterizableViewController:

 - configure a View instance (in addition to view name)
 - configure response status code

The status code may be useful to send a 404 while also writing to the
body using a view.

The status code may also be used to override the redirect status code
of RedirectView. Even today it's possible to configure a "redirect:"
prefixed view name but the status code could not be selected. When a
3xx status is set, the code is passed on to the RedirectView while the
view name is automatically prefixed with "redirect:" (if not already).

For full control over RedirectView it is now also possible to
parameterize the controller with a View instance.

As one more possible resulting variation, given status 204 and no view
the request is considered handled (controller returns null).

This change is preparation for SPR-11543.
2014-07-17 23:04:08 -04:00
Rossen Stoyanchev dfcc1d7e8c Add default ViewResolver to MVC Java config
Since the MVC Java config always registers a ViewResolver (composite)
bean, at a very minimum we must add an InternalResourceViewResolver
consistent with default DispatcherServlet configuration and by
extension with the MVC namespace which falls back on DispatcherServlet
implicity if no <view-resolvers> element is present.

Issue: SPR-7093
2014-07-17 23:04:08 -04:00
Rossen Stoyanchev 7f7fd7d311 Polish view controller MVC config 2014-07-17 23:04:08 -04:00
Rossen Stoyanchev 61ec8fd236 Polish 2014-07-17 23:04:08 -04:00
Sam Brannen 2e75adb04c Improve transaction management for @Sql scripts
Prior to this commit, the support for SQL script execution via @Sql
provided an algorithm for looking up a required
PlatformTransactionManager to use to drive transactions. However, a
transaction manager is not actually required for all testing scenarios.

This commit improves the transaction management support for @Sql so
that SQL scripts can be executed without a transaction if a transaction
manger is not present in the ApplicationContext. The updated algorithm
now supports the following use cases.

 - If a transaction manager and data source are both present (i.e.,
   explicitly specified via the transactionManager and dataSource
   attributes of @SqlConfig or implicitly discovered in the
   ApplicationContext based on conventions), both will be used.

 - If a transaction manager is not explicitly specified and not
   implicitly discovered based on conventions, SQL scripts will be
   executed without a transaction but requiring the presence of a data
   source. If a data source is not present, an exception will be thrown.

 - If a data source is not explicitly specified and not implicitly
   discovered based on conventions, an attempt will be made to retrieve
   it by using reflection to invoke a public method named
   getDataSource() on the transaction manager. If this attempt fails,
   an exception will be thrown.

 - If a data source can be retrieved from the resolved transaction
   manager using reflection, an exception will be thrown if the
   resolved data source is not the data source associated with the
   resolved transaction manager. This helps to avoid possibly
   unintended configuration errors.

 - If @SqlConfig.transactionMode is set to ISOLATED, an exception will
   be thrown if a transaction manager is not present.

Issue: SPR-11911
2014-07-18 02:59:03 +02:00
Rossen Stoyanchev 44e4569150 Polish 2014-07-17 09:24:45 -04:00
Sebastien Deleuze 59f39706dc Fix default target destination when using "." as path separator
Issue: SPR-11660
2014-07-17 09:01:45 -04:00
Rossen Stoyanchev e8d8c3390a Increase timeout in integration test 2014-07-16 22:05:50 -04:00
Rossen Stoyanchev d23b0fec0c Polish 2014-07-16 21:43:30 -04:00
Sebastien Deleuze 80f4ea13c7 Add Groovy markup templating support to the MVC config
Issue: SPR-11998
2014-07-16 21:43:30 -04:00
Rossen Stoyanchev b18ed6b724 Remove erroneously commited code 2014-07-16 20:49:12 -04:00
Sam Brannen dfcb2a2875 Fix test broken by previous refactoring 2014-07-16 21:51:49 +02:00
Sam Brannen 91c9bad12e Rename test classes previously related only to ContextLoaderUtils 2014-07-16 21:49:29 +02:00
Sam Brannen c290839914 Investigate claim on SO regarding ctx cache in TCF
This commit introduces a test that investigates a claim made on Stack
Overflow regarding context caching in the TestContext Framework (TCF).
2014-07-16 21:34:43 +02:00
Juergen Hoeller 48fea0bafa ClassUtils.isCacheSafe defensively catches SecurityException (for Google App Engine compatibility)
Issue: SPR-12002
2014-07-16 20:33:36 +02:00
Juergen Hoeller a53987ace1 Renamed JCacheEhCacheTest to JCacheEhCacheTests 2014-07-16 20:27:49 +02:00
Sam Brannen da5b0b97d3 Update Javadoc in SqlScriptsTestExecutionListener 2014-07-16 18:19:49 +02:00
Sam Brannen 5cb046da3d Update Javadoc in SqlScriptsTestExecutionListener 2014-07-16 18:16:46 +02:00
Sam Brannen a488515097 Simplify logic in MergedSqlConfig 2014-07-16 18:12:59 +02:00
Sam Brannen 6d6f008faa Polish Javadoc for MergedSqlConfig 2014-07-16 17:54:23 +02:00
Sam Brannen c1a2f5efa4 Remove unnecessary warning suppression in ModelResultMatchers 2014-07-16 17:44:44 +02:00
Stephane Nicoll fac0599aef Add cache abstraction tests using JSR-107
This commit adds an extra test implementation that runs the cache
abstraction tests using a JSR-107 cache manager. This further covers
JCacheCacheManager.

The actual JSR-107 implementation is ehcache-jcache that requires at
least ehcache 2.8.3. Since the ehcache-core artifact is no longer a
public artifact, this commit switches to the full ehcache library,
that is net.sf.ehcache:ehcache
2014-07-16 17:35:34 +02:00
Stephane Nicoll 35226695eb Rename default to testCache in Cache related tests
This simply renames the default cache from "default" to "testCache" as
this might be a reserved names for third party cache providers.
2014-07-16 17:25:27 +02:00
Sam Brannen bbfc31b11f Merge from sbrannen/SPR-11896
* SPR-11896:
  Introduce @SqlConfig for common SQL script config
2014-07-16 17:23:23 +02:00
Sam Brannen 628806e85b Introduce @SqlConfig for common SQL script config
Prior to this commit, @Sql provided attributes for configuring the
syntax of the referenced SQL scripts as well as exception handling and
transaction behavior; however, such configuration could not be reused
across @Sql declarations thus requiring developers to copy-and-paste
common configuration and resulting in unnecessary code duplication.

This commit addresses this issue by introducing a new @SqlConfig
annotation that can be used to declare common, global configuration for
SQL scripts that can be reused within a test class hierarchy.

 - Introduced top-level @SqlConfig annotation and extracted
   common configuration attributes from @Sql.

 - @SqlConfig can be used at the class level for common, global config
   or via the new 'config' attribute of @Sql for local config.

 - Introduced MergedSqlConfig as a holder for the merged values from
   local and global @SqlConfig instances. MergedSqlConfig also contains
   the logic for overriding global configuration with local
   configuration.

 - Refactored all attributes of @SqlConfig to be either of type String
   or custom enums in order to support overriding. Empty Strings or
   DEFAULT enum values imply the use of a default or inherited value.

Issue: SPR-11896
2014-07-16 17:21:15 +02:00
Juergen Hoeller 4e25a14f6c Merge pull request #571 from sdeleuze/SPR-11820
Make ListenableFuture compliant with Java 8 lambda
2014-07-16 16:41:41 +02:00
Sebastien Deleuze 86e8bdab6b Make ListenableFuture compliant with Java 8 lambda
Make it possible to use a ListenableFuture with Java 8
lambda expressions, using a syntax like
listenableFuture.addCallback(() -> ..., () -> ...);

Issue: SPR-11820
2014-07-16 16:06:24 +02:00
Juergen Hoeller b1b7f8ab1f EhCacheFactoryBean uses reflection to call setStatisticsEnabled method (which allows for building against EhCache 2.7+)
Issue: SPR-11262
2014-07-16 16:02:03 +02:00
Juergen Hoeller 980f971236 SelectedValueComparator defensively handles null values in exhaustiveCompare
Issue: SPR-12001
2014-07-16 15:11:19 +02:00
Juergen Hoeller bad74dc836 Revision of SpelCompiler support, resolving a subpackage cycle through moving CodeFlow and CompilablePropertyAccessor to the main spel package
Also contains explicit ClassLoader management, passed through StandardBeanExpressionResolver and SpelParserConfiguration to SpelCompiler lookup.

Issue: SPR-10943
2014-07-16 14:41:46 +02:00
Juergen Hoeller 8fb592712c Merge pull request #593 from poutsma/SPR-12000
StaxStreamXMLReader ignores significant whitespace
2014-07-16 13:37:45 +02:00
Arjen Poutsma d6950d8add StaxStreamXMLReader ignores significant whitespace
The StaxStreamXMLReader no longer handles all whitespace as ignorable
whitespace.

Issue: SPR-12000
2014-07-16 13:16:20 +02:00
Rossen Stoyanchev 89b202029a Fix failing test 2014-07-15 22:59:25 -04:00
Rossen Stoyanchev c2b0fac852 Add extension point to pre-configure a MockMvcBuilder
Issue: SPR-11497
2014-07-15 22:59:25 -04:00
Rossen Stoyanchev d86e4cf203 Re-obtain port on every WebSocket integration test
This change adds a WebSocketTestServer setup method that initializes
the server and obtains a new port.

In turn AbstractWebSocketIntegrationTests invokes the new method from
its setup method thus ensuring a new port is used on every test.
2014-07-15 16:53:38 -04:00
Rossen Stoyanchev ea7ae8949e Use regex comparison in WebSocket stats test
The output includes thread pool information which appears to not vary
enough to cause failures locally but does so on the build server.
2014-07-15 16:53:38 -04:00
Juergen Hoeller c18b6bf0b7 NestedIOException can use IOException constructor with cause argument on JDK 6+ 2014-07-15 22:05:45 +02:00
Juergen Hoeller bbf5800831 ConfigurationClassParser processes late-arriving DeferredImportSelectors as regular import selectors
Also contains refined exception handling, treating regular class loading and ASM-based loading consistently in terms of exception wrapping, and always mentioning the current configuration class in all exception messages.

Issue: SPR-11997
2014-07-15 22:04:52 +02:00
Rossen Stoyanchev 10a4c2cd81 Remove FreeMarker/Velocity/TilesConfigurer MVC config
After some further discussion:

The MVC config simplifies ViewResolver configuration especially where
content negotiation view resolution is involved.

The configuration of the underlying view technology however is kept
completely separate. In the case of the MVC namespace, dedicated
top-level freemarker, velocity, and tiles namespace elements are
provided. In the case of the MVC Java config, applications simply
declare FreeMarkerConfigurer, VelocityConfigurer, or TilesConfigurer
beans respectively.

Issue: SPR-7093
2014-07-15 14:27:01 -04:00
Rossen Stoyanchev be4d73e92d Merge HandshakeInterceptor-related config updates 2014-07-15 13:32:08 -04:00
Rossen Stoyanchev 4dd5c274a0 Add missing HandshakeInterceptor for STOMP endpoints
Issue: SPR-11845
2014-07-15 13:31:30 -04:00
Rossen Stoyanchev 6d6cc0ecec Polish WebSocket Java config 2014-07-15 13:31:30 -04:00
Rossen Stoyanchev 85c175059a Add missing handshake-interceptor namespace support
Issue: SPR-11845
2014-07-15 13:31:29 -04:00
Rossen Stoyanchev 61e77eeb61 Fix white spaces 2014-07-15 13:31:29 -04:00
Rossen Stoyanchev 6557800f97 Polish WebSocket namespace 2014-07-15 13:31:29 -04:00
Juergen Hoeller 2665d56209 Polishing 2014-07-15 15:13:35 +02:00
Juergen Hoeller a4968b9015 DefaultListableBeanFactory's registerBeanDefinition only calls resetBeanDefinition in case of pre-existing bean definition or pre-existing singleton instance
Issue: SPR-8318
2014-07-15 15:05:13 +02:00
Rossen Stoyanchev ab2526a586 Update support for using "." as path separator
Issue: SPR-11660
2014-07-14 18:49:25 -04:00
Sebastien Deleuze 928a466b5d Allow to customize separator for messaging destinations
In order to be able to use separators like "." (used by default
by most broker relays) instead of "/" for destination patterns
handling, the PathMatcher used in spring-messaging can now
be customized easily thanks to XML websocket namespace
or JavaConfig.

AntPathMatcher has been updated in order to use the configured path
separator instead of an hardcoded "/" for path concatenation.
Extension handling is now disabled when the "." separator is configured.

Issue: SPR-11660
2014-07-14 18:49:24 -04:00
Stephane Nicoll b676c41805 Add missing 4.1 XSDs
This commit adds the missing 4.1 XSDs for the following components:

* spring-aop
* spring-context
* spring-jee
* spring-lang
* spring-tx
* spring-util

These are strictly identical to the definition of the 4.0 XSDs.

Issue: SPR-11990
2014-07-14 17:59:54 +02:00
Stephane Nicoll 29bdbceaa2 Customize concurrency at listener level
Prior to this commit, customizing the concurrency to use fo a given JMS
listener involved to define it in a specific listener-container. As
this is quite restrictive, users may stop using the XML namespace
support altogether to fallback on regular abstract bean definition for
the container.

This commit adds a concurrency attribute to the jms and jca listener
element as well as on the @JmsListener annotation. If the value is set,
it takes precedence; otherwise the value provided by the factory is
used.

Issue: SPR-11988
2014-07-14 15:49:31 +02:00
Rossen Stoyanchev 5e1a5c8d51 Update view-resolver namespace
Issue: SPR-7093
2014-07-13 22:53:29 -04:00
Rossen Stoyanchev f54cee47b0 Update ViewResolver registration classes
Following the separation of FreeMarker/Velocity/TilesConfigurer-related
configuration via separate interface, simplify and streamline the
view registration helper classes which no longer have much difference
(most are UrlBasedViewResolver's).

Updates to Javadoc and tests.

Issue: SPR-7093
2014-07-13 22:53:23 -04:00
Rossen Stoyanchev 5bc793768c Introduce Freemarker/Velocity/TilesWebMvcConfigurer
This change improves the support for auto-registration of FreeMarker,
Velocity, and Tiles configuration.

The configuration is now conditional not only based on the classpath
but also based on whether a FreeMarkerConfigurer for example is already
present in the configuration.

This change also introduces FreeMarker~, Velocity~, and
TilesWebMvcConfigurer interfaces for customizing each view technology.

The WebMvcConfigurer can still be used to configure all view resolvers
centrally (including FreeMarker, Velocity, and Tiles) without some
default conifguration, i.e. without the need to use the new
~WebMvcConfigurer interfaces until customizations are required.

Issue: SPR-7093
2014-07-12 17:23:47 -04:00
Sebastien Deleuze cc7e8f5558 Support Java and MVC namespace view resolution config
This commit improves and completes the initial MVC namespace
view resolution implementation. ContentNegotiatingViewResolver
registration is now also supported.

Java Config view resolution support has been added.
FreeMarker, Velocity and Tiles view configurers are registered
depending on the classpath thanks to an ImportSelector.

For both, a default configuration is provided and documented.

Issue: SPR-7093
2014-07-12 17:23:47 -04:00
Sebastien Deleuze a26b1ef8d9 Make Tiles 3 the default implementation in spring-webmvc
Move spring-webmvc-tiles3 content to spring-webmvc, and
create a spring-webmvc-tiles2 module with Tiles 2 support.

Its allows View Resolution to configure Tiles 3 instead of Tiles 2.

Issue: SPR-7093
2014-07-12 17:23:47 -04:00
Siva Prasad Valluru 92402e7715 Add initial support for MVC namespace view resolution config
<mvc:view-resolution /> now allows to configure easily
view resolvers.

Issue: SPR-7093
2014-07-12 17:23:47 -04:00
Rossen Stoyanchev b318880661 Ensure StompEncoder never writes content-length twice
Issue: SPR-11984
2014-07-11 10:09:48 -04:00
Lea Farmer 85cdb9196e Added field error code matching to MockMvc
This change adds a method within the ModelResultMatcher that will allow
a user to assert whether the returned Model has an attribute with a
field that has a specific error associated with it.

Issue: SPR-11971
2014-07-11 09:36:09 -04:00
Rossen Stoyanchev 2ba8fd7e59 Polish 2014-07-11 09:17:02 -04:00
Brian Clozel 2c77de10dd Fix X-Forwarded-* behavior in ServletUriComponentsBuilder
When using ServletUriComponentsBuilder.fromRequest, this change
makes sure that:
* the default port is used when the "X-Forwarded-Host" header is set
and no port is defined in that header value
* to use the scheme defined in the "X-Forwarded-Proto" header if set

Issue: SPR-11872
2014-07-11 00:55:17 +02:00
Rossen Stoyanchev 2f371e5aeb Add ExecutorChannelInterceptor
Issue: SPR-11968
2014-07-10 16:55:34 -04:00
Rossen Stoyanchev f8e4547228 Fix test class name and protect debug log message 2014-07-10 16:55:34 -04:00
Rossen Stoyanchev 670c216d38 Change converter ordering in message broker config
Issue: SPR-11961
2014-07-10 12:57:00 -04:00
Rossen Stoyanchev 2ebc921545 Add Lifecycle to SockJsClient and Transport types
Issue: SPR-10797
2014-07-10 12:56:59 -04:00
Rossen Stoyanchev d73c1d5693 Update WebSocket client SmartLifecycle support
Before this change WebSocketConnectionManager delegated SmartLifecycle
methods to the client instance it contained. After this change
WebSocketClient implementations are expected to implement Lifecycle
(rather than SmartLifecycle).

The need for this is even more evident with SockJsClient, which is a
WebSocketClient implementation and contains a WebSocketTransport that
in turn contains the actual WebSocketClient. In this case
WebSocketConnectionManager as the top level container is the obvious
place to configure autostartup while Lifecycle events can be
propagated all the way down to the root WebSocketClient.
2014-07-10 12:56:59 -04:00
Andy Clement 6937eea48a Merge pull request #554 from aclement/SPR-10943
Add a compiler for SpEL
2014-07-10 17:52:34 +01:00
Andy Clement 2eeb2e9235 Add a compiler for SpEL
With these changes an optional compiler is added for SpEL
expressions. The compiler is off by default but can be enabled
via the SpEL parser configuration object or system property
(when SpEL is embedded and parser configuration is not possible).
Not all expressions are currently handled but the common
cases are and it is an extensible compilation framework.

Issue: SPR-10943
2014-07-10 17:49:05 +01:00
Juergen Hoeller e254b558f0 Polishing 2014-07-10 18:38:07 +02:00
Brian Clozel bf7dac5fc8 Rename AppCacheResourceTransformer
This change renames AppCacheResourceTransformer to
AppCacheManifestTransfomer, in order to avoid confusion between this
transformer and the CacheResourceTransformer (which caches
transformations done by the chain to save CPU/memory at runtime).

Issue: SPR-11964
2014-07-10 18:17:48 +02:00
Brian Clozel f11815c960 Add AppCacheResourceTransformer
This change adds a new ResourceTransformer that helps handling resources
within HTML5 AppCache manifests for HTML5 offline application.

This transformer:
* modifies links to match the public URL paths
* appends a comment in the manifest, containing a Hash (e.g. "# Hash:
9de0f09ed7caf84e885f1f0f11c7e326")

See http://www.whatwg.org/specs/web-apps/current-work/multipage/offline.html#offline
for more details on HTML5 offline apps and appcache manifests.

Here is a WebConfig example:

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {

    AppCacheResourceTransformer appCacheTransformer =
          new AppCacheResourceTransformer();

      registry.addResourceHandler("/**")
          .addResourceLocations("classpath:static/")
          .setResourceResolvers(...)
          .setResourceTransformers(..., appCacheTransformer);
  }

Issue: SPR-11964
2014-07-10 16:55:25 +02:00
Juergen Hoeller 2bd6e24b65 MBeanExporter implements newly introduced SmartInitializingSingleton callback interface
Issue: SPR-8045
2014-07-10 15:48:36 +02:00
Juergen Hoeller 29f6f3d7e7 Test ASM5's bytecode parsing of INVOKESPECIAL/STATIC on interfaces
Issue: SPR-11979
2014-07-10 15:46:18 +02:00
Rossen Stoyanchev eaad0a0f52 Add after completion callbacks to ChannelInterceptor
Issue: SPR-11966
2014-07-09 18:57:11 -04:00
Juergen Hoeller 6f062581a6 Removed unused import (which indicated a package cycle) 2014-07-10 00:36:54 +02:00
Juergen Hoeller 0d4ff1adbf Polishing 2014-07-09 22:25:35 +02:00
Juergen Hoeller fa4f51c5d0 Subpackage rearrangements within 4.1's jms-messaging alignment
Issue: SPR-10873
2014-07-09 22:19:14 +02:00
Juergen Hoeller 1fadd1c954 BackOff abstraction lives in util.backoff subpackage now
Issue: SPR-11746
2014-07-09 21:53:32 +02:00
Juergen Hoeller bf9ccc8138 ExceptionTypeFilter and InstanceFilter live in util package itself now
Issue: SPR-9616
2014-07-09 21:43:19 +02:00
Juergen Hoeller 3c726aa6c1 Polishing 2014-07-09 21:24:59 +02:00
Juergen Hoeller 777085bbfc MappingJackson2JsonView/MessageConverter calls non-deprecated Jackson 2.1+ createGenerator API
Also removing workaround for https://github.com/FasterXML/jackson-databind/issues/12 (fixed in 2.1+)

Issue: SPR-11262
2014-07-09 21:23:09 +02:00
Juergen Hoeller a6a86b8025 DefaultBeanDefinitionDocumentReader evaluates profile attribute only in beans namespace
Issue: SPR-11975
2014-07-09 21:17:43 +02:00
Rossen Stoyanchev fa33ed4b4b Support <ref> for arg resolvers return value handlers
Issue: SPR-11927
2014-07-09 15:11:20 -04:00
Rossen Stoyanchev 0d5c5a3d33 Fix white space issue 2014-07-09 15:11:19 -04:00
Agim Emruli 19760f9eb9 Add support to reference external HandlerMethodArgumentResolver beans which might contain already configured instances (e.g. through a 3rd-party namespace handler).
Users can not mix and match between "inner bean" argument resolver and "external bean" argument resolver. This commit only focuses only on argument-resolver, while the support could be extended to return value handlers as well.

 Issue: SPR-11927
2014-07-09 15:11:19 -04:00
Brian Clozel a4484bb767 Fix UriComponentsBuilder.fromUriString parsing error
This commit fixes cases where part of the URI was mistaken for the
userinfo when:
* the URI did not contain any path
* the query string contained the "@"

Issue: SPR-11964
2014-07-09 16:58:11 +02:00
Rossen Stoyanchev bc62d63fdd Fix left-over merge issue from previous commit 2014-07-09 00:46:06 -04:00
Rossen Stoyanchev 48236be4a2 STOMP and WebSocket messaging related logging updates
This change removes most logging at INFO level and also ensures the
amount of information logged at DEBUG level is useful, brief, and
not duplicated.

Also added is custom logging for STOMP frames to ensure very readable
and consise output.

Issue: SPR-11934
2014-07-09 00:39:59 -04:00
Rossen Stoyanchev ab4864da2a Add STOMP/WebSocket stats collection
This change adds collection of stats in key infrastructure components
of the WebSocket message broker config setup and exposes the gathered
information for logging and viewing (e.g. via JMX).

WebSocketMessageBrokerStats is a single class that assembles all
gathered information and by default logs it once every 15 minutes.
Application can also easily expose to JMX through an MBeanExporter.

A new section in the reference documentation provides a summary of
the available information.

Issue: SPR-11739
2014-07-09 00:39:47 -04:00
Stephane Nicoll b78b2e9a03 Fix priority semantic
Commit 5fe8f52 introduced a support for @Priority as an alternative to
@Primary but it broke the semantic of the priority value. This commit
fixes this inconsistency.

As for @Order, the lowest value means the highest priority so if
several beans are candidates for injection, the one having the lowest
value will be used.

Issue: SPR-10548
2014-07-08 10:26:03 +02:00
Phillip Webb ea16ce0aa0 Disable processing javac warnings
Set `-Xlint:-processing` to disable annotation processing warnings.
Required when building on JDK 1.8.0_20-b05 due to JDK-8039469.

Issue: SPR-11973
2014-07-07 19:21:18 -07:00
Juergen Hoeller 01264dc673 Polishing 2014-07-08 00:59:31 +02:00
Juergen Hoeller 1115374188 MBeanExporter should not implement SmartLifecycle but rather receive a ContextRefreshedEvent-like callback
This commit removes the immediate package dependency cycle between the context and jmx packages. A specific callback arrangement will follow in time for 4.1 RC1; at this point, it's temporarily back to registration kicked off by afterPropertiesSet again.

Issue: SPR-8045
2014-07-07 23:50:27 +02:00
Juergen Hoeller b559f15a00 Polishing 2014-07-07 21:45:40 +02:00
Juergen Hoeller 387da221c3 Polishing
Issue: SPR-11963
2014-07-07 19:41:39 +02:00
Brian Clozel 6bcb48f95d Polish SPR-11963 2014-07-07 17:34:08 +02:00
Brian Clozel e549103ca0 Remove Jackson dependency from TransportHandler hierarchy
Prior to this change, AbstractHttpReceivingTransportHandler had a direct
dependency on a Jacckson Exception (checking that exception in a catch
clause). This can cause issues for applications that don't have that
dependency.

This commit removes that direct dependency, still logging the
appropriate log messages using a parent exception (IOException) and
reflection.

Issue: SPR-11963
2014-07-07 17:29:32 +02:00
Juergen Hoeller 55c351523d Polishing 2014-07-07 16:28:48 +02:00
Juergen Hoeller 4082274630 SQLStateSQLExceptionTranslator checks exception class name for timeout indication before resorting to UncategorizedSQLException
Issue: SPR-11959
2014-07-07 16:26:25 +02:00
Juergen Hoeller 1222ca38fb RestTemplate accepts getMessageConverters() List on setMessageConverters again
Issue: SPR-11962
2014-07-07 14:33:48 +02:00
Stephane Nicoll e20ac27fb4 Fix mutually exclusive use of CachePut and Cacheable
Commit eea230f introduced a regression by adding a support for the
"result" variable in SpEL expression for @CachePut. As such expressions
cannot be evaluated upfront anymore, any method that contains both
@Cacheable and @CachePut annotations are always executed even when
their conditions are mutually exclusive.

This is an example of such mutual exclusion

@Cacheable(condition = "#p1", key = "#p0")
@CachePut(condition = "!#p1", key = "#p0")
public Object getFooById(Object id, boolean flag) { ... }

This commit updates CacheEvaluationContext to define a set of
unavailable variables. When such variable is accessed for a given
expression, an exception is thrown. This is used to restore the
evaluation of the @CachePut condition upfront by registering "result"
as an unavailable variable.

If all @CachePut operations have been excluded by this upfront check,
the @Cacheable operation is processed as it was before. Such upfront
check restore the behavior prior to eea230f.

Issue: SPR-11955
2014-07-07 14:18:19 +02:00
Juergen Hoeller a8848cb670 TypedValue implements equals/hashCode based on value and TypeDescriptor
Issue: SPR-11960
2014-07-07 11:29:29 +02:00
Juergen Hoeller 11532794f4 Removed outdated AspectJ comment
Issue: SPR-11957
2014-07-07 11:28:08 +02:00
Juergen Hoeller b9a70c6000 Refined CachingConnectionFactory log messages
Issue: SPR-11956
2014-07-05 08:59:11 +02:00
Juergen Hoeller 6fd0fc7005 CachedSessionInvocationHandler includes durable flag in ConsumerCacheKey
Issue: SPR-11956
2014-07-05 08:49:28 +02:00
Juergen Hoeller 6c41cc354c ConstructorResolver's exception message on null factory-bean hints at potential BeanPostProcessor involvement
Issue: SPR-11951
2014-07-04 22:30:19 +02:00
Juergen Hoeller 7a7641bd23 CachedSessionInvocationHandler properly caches createSharedConsumer results
Issue: SPR-11956
2014-07-04 22:20:19 +02:00
Stephane Nicoll 4e257243f2 Reduce PlatformTransactionManager lookups
Prior to this commit, cache operations mentioning a qualifier led to
a lookup for the same PlatformTransactionManager over and over again.
The same applied when a transactionManager bean name was specified on
the interceptor.

This commit adds a cache to store the reference of such transaction
managers. As a convenience, the default PlatformTransactionManager is
also initialized if it has not been through configuration.

Issue: SPR-11954
2014-07-04 16:45:23 +02:00
Sam Brannen 58955236ee Introduce tests for Spring Boot issue 885
This commit introduces unit tests that attempt to reproduce the problem
described in Spring Boot issue 885; however, the tests pass and
therefore do not confirm the reported problem.

See: https://github.com/spring-projects/spring-boot/issues/885
2014-07-04 16:11:28 +02:00
Juergen Hoeller b6a7957dc4 Revised Jms2MessageProducerInvocationHandler implementation with switch statement
Issue: SPR-11950
2014-07-04 16:09:06 +02:00
Stephane Nicoll c2da846732 Add support for extra MessageProducer method
This commit supports two additional methods of the MessageProducer
interface as from JMS 2.0

An integration test infrastructure is necessary to be able to test
those scenario: this is taken care of in a separated initiative.

Issue: SPR-11950
2014-07-04 14:08:56 +02:00
Juergen Hoeller 134e5a2aec Jms2MessageProducerInvocationHandler properly delegates to CachedMessageProducer.this
Issue: SPR-11949
2014-07-04 00:25:43 +02:00
Juergen Hoeller b5a5fffdfd Reference documentation covers application scope now
Issue: SPR-11944
2014-07-03 22:26:24 +02:00
Juergen Hoeller af13310da0 Refined DefaultJpaDialect's InvalidIsolationLevelException message
Issue: SPR-8325
2014-07-03 22:24:36 +02:00
Sam Brannen 0d710f197e Polishing tests in spring-test 2014-07-03 19:33:20 +02:00
Stephane Nicoll 62351e0f6e Fix wrong method signature in documentation
Issue: SPR-11945
2014-07-03 17:00:25 +02:00
Sam Brannen bdceaa481b Introduce TestNG tests for programmatic tx mgmt in the TCF
Issue: SPR-5079
2014-07-02 23:14:42 +02:00
Sam Brannen 90f0d14c8e Merge from sbrannen/SPR-5079
* SPR-5079:
  Introduce programmatic tx mgmt in the TCF
2014-07-02 22:56:44 +02:00
Sam Brannen f667e43ca2 Introduce programmatic tx mgmt in the TCF
Historically, Spring's JUnit 3.8 TestCase class hierarchy supported
programmatic transaction management of "test-managed transactions" via
the protected endTransaction() and startNewTransaction() methods in
AbstractTransactionalSpringContextTests.

The Spring TestContext Framework (TCF) was introduced in Spring 2.5 to
supersede the legacy JUnit 3.8 support classes; however, prior to this
commit the TCF has not provided support for programmatically starting
or stopping the test-managed transaction.

This commit introduces a TestTransaction class in the TCF that provides
static utility methods for programmatically interacting with
test-managed transactions. Specifically, the following features are
supported by TestTransaction and its collaborators.

 - End the current test-managed transaction.

 - Start a new test-managed transaction, using the default rollback
   semantics configured via @TransactionConfiguration and @Rollback.

 - Flag the current test-managed transaction to be committed.

 - Flag the current test-managed transaction to be rolled back.

Implementation Details:

 - TransactionContext is now a top-level, package private class.

 - The existing test transaction management logic has been extracted
   from TransactionalTestExecutionListener into TransactionContext.

 - The current TransactionContext is stored in a
   NamedInheritableThreadLocal that is managed by
   TransactionContextHolder.

 - TestTransaction defines the end-user API, interacting with the
   TransactionContextHolder behind the scenes.

 - TransactionalTestExecutionListener now delegates to
   TransactionContext completely for starting and ending transactions.

Issue: SPR-5079
2014-07-02 22:52:54 +02:00
Juergen Hoeller 526b463474 Standard use of resolvedDestinationCache Map; fixed formatting
Issue: SPR-11939
2014-07-02 20:53:52 +02:00
Rossen Stoyanchev 6d15fcc4a6 Log name of test for WebSocket integration tests
This makes it easier to trace log output on the CI server where the
output is per class.
2014-07-02 14:26:51 -04:00
Rossen Stoyanchev 59e02e63c4 Synchronize request init in AbstractHttpSockJsSession
Although unlikely in practice (but not impossible), the SockJS
integration tests write a message while the request is initializing.
This change adds synchronization around request intiailization
for the SockJS HTTP sesion.

Issue: SPR-11916
2014-07-02 13:53:23 -04:00
Juergen Hoeller 5614e257d1 Polishing
Issue: SPR-11939
2014-07-02 17:03:05 +02:00
Juergen Hoeller c84b30d4a4 CachingDestinationResolverProxy for slow target DestinationResolvers
Issue: SPR-11939
2014-07-02 17:00:26 +02:00
Brian Clozel ea4a5d4722 Add XML config support ResourceResolver & ResourceTransformer
This change adds support for configuring ResourceResolvers and
ResourceTransformers with ResourceHttpRequestHandlers.

This is an example configuration:

    <mvc:resources mapping="/resources/**" location="/">
      <mvc:resolvers>
        <bean class="org.springframework.web.servlet.resource.PathResourceResolver"/>
        <ref bean="myResourceResolver"/>
      </mvc:resolvers>
      <mvc:transformers>
        <bean class="org.springframework.web.servlet.resource.CssLinkResourceTransformer" />
      </mvc:transformers>
    </mvc:resources>

    <bean id="myResourceResolver" class="org.example.resource.MyResourceResolver"/>

Issue: SPR-10951
2014-07-02 16:47:08 +02:00
Juergen Hoeller 87cbade8be Test variant for SPR-11521 (currently @Ignored)
Issue: SPR-11521
2014-07-02 15:13:34 +02:00
Juergen Hoeller 367b0394a2 Spring's ROME support requires ROME 1.5 (com.rometools.rome) now
Issue: SPR-11893
2014-07-02 15:04:41 +02:00
Sam Brannen 32b87079cb Re-enable Hibernate EntityManagerFactory tests
HibernateEntityManagerFactoryIntegrationTests in the spring-orm module
has been disabled for quite some time due to a dependency on the
AnnotationBeanConfigurerAspect from the spring-aspects module. Since
spring-aspects depends on spring-orm, a cyclical dependency would
result if this code were re-enabled "as is".

This commit removes the dependency on AnnotationBeanConfigurerAspect in
HibernateEntityManagerFactoryIntegrationTests by deleting all test code
and configuration related to @Configurable. In addition, this commit
also deletes all SessionFactory-specific test code in
HibernateEntityManagerFactoryIntegrationTests, allowing the test class
to focus on Hibernate as a JPA provider.

Issue: SPR-11922
2014-07-02 13:10:19 +02:00
Rossen Stoyanchev 476864f3e9 Expose handled exception as request attribute
This change exposes exceptions handled in the DispatcherServlet with a
HandlerExceptionResolver as a request attribute. This is done only when
the resolver returns an empty ModelAndView indicating the exception was
resolved but not view is required (e.g. status code was set). In such
cases the exception may be useful to any handlers in an ERRPR dispatch
by the servlet container.

Issue: SPR-11686
2014-07-01 23:32:27 -04:00
Sebastien Deleuze 9e52004222 Add support for asserting JSON
Based on the JSONassert library.

Issue: SPR-10113
2014-07-01 22:47:02 -04:00
Juergen Hoeller d870b382da Defensively check for pre-resolved FactoryBean.getObject() results in circular reference scenarios
Issue: SPR-11937
2014-07-01 23:50:17 +02:00
Rossen Stoyanchev d3a87a13f7 Polish 2014-07-01 16:10:00 -04:00
Arjen Poutsma f6fbdafb6a Introduce RequestEntity and builder
This commit introduces the RequestEntity, a class similar to
ResponseEntity, but meant for HTTP requests rather than responses. The
RequestEntity can be used both in RestTemplate as well as @MVC
scenarios.

The class also comes with a builder, similar to the one found in
ResponseEntity, which allows for building of a RequestEntity through a
fluent API.

Issue: SPR-11752
2014-07-01 16:10:00 -04:00
Juergen Hoeller b7984f21d8 Polishing 2014-07-01 20:30:31 +02:00
Juergen Hoeller ec4e6e0b0e StatementCreatorUtils populates NVARCHAR, LONGNVARCHAR, NCLOB via setString as well
Issue: SPR-11938
2014-07-01 20:30:23 +02:00
Rossen Stoyanchev 7a1f19b96c Polish 2014-07-01 14:24:16 -04:00
Sebastien Deleuze ce20abde51 Improve subscription removal in SubscriptionRegistry
Avoid using destination pattern based search when removing sessions or
subscriptions from DefaultSubscriptionRegistry and use only session and
subscription ids.

Issue: SPR-11930
2014-07-01 14:23:26 -04:00
Sebastien Deleuze 083d415fc4 Fix cache cleanup issue in DefaultSubscriptionRegistry
Issue: SPR-11931
2014-07-01 14:23:06 -04:00
Sam Brannen 669386abac Organize imports in Spring MVC Test classes 2014-07-01 17:57:48 +02:00
Sam Brannen c60c1cec45 Suppress serial warning in JCacheCustomInterceptorTests 2014-07-01 17:40:18 +02:00
Sam Brannen f40d67cfec Prepare for upgrade to Gradle 2.0
Upgraded propdeps plugin to version 0.0.7 for Gradle 2.0 support.

Issue: SPR-11934
2014-07-01 17:37:17 +02:00
Juergen Hoeller ba522e5e55 Package javadoc cleanup for web.servlet.mvc, removing outdated references
Issue: SPR-11935
2014-07-01 17:26:27 +02:00
Stephane Nicoll 5ed20d21b1 Add test
This commit adds a test that reproduces the behaviour described in
SPR-11915 and validates that the fix introduced in f8b6114440 works
as expected.

Issue: SPR-11915
2014-07-01 17:21:08 +02:00
Sam Brannen 49e960c151 Polish doc & imports for MockAsyncClientHttpRequest
Issue: SPR-11822
2014-07-01 16:36:48 +02:00
Juergen Hoeller f8b6114440 BeanFactoryAnnotationUtils consistently applies bean name fallback when no BeanDefinition present
Issue: SPR-11915
2014-07-01 16:29:40 +02:00
Juergen Hoeller 0451232eb4 Polishing
(cherry picked from commit 86ea305)
2014-07-01 15:14:31 +02:00
Sam Brannen 7497391497 Prepare for upgrade to Gradle 2.0
MergePlugin now uses syntax appropriate for Gradle 2.0.

Issue: SPR-11934
2014-07-01 14:49:18 +02:00
Juergen Hoeller e90143e03b Polishing 2014-07-01 12:52:09 +02:00
Juergen Hoeller 9c46cb6f49 Upgrade to Reactor 1.1.3 2014-07-01 12:51:33 +02:00
Juergen Hoeller 37ba1b966e Seamless support for Groovy bean definitions mixed with XML bean definitions
GroovyBeanDefinitionReader and Groovy ApplicationContexts redirect ".xml" files to XmlBeanDefinitionReader now, similar to what they've been doing for importBeans directives already. Analogously, @ImportResource for configuration classes redirects ".groovy" to GroovyBeanDefinitionReader now.

Issue: SPR-11924
2014-06-30 22:53:25 +02:00
Rossen Stoyanchev 06d3e1b94e Fix Javadoc typo 2014-06-30 13:27:54 -04:00
Juergen Hoeller db616dc438 Polishing 2014-06-30 14:55:43 +02:00
Juergen Hoeller cee53e9330 Unit tests for method validation before async dispatching
Issue: SPR-11775
2014-06-30 13:49:48 +02:00
Rossen Stoyanchev 71b942698d Fix sniff task warnings 2014-06-29 22:56:53 -04:00
Rossen Stoyanchev b1aebb2c0c Fix failing test 2014-06-29 22:09:54 -04:00
Juergen Hoeller 974bd43f28 AbstractAutowireCapableBeanFactory's createBean/autowireBean/configureBean use CacheUtils.isCacheSafe to re-enable PropertyDescriptor caching
Issue: SPR-11875
2014-06-30 00:58:44 +02:00
Juergen Hoeller fab67b0595 CachedIntrospectionResults and co consistently avoid synchronization (through use of ConcurrentReferenceHashMap)
As a side effect, through ConcurrentReferenceHashMap, we're using soft references for non-cache-safe bean classes.

Issue: SPR-11867
2014-06-30 00:16:15 +02:00
Rossen Stoyanchev cb40908a7d Fix warnings from the sniff task 2014-06-29 17:35:00 -04:00
Rossen Stoyanchev 2b1ff4c5db Fix condition in SubProtocolWebSocketHandler
Issue: SPR-11884
2014-06-29 17:00:24 -04:00
Rossen Stoyanchev 9880d2b5f4 Remove JDK 1.7 guard for removeOnCancelPolicy property
Issue: SPR-11918
2014-06-29 15:30:39 -04:00
Rossen Stoyanchev 7441f23012 Add support for removeOnCancelPolicy
This change removes the recently added SockJsThreadPoolTaskScheduler
and instead builds support for the removeOnCancelPolicy property in
ThreadPoolTaskScheduler and ScheduledExecutorFactoryBean.

Issue: SPR-11918
2014-06-29 14:28:52 -04:00
Rossen Stoyanchev 7da3fb4ce6 Support STOMP DISCONNECT with receipt
Issue: SPR-11599
2014-06-29 13:15:22 -04:00
Rossen Stoyanchev 7dc2b2927e Use SESSION_NOT_RELIABLE when no messages received
When a WebSocket session is closed after not having received any
messages, we'll use SESSION_NOT_RELIABLE to indicate to other parts
of the session closing code not to send anything further (e.g.
SockJS "Go Away!" frame).

Issue: SPR-11884
2014-06-29 13:15:21 -04:00
Rossen Stoyanchev 3af488a701 Avoid multiple invocations of afterConnectionClosed
This change ensures the state of a SockJS session is set to CLOSED
immediately after close is invoked. This avoids duplicate invocations
of afterConnectionClosed in WebSocket transport.

Issue: SPR-11884
2014-06-29 13:15:21 -04:00
Rossen Stoyanchev 86de416908 More WebSocket logging updates
Update WebSocketSession toString methods to include the handshake URI
and add id and URI fields to ensure they're available after close().

Log WebSocket session open and close events at INFO.

Remove trace messages for destinations that do not match.

Issue: SPR-11884
2014-06-29 13:15:02 -04:00
Juergen Hoeller fac2d80054 AbstractApplicationContext uses AtomicBoolean instead of synchronization for active/closed flags
Issue: SPR-11863
2014-06-29 17:36:50 +02:00
Juergen Hoeller 0232739293 Javadoc coverage of package metadata detection (supported for Hibernate, not needed for EclipseLink and OpenJPA)
Issue: SPR-10910
2014-06-29 17:36:26 +02:00
Rossen Stoyanchev 1c91a52639 Add STOMP subscribe/unscubscribe ApplicationContext events
Issue: SPR-11813
2014-06-28 11:07:42 -04:00
Rossen Stoyanchev 9aa53abdf9 Add AsyncRestTemplate support to client-side MockMvc
Issue: SPR-1822
2014-06-28 09:49:19 -04:00
Juergen Hoeller 045d7357d5 SharedEntityManagerCreator immediately throws TransactionRequiredException on persist, merge, remove etc (as required by JPA spec)
Issue: SPR-11923
2014-06-27 19:58:20 +02:00
Rossen Stoyanchev cab0b97a83 Polish 2014-06-27 12:43:52 -04:00
Rossen Stoyanchev 7a5b3c1eed Append suffx to user dest in SimpMessagingTemplate
Isssue: SPR-11868
2014-06-27 12:43:52 -04:00
Juergen Hoeller 682a910bb6 ReflectionUtils caches Class.getDeclaredMethods() results; AnnotationUtils caches findAnnotation results
Issue: SPR-11882
2014-06-27 17:07:06 +02:00
Stephane Nicoll b0979cbab6 autoGrow support in DataBinder for field access
This commit harmonizes the autoGrow feature for both regular bean
property and direct field access.

Issue: SPR-8692
2014-06-27 11:28:09 +02:00
Stephane Nicoll 17d15cc495 Fix warning 2014-06-27 11:04:41 +02:00
Stephane Nicoll bc714888c4 Nested properties support for field-based validation
This commit validates that the changes introduced in 8221c9abc5 are
indeed allowing DirectFieldBindingResult to support nested validation
paths.

Issue: SPR-10623
2014-06-27 11:00:41 +02:00
Rossen Stoyanchev 113fd1180a Fine tune STOMP and WebSocket related logging
Optimize logging with tracking the opening and closing of WebSocket
sessions and STOMP broker connections in mind.

While the volume of messages makes it impractical to log every message
at anything higher than TRACE, the opening and closing of connections
is more manageable and can be logged at INFO. This makes it possible to
drop to INFO in production and get useful information without getting
too much in a short period of time.

The logging is also optimized to avoid providing the same information
from multiple places since messages pass through multiple layers.

Issue: SPR-11884
2014-06-27 04:06:06 -04:00
Brian Clozel b2f6445058 Fix unit test for SPR-11919
Issue: SPR-11919
2014-06-27 10:01:27 +02:00
Brian Clozel 24cdefb785 Workaround for WFLY-3474 NullPointerException
Prior to this commit, the ServletResponseHttpHeaders.get method
would throw an NPE when used under Wildfly 8.0.0.Final and 8.1.0.Final.
This can be traced to WFLY-3474, which throws an NPE when calling
HttpServletResponse.getHeaders("foo") and that header has not
been defined prior to that.
This would cause NPE being thrown by AbstractSockJsService when
checking for CORS HTTP headers in the server HTTP response.

This commit surrounds that method call in AbstractSockJsService and
guards against this issue.

Issue: SPR-11919
2014-06-26 23:47:10 +02:00
Stephane Nicoll 8221c9abc5 Support for nested path using field access
This commit adds a nested path support for DirectFieldAccessor that is
similar to what BeanWrapper provides. It is now possible to use
expressions such as "person.address.city.name" to access the name of
the city that a given person lives in using fields to traverse the
graph.

DirectFieldAccessor also now supports an auto-grow option to create
a default instance for a "null" intermediate path. This option is
false by default and leads to a NullValueInNestedPathException in such
a case.

This commit also harmonizes part of the tests suite so that core tests
are shared between BeanWrapperImpl and DirectFieldAccessor.

Note that map and list access is not implemented as part of this
commit.

Issue: SPR-9705
2014-06-26 19:22:02 +02:00
Juergen Hoeller 442bd682a7 Polishing
Issue: SPR-11910
2014-06-26 18:14:51 +02:00
Juergen Hoeller a9b650fd0f DefaultAdvisorChainFactory never passes null into ClassFilter, enabling async advisor to work without target class as well
Issue: SPR-11910
2014-06-26 18:12:44 +02:00
Rossen Stoyanchev 9796af72db Improve JettySockJsIntegrationTests to show real error
In a recent CI build failure a test timed out waiting for a message:
https://build.spring.io/browse/SPR-PUB-1316

In fact there was a WebSocket transport failure originating in Jetty's
WebSocket Parser. However this failure is only apparent by looking at
the logs. This change adds a check if a transport error ocurred while
we were waiting and throws an AssertionError.
2014-06-26 10:07:00 -04:00
Rossen Stoyanchev a3fa9c9797 Add check for unused WebSocket sessions
Sessions connected to a STOMP endpoint are expected to receive some
client messages. Having received none after successfully connecting
could be an indication of proxy or network issue. This change adds
periodic checks to see if we have not received any messages on a
session which is an indication the session isn't going anywhere
most likely due to a proxy issue (or unreliable network) and close
those sessions.

Issue: SPR-11884
2014-06-26 10:07:00 -04:00
Juergen Hoeller 98d6f7b443 Polishing 2014-06-26 16:01:24 +02:00
Juergen Hoeller 35067790f3 MBeanExporter implements DisposableBean again (also revised logging and rearranged properties)
Issue: SPR-8045
2014-06-26 15:55:40 +02:00
Juergen Hoeller 46dc07a005 AsyncExecutionInterceptor uses submitListenable if method signature indicates ListenableFuture
Issue: SPR-11909
2014-06-26 15:19:17 +02:00
Sam Brannen 50b21d061f Add TODO for fate of Hibernate JPA integration tests
Issue: SPR-11922
2014-06-26 14:41:06 +02:00
Sam Brannen 158cb346a5 Re-enable OpenJPA + AspectJ tests
With the upgrade to JDK 8, the OpenJPA/AspectJ integration tests now
pass again. Thus, we are re-enabling these tests.

Issue: SPR-10333
2014-06-26 14:41:06 +02:00
Sam Brannen e73b8b31f8 Delete accidental call to System.err 2014-06-26 14:41:05 +02:00
Juergen Hoeller 8ea0d8eea1 Latest dependency updates (AspectJ 1.8.1, Reactor 1.1.2, Tomcat 8.0.9, Jackson 2.4.1, JAMon 2.76) 2014-06-26 12:21:21 +02:00
Juergen Hoeller cc917de24d Polishing 2014-06-26 11:44:07 +02:00
Juergen Hoeller b3e3c5312f Introduced AnnotationConfigRegistry as common interface for AnnotationConfig(Web)ApplicationContext
Issue: SPR-11814
2014-06-26 11:43:03 +02:00
Juergen Hoeller 591f79514d Spring's default JPA persistence unit exposes package metadata as well (currently only supported with Hibernate)
Issue: SPR-10910
2014-06-26 11:25:50 +02:00
Brian Clozel 9919a98231 HttpHeaders fails getAllow if set to EmptyCollection
Prior to this commit, calls to getAllow would fail is setAllow was set
to an EmptyCollection right before.

    java.lang.IllegalArgumentException: No enum constant
    org.springframework.http.HttpMethod

This commit fixes this by testing the header value for an empty value
before trying to use it to get a value from the Enum.

Issue: SPR-11917
2014-06-25 23:12:35 +02:00
Rossen Stoyanchev 0f47732523 Fix failing test 2014-06-25 15:51:42 -04:00
Sam Brannen 502022acca Delete test annotations in spring-orm
This commit deletes all test annotations from the spring-orm module in
order to reduce unnecessary confusion between these "copies" and the
real, current versions of such classes in the spring-test module.

Furthermore, the legacy abstract JUnit 3.8 base classes and test cases
have been refactored accordingly.
2014-06-25 21:25:00 +02:00
Rossen Stoyanchev 6ad79e03c6 Create SockJS ThreadPoolTaskScheduler extension
Commit 5d2e6f enabled the setRemoveOnCancelPolicy of the SockJS
ScheduledThreadPoolExecutor by default. However that property is only
available in JDK 1.7 or later. This change fixes the issue and
introduces an extension of ThreadPoolTaskScheduler for for use with
SockJS which is necessary in any case since ThreadPoolTaskScheduler
does not expose the underlying ScheduledThreadPoolExecutor otherwise.

Issue: SPR-11918
2014-06-25 15:08:42 -04:00
Rossen Stoyanchev 3ed9885dc5 Merge SockJS client-related changes 2014-06-25 14:10:29 -04:00
Rossen Stoyanchev 1e342e4dbf Use SimpleAsyncTaskExecutor in WebSocketClient impls
JettyWebsocketClient and StandardWebSocketClient now use the
SimpleAsyncTaskExecutor by default to ensure non-blocking connect.
2014-06-25 14:09:42 -04:00
Rossen Stoyanchev 5d2e6f6d4c Prevent unbounded retention of cancelled SockJS tasks
This change sets the removeOnCancelPolicy on the SockJS
ScheduledThreadPoolExecutor to true. This ensures that cancelled
tasks are removed immediately to avoid the "unbounded retention
of cancelled tasks" that is mentioned in the Javadoc of
ScheduledThreadPoolExecutor:

"By default, such a cancelled task is not automatically removed from
the work queue until its delay elapses. While this enables further
inspection and monitoring, it may also cause unbounded retention of
cancelled tasks. To avoid this, set setRemoveOnCancelPolicy to true,
which causes tasks to be immediately removed from the work queue at
time of cancellation."

Issue: SPR-11918
2014-06-25 14:09:42 -04:00
Rossen Stoyanchev fcf6ae8328 Fix concurrency issues in SockJS session impls
This change ensures the server "WebSocketHandler" is notified of the
opening of a session before writing the open frame to the remote
handler. Any messages sent by the server "WebSocketHandler" while
getting notified of the opening get cached and flushed after the open
frame has been written.

This change introduces locking in AbtractHttpSockJsSession to guard
access to the HTTP response. The goal is to prevent contention between
client requests to receive messages (i.e. long polling) and
the application trying to write.

Issue: SPR-11916
2014-06-25 14:09:42 -04:00
Rossen Stoyanchev e82df99a22 Add SockJS client
This change adds a new implementation of WebSocketClient that can
connect to a SockJS server using one of the SockJS transports
"websocket", "xhr_streaming", or "xhr". From a client perspective
there is no implementation difference between "xhr_streaming" and
"xhr". Just keep receiving and when the response is complete,
start over. Other SockJS transports are browser specific
and therefore not relevant in Java ("eventsource", "htmlfile" or
iframe based variations).

The client loosely mimics the behavior of the JavaScript SockJS client.
First it sends an info request to find the server capabilities,
then it tries to connect with each configured transport, falling
back, or forcing a timeout and then falling back, until one of the
configured transports succeeds.

The WebSocketTransport can be configured with any Spring Framework
WebSocketClient implementation (currently JSR-356 or Jetty 9).

The XhrTransport currently has a RestTemplate-based and a Jetty
HttpClient-based implementations. To use those to simulate a large
number of users be sure to configure Jetty's HttpClient executor
and maxConnectionsPerDestination to high numbers. The same is true
for whichever underlying HTTP library is used with the RestTemplate
(e.g. maxConnPerRoute and maxConnTotal in Apache HttpComponents).

Issue: SPR-10797
2014-06-25 13:56:04 -04:00
Rossen Stoyanchev dc1d85d045 Support registering Filters in WebSocket test servers 2014-06-25 08:50:26 -04:00
Rossen Stoyanchev c14ba1a0ff Add SockJsFrameType enum
SPR-10797
2014-06-25 08:50:26 -04:00
Rossen Stoyanchev c86f711de5 Polish SockJsFrame 2014-06-25 08:50:26 -04:00
Juergen Hoeller b6255128bf ConfigurationClassPostProcessor skips pre-processed bean definitions now (avoiding side effects in case of multiple CCPPs)
Issue: SPR-11858
2014-06-25 13:46:15 +02:00
Juergen Hoeller d239016a8c UriComponentBuilder allows for multiple independent build() calls on same builder instance
Issue: SPR-11885
2014-06-25 13:19:28 +02:00
Juergen Hoeller f966bf683c Explicitly mention BeanPostProcessor declaration requirements with @Bean methods
Issue: SPR-11906
2014-06-25 12:44:17 +02:00
Juergen Hoeller 779ca99f38 SelectTag consistently checks specified 'multiple' attribute now, never falling back to forceMultiple in case of user-provided value
Issue: SPR-11903
2014-06-25 12:36:51 +02:00
Sam Brannen b4e16eacc5 Require JUnit 4.9 or higher in the TCF
Prior to this commit, the Spring TestContext Framework (TCF) was
compatible with JUnit 4.5 or higher.

This commit effectively raises the minimum version of JUnit that is
officially supported by the TCF to JUnit 4.9, thereby aligning with
similar upgrades made in the Spring Framework 4.0 release (i.e.,
upgrading minimum requirements on third-party libraries to versions
released mid 2010 or later).

Issue: SPR-11908
2014-06-25 10:10:25 +02:00
Juergen Hoeller 2d892da641 FrameworkPortlet exposes local PortletRequestAttributes in case of pre-bound ServletRequestAttributes as well
Issue: SPR-11295
2014-06-24 21:20:57 +02:00
Juergen Hoeller 938a2846c8 Avoid pattern matching in isCglibRenamedMethod as far as possible (for performance reasons)
Issue: SPR-11894
2014-06-24 21:07:08 +02:00
Juergen Hoeller d6635802c4 TypeDescriptor's nested type traversal leniently returns null in case of unresolvable nested type
Issue: SPR-11898
2014-06-24 20:57:09 +02:00
Juergen Hoeller a6f3f101e4 Preparations for eventual removal of ParameterizedRowMapper along with SimpleJdbcTemplate
Issue: SPR-11895
2014-06-24 19:26:53 +02:00
Brian Clozel 13c4a0396d Refactor VersionResourceResolver in strategies
Prior to this commit, one of the available strategies for resolving
resources was the PrefixResourceResolver. Reconsidering the core goal of
this resolver and the FingerprintResourceResolver, we found that the
true core feature is versioning static resources application-wide.

This commit refactors both Resolvers by:
* having only on VersionResourceResolver
* that resolver takes a mapping of paths -> VersionStrategy
* provided VersionStrategy implementations are ContentBasedVS
  (previously FingerprintRR), FixedVS (previously PrefixRR)

One can add a VersionResourceResolver like this:

  Map<String, VersionStrategy> versionStrategies = new HashMap<>();
  versionStrategies.put("/**/*.js", new PrefixVersionStrategy("prefix"));
  versionStrategies.put("/**", new ContentBasedVersionStrategy());

  VersionResourceResolver versionResolver = new VersionResourceResolver();
  versionResolver.setVersionStrategyMap(versionStrategies);

  List<ResourceResolver> resolvers = new ArrayList<ResourceResolver>();
  resolvers.add(versionResolver);
  resolvers.add(new PathResourceResolver());

Issue: SPR-11871
2014-06-24 18:18:05 +02:00
Juergen Hoeller 18131bf611 Consistent declaration of private static final logger variables
Issue: SPR-11905
2014-06-24 14:02:05 +02:00
Juergen Hoeller 2c0c081bbb AnnotationUtils defensively catches and logs unexpected exceptions from retrieval attempts (proceeding like the annotation wasn't there)
Issue: SPR-11874
2014-06-24 13:36:04 +02:00
Juergen Hoeller fd809cd18e AnnotationUtils defensively catches unexpected exceptions from retrieval attempts (proceeding like the annotation wasn't there)
This is analogous to what the JVM does for cases where the annotation type itself isn't present on the classpath. We're effectively extending that policy to values referenced within an annotation declaration.

Issue: SPR-11874
2014-06-24 11:01:37 +02:00
Sam Brannen f1517f03ff Delete remaining SimpleJdbcTemplate usage
This commit deletes all remaining usage of the deprecated
SimpleJdbcTemplate class within the framework itself.

Issue: SPR-11895
2014-06-22 15:40:41 +02:00
Sam Brannen 0c1249fe42 Introduce repeatable @Sql tests
This commit introduces explicit integration tests that verify Java 8's
@Repeatable support for the @Sql annotation.

Issue: SPR-7655
2014-06-20 14:37:45 +02:00
Sam Brannen abdb010fc8 Rename SQL script annotations in the TCF
Prior to this commit, SQL script annotations and related classes in the
TestContext framework (TCF) were named DatabaseInitializer*. However,
these annotations are not used only for initialization and are
therefore misleading when used for cleaning up the database.

This commit refines the names of annotations and related classes for
configuring SQL scripts to be executed for integration tests in the TCF
as follows:

- @DatabaseInitializer -> @Sql
- @DatabaseInitializers -> @SqlGroup
- DatabaseInitializerTestExecutionListener -> SqlScriptsTestExecutionListener

A special thanks goes out to the following attendees of the Zurich
Hackergarten meeting last night for their collective brainstorming:
@aalmiray, @atsticks, @ollin, @simkuenzi, @tangresh, @vyazelenko.

Issue: SPR-7655
2014-06-20 14:33:23 +02:00
Juergen Hoeller d801f9da89 ResourceUtils isFileURL detects "vfsfile" as a file system protocol (again)
Issue: SPR-11887
2014-06-20 01:03:01 +02:00
Juergen Hoeller dfc370c368 PathMatchingResourcePatternResolver's findPathMatchingResources needs to check for VFS before checking isJarResource
Issue: SPR-11887
2014-06-19 23:09:12 +02:00
Juergen Hoeller 03f3412434 WebSphereUowTransactionManager exposes SmartTransactionObject
Issue: SPR-11876
2014-06-19 22:30:10 +02:00
Juergen Hoeller 6f68b034ca MBeanExportConfiguration's SpecificPlatform properly calls afterPropertiesSet
Also makes the nested SpecificPlatform enum public for Spring Boot to reuse it.

Issue: SPR-11877
2014-06-19 22:24:04 +02:00
Juergen Hoeller ab5aea5a13 Defensively check javaUtilOptionalEmpty
Issue: SPR-11888
2014-06-19 21:25:46 +02:00
Juergen Hoeller bc1f19ce18 Defensively check javaUtilOptionalEmpty
Issue: SPR-11888
2014-06-19 21:14:43 +02:00
Brian Clozel cff36d68b1 update documentation with latest forum infos
Forums will be shortly deactivated and questions should take place
in StackOverflow.
2014-06-19 15:33:06 +02:00
Juergen Hoeller ef25b9e7af ObjectToOptionalConverter needs to be marked as @UsesJava8 2014-06-18 23:39:19 +02:00
Juergen Hoeller 88eabe874c Latest dependency updates (Jackson 2.4, Jetty 9.2.1, H2 1.4.178, Apache HttpClient 4.3.4) 2014-06-17 21:58:11 +02:00
Rossen Stoyanchev bb4a9cca1b Add suppress warning in SettableListenableFuture 2014-06-16 17:06:52 -04:00
Rossen Stoyanchev cf9d519e97 Merge pull request #504 from matsev/SPR-11614 2014-06-16 15:42:31 -04:00
Rossen Stoyanchev 0640a32863 Revise SettableListenableFuture implementation
This change modifies the SettableListenableFuture implementation to use
internally a ListenableFutureTask created with a "settable" Callable.

Issue: SPR-11614
2014-06-16 15:37:30 -04:00
Mattias Severson 38b525aa9e Created SettableListenableFuture with tests
A SettableListenableFuture implementation of Spring's ListenableFuture
The class is inspired by Google Guava’s
com.google.common.util.concurrent.SettableFuture, but this
implementation uses ReentrantReadWriteLock and CountDownLatch
internally to handle thread synchronization.

Issue: SPR-11614
2014-06-16 15:14:12 -04:00
Rossen Stoyanchev 0dc6082b01 Support java.util.Optional for @MVC named value args
After this change, java.util.Optional is supported with @RequestParam,
@RequestHeader, and @MatrixVariable arguments in Java 8. When Optional
is used the required flag is effectively ignored.

Issue: SPR-11829
2014-06-16 14:16:56 -04:00
Stephane Nicoll c2356e57c8 Merge pull request #562 from hengyunabc/master
* pull562:
  Improve StringUtils#trimAllWhitespace
2014-06-13 18:19:10 +02:00
hengyunabc 4d328d6188 Improve StringUtils#trimAllWhitespace
Prior to this commit, StringUtils#trimAllWhitespace(String str) was
unecessary slower. Using sb.deleteCharAt(index) leads to a complete
copy of the char[]
2014-06-13 18:18:19 +02:00
Sam Brannen 98a224fed9 Update Eclipse import script regarding AJDT 2014-06-12 14:04:54 +02:00
Brian Clozel 72baa9bf27 Ignore external resources in CssLinkResourceTransormer
Prior to this commit, the CssLinkResourceTransformer would transform
"external resources", i.e. resources not served by the web application.

This commit only allows transformation for resources which path don't
contain scheme such as "file://" or "http://". Only relative and
absolute paths for resources served by the webapp are valid.

Issue: SPR-11860
2014-06-12 10:54:41 +02:00
Brian Clozel e18e44980d Upgrade to Groovy 2.3.3
See release notes:
https://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=10242&version=20415
2014-06-12 10:22:53 +02:00
Rossen Stoyanchev 9f4d4d1829 Check X-Forwarded-Host in ServletUriComponentsBuilder
Issue: SPR-11855
2014-06-11 22:40:48 -04:00
Rossen Stoyanchev c809143cc4 Upgrade to Undertow 1.0.15 and fix failing test
Issue: SPR-11777
2014-06-11 17:45:19 -04:00
Brian Clozel d746e3fbfa Rollback AntPathMatcher behavior for ".*" comparisons
Prior to this commit, AntPathMatcher had been refactored for SPR-6741.
During that process, a key feature has been removed:
When comparing two patterns, pattern elements (*, {}, etc) are counted
to score those patterns. When a pattern ends with ".*", the ending
wildcard should not be counted against pattern elements for this
pattern.

This commit reintroduces that behavior.

Issue: SPR-6741
2014-06-11 20:51:09 +02:00
Rossen Stoyanchev ce0473f926 Further encapsulate AntPatternComparator$PatternInfo
Issue: SPR-6741
2014-06-11 20:51:09 +02:00
Brian Clozel f829cd1b35 Fix "**" precedence in AntPathMatcher comparator
Prior to this commit, "**" and "*" pattern elements had
the same priority when comparing two patterns.
So when comparing several patterns, the computed order was:
1- /hotels/{hotel}/bookings/{booking}
2- /hotels/**
3- /hotels/{hotel}/bookings/{booking}/customer/{customer}

This commit updates the comparator so that patterns ending
with "**" (a.k.a "catch-all" patterns) are less specific than
the others; in the previous example, the 2nd pattern would
then end up last.

This commit also optimizes the comparator implementation.

Issue: SPR-6741
2014-06-11 20:51:09 +02:00
Brian Clozel 80a16c6d10 PathMatching options:configure all HandlerMappings
Since SPR-11486 and SPR-10163, Path Matching options can be configured
to customize path matching options for RequestMappingHandlerMapping.
Prior to this commit, the defined pathMatcher and pathHelper instances
were only used in RequestMappingHandlerMapping.

This commit now registers pathMatcher and pathHelper beans under
well-known names and share them with several HandlerMappings beans,
such as ViewControllerMappings and ResourcesMappings.

Issue: SPR-11753
2014-06-11 17:07:04 +02:00
Stephane Nicoll 014ea963fe Merge pull request #544 from kryger/patch-1
* patch-2:
  Fix method comment for getRequiredProperty(String)
2014-06-11 11:58:44 +02:00
Lukasz Kryger 0dc52a8343 Fix method comment for getRequiredProperty(String) 2014-06-11 11:56:58 +02:00
Stephane Nicoll 9ba9f562f2 Add @ComponentScan documentation
This commit provides a proper documentation for the @ComponentScan
annotation as a java config alternative to <context:component-scan/>

Issue: SPR-11846
2014-06-11 11:51:10 +02:00
Rossen Stoyanchev b214db3fc8 Rename HttpStatus 308 to Permanent Redirect
Issue: SPR-11854
2014-06-10 18:51:22 -04:00
Rossen Stoyanchev 6b129c52e3 Add "mvcUrl" function to Spring tag library
This commit adds a new function to the Spring tag library for preparing
links to @Controller methods. For more details see the Javadoc of
MvcUriComponentsBuilder.fromMappingName.

Issue: SPR-5779
2014-06-10 18:11:11 -04:00
Rossen Stoyanchev 2140648246 Fix failing test 2014-06-10 15:03:17 -04:00
Rossen Stoyanchev 5b02222a9f Add resolveTemplate method to Groovy markup configurer 2014-06-10 11:59:11 -04:00
Brian Clozel 0ecfa8e404 Add Spring View support for Groovy Markup Templates
This commit adds support for Groovy Markup templates.
Spring's support requires Groovy 2.3.1+.

To use it, simply create a GroovyMarkupConfigurer and a
GroovyMarkupViewResolver beans in the web application context.

Issue: SPR-11789
2014-06-10 11:37:53 -04:00
Stephane Nicoll 6b6b008c1f Fix typo
Issue: SPR-11862
2014-06-10 16:51:13 +02:00
Stephane Nicoll 5cc779ab35 Fix callout formatting issue 2014-06-10 10:58:05 +02:00
Stephane Nicoll d1c780f5b5 Merge pull request #551 from mdeinum/SPR-8045
* SPR-8045:
  polishing
  Using SmartLifecycle to register MBeans
2014-06-10 10:22:27 +02:00
Stephane Nicoll c7b106577f polishing
Reorganized class structure to match our code style (setter for
properties at the top of the class, public method before private
implementation).

Removed DisposableBean as it the lifecycle is already taking care
of removing MBeans on stop.

Cleaned test suite

Issue: SPR-8045
2014-06-10 10:13:58 +02:00
Marten Deinum 2ede219e66 Using SmartLifecycle to register MBeans
Prior to this commit, MBeans were registered in a post construct
call of MBeanExporter. This commit moves that logic after the
initialization phase using the SmartLifecycle callback.

Issue: SPR-8045
2014-06-10 10:13:58 +02:00
Rossen Stoyanchev fc91add35e Allow custom UrlPathHelper for WebSocket requests
Issue: SPR-117711
2014-06-09 17:42:43 -04:00
Rossen Stoyanchev 0cb6f8c88c Polish MappingJackson2JsonView 2014-06-09 09:39:17 -04:00
Sebastien Deleuze 5dc27ee134 Add JSONP support to MappingJackson2JsonView
Enable JSONP support by wrapping the JSON output into
a callback when a JSONP query parameter specifying the
function name to use as callback is detected.

Default query parameter names recognized as JSONP ones
are "jsonp" and "callback". This list can be customized if
needed.

This commit also fixes JSONView support by removing
the view name specified in the model from the output.

Issue: SPR-8346
2014-06-09 09:39:17 -04:00
Rossen Stoyanchev 56a82c1cbe Add dependency ordering for @ModelAttribute methods
Before this change @ModelAttribute methods were not invoked in any
particular order other than ensuring global @ControllerAdvice methods
are called first and local @Controller methods second.

This change introduces a simple algorithm that selects the next
@ModelAttribute method to invoke by making a pass over all methods and
looking for one that has no dependencies (i.e. @ModelAttribute
input arguments) or has all dependencies resolved (i.e. available in
the model). The process is repeated until no more @ModelAttribute
methods remain.

If the next @ModelAttribute method cannot be determined because all
remaining methods have unresolved dependencies, the first available
method is picked anyway just as before, i.e. with required
dependencies created through the default constructor.

Examples in ModelFactoryOrderingTests.

Issue: SPR-6299
2014-06-07 16:21:36 -04:00
Rossen Stoyanchev e374769ecc Polish ModelFactory 2014-06-07 16:21:35 -04:00
Sam Brannen f442ce180e Polish assertions and Javadoc for ResourceDatabasePopulator 2014-06-07 14:13:45 +02:00
Juergen Hoeller af4621d996 Reference documentation consistently refers to Apache HttpComponents instead of outdated Commons HttpClient
Includes overhaul of remaining references to J2SE and J2EE, consistently replacing them with "the JDK" and "Java EE".

Issue: SPR-11853
2014-06-07 00:29:09 +02:00
Juergen Hoeller ea346d59b3 Polishing 2014-06-07 00:25:06 +02:00
Juergen Hoeller ae66e45887 Refined tests for FactoryBean return type resolution on @Bean methods
Issue: SPR-11842
2014-06-07 00:24:45 +02:00
Juergen Hoeller 85b2c7d116 AbstractAutowireCapableBeanFactory's getTypeForFactoryBean considers FactoryBean<Object> declarations as non-indicative (just like raw declarations)
Issue: SPR-11842
2014-06-06 18:43:29 +02:00
Rossen Stoyanchev de1a41ac27 Fix issue with RequestBody(required=true)
Issue: SPR-11828
2014-06-06 12:07:14 -04:00
Rossen Stoyanchev c269d27bde Improve no content handling in MockHttpServletRequest
Issue: SPR-11764
2014-06-06 11:25:15 -04:00
Rossen Stoyanchev 9706e5cc99 Update Javadoc on SockJS client library URL
Issue: SPR-11766
2014-06-06 10:32:04 -04:00
Juergen Hoeller 5cb3f8eada Support for java.util.Optional within ObjectFactory/Provider
Includes support for arbitrary deep nesting levels in DependencyDescriptor's getDependencyType() and MethodParameter's getNestedParameterType().

Issue: SPR-11833
2014-06-06 15:29:50 +02:00
Juergen Hoeller 7d03daf8cb Support for Java 8's java.util.Optional at injection points
Issue: SPR-11833
2014-06-06 14:41:01 +02:00
Juergen Hoeller 6aa9c40552 ConfigurationClassParser falls back to ASM-based resolution of member classes in case of NoClassDefFoundError
Issue: SPR-11844
2014-06-06 14:01:38 +02:00
Stephane Nicoll bb8d0f1740 Merge pull request #558 from VasylTretiakov/patch01
* patch01:
  Reference manual polishing
2014-06-06 08:01:33 +02:00
Vasyl Tretiakov abc377603d Reference manual polishing 2014-06-06 07:57:55 +02:00
Sam Brannen f48bdafd52 Polish Javadoc for TestContextTransactionUtils 2014-06-05 21:09:29 +02:00
Sam Brannen 56ad0cf3e1 Polish Eclipse import script 2014-06-05 21:00:37 +02:00
Sam Brannen f2c37fcdb4 Update regarding tested versions of Eclipse & STS 2014-06-05 20:51:20 +02:00
Sam Brannen ae29f48f5f Merge pull request #557 from sbrannen/SPR-7655
Introduce annotation to execute SQL scripts in the TCF
2014-06-05 20:12:45 +02:00
Sam Brannen 5fd6ebb548 Introduce annotation to execute SQL scripts in the TCF
Prior to this commit, it was possible to execute SQL scripts
programmatically via ResourceDatabasePopulator, JdbcTestUtils, and
ScriptUtils. Furthermore, it was also possible to execute SQL scripts
declaratively via the <jdbc> XML namespace. However, it was not
possible to execute SQL scripts declaratively on a per test class or
per test method basis.

This commit makes it possible to declaratively configure SQL scripts
for execution in integration tests via annotations that can be declared
at the class or method level. Details follow.

 - Introduced a repeatable @DatabaseInitializer annotation that can be
   used to configure SQL scripts at the class or method level with
   method-level overrides. @DatabaseInitializers serves as a container
   for @DatabaseInitializer.

 - Introduced a new DatabaseInitializerTestExecutionListener that is
   responsible for parsing @DatabaseInitializer and
   @DatabaseInitializers and executing SQL scripts.

 - DatabaseInitializerTestExecutionListener is registered by default in
   abstract base test classes as well as in TestContextBootstrapper
   implementations.

 - @DatabaseInitializer and @DatabaseInitializers may be used as
   meta-annotations; however, attribute overrides are not currently
   supported for repeatable annotations used as meta-annotations. This
   is a known limitation of Spring's AnnotationUtils.

 - The semantics for locating SQL script resources is consistent with
   @ContextConfiguration's semantics for locating XML configuration
   files. In addition, a default SQL script can be detected based
   either on the name of the annotated class or on the name of the
   annotated test method.

 - @DatabaseInitializer allows for specifying which DataSource and
   PlatformTransactionManager to use from the test's
   ApplicationContext, including default conventions consistent with
   TransactionalTestExecutionListener and @TransactionConfiguration.

 - @DatabaseInitializer supports all of the script configuration options
   currently supported by ResourceDatabasePopulator.

 - @DatabaseInitializer and DatabaseInitializerTestExecutionListener
   support execution phases for scripts that dictate when SQL scripts
   are executed (i.e., before or after a test method).

 - SQL scripts can be executed within the current test's transaction if
   present, outside of the current test's transaction if present, or
   always in a new transaction, depending on the value of the boolean
   requireNewTransaction flag in @DatabaseInitializer.

 - DatabaseInitializerTestExecutionListener delegates to
   ResourceDatabasePopulator#execute to actually execute the scripts.

Issue: SPR-7655
2014-06-05 20:06:11 +02:00
Juergen Hoeller 8fb0f5c7d1 FrameworkPortlet properly initializes StandardPortletEnvironment's property sources
Issue: SPR-11816
2014-06-05 15:09:02 +02:00
Juergen Hoeller 5a8e470ede DefaultSubscriptionRegistry's removeSubscriptionInternal defensively handles non-existing destinations
Issue: SPR-11832
2014-06-04 22:48:34 +02:00
Juergen Hoeller 2cf88ac6a3 Polishing 2014-06-04 21:49:23 +02:00
Juergen Hoeller f7b465390c Moved @Uses annotations to org.springframework.lang; fixed Base64Utils to declare Java 8, and fixed PathResource's declaration to refer to Java 7.
Issue: SPR-11604
2014-06-04 21:34:23 +02:00
Stephane Nicoll 77cbc53d1e Merge pull request #553 from AlexYursha/patch-1
* patch-1:
  Fix a typo
2014-06-04 21:17:36 +02:00
Alex Yursha 8b8afe3def Fix a typo 2014-06-04 21:16:17 +02:00
Stephane Nicoll 001d0e734c Support for @Order at the bean declaration level
This commit introduces OrderProvider and OrderProviderComparator, two
interfaces designed to externalize how a collection of element is sorted
according to their order value.

FactoryAwareOrderProvider is an OrderProvider implementation that knows
about the objects to order and the corresponding BeanFactory instance.
This allows to retrieve additional metadata about the actual instances
to sort, such as its factory method.

A @Bean method can now holds an additional @Order to define the order
value that this bean should have when injected as part of a collection
or array.

Issue: SPR-11310
2014-06-04 21:04:13 +02:00
Juergen Hoeller 64bb308763 GsonBuilderUtils for programmatic Base64 serialization setup; common Base64Utils class adapts between Java 8 and Commons Codec
Issue: SPR-9488
2014-06-04 13:22:11 +02:00
Stephane Nicoll 674bad4cfa Clarify the use of sessionTransacted
This commit clarifies the role of the external transactionManager for
a DMLC and the relationship with the sessionTransacted flag

Issue: SPR-9200
2014-06-02 17:52:03 +02:00
Stephane Nicoll aaae10ce3b Cache operation invocation hook point
This commit adds a invokeOperation protected method in case one
needs a hook point in the way the underlying cache method is invoked,
and how exceptions that might be thrown by that invocation are handled.

Issue: SPR-11540
2014-06-02 15:00:51 +02:00
Rossen Stoyanchev c9d0ebd730 Rename ResponseBodyInterceptor to ResponseBodyAdvice
Issue: SPR-10859
2014-05-30 17:02:28 -04:00
Rossen Stoyanchev 2655c507e0 Parameterize ResponseBodyInterceptor on the type level
Issue: SPR-10859
2014-05-30 17:02:28 -04:00
Juergen Hoeller 5eecb138f6 Unit tests for custom profile annotations on configuration classes
Issue: SPR-11808
2014-05-30 22:04:40 +02:00
Juergen Hoeller 31f2f20307 Unit tests for configuration classes implementing the JSR-330 Provider interface
Issue: SPR-11830
2014-05-30 21:37:01 +02:00
Juergen Hoeller b4954780aa ClassMetadata exposes isAnnotation() now, and correctly returns false from hasSuperClass() for interfaces and annotations
Issue: SPR-11711
2014-05-30 18:14:36 +02:00
Rossen Stoyanchev 4372dac002 Polish 2014-05-30 12:04:32 -04:00
Artem Bilan 0dddb6f3e1 Fix issue with StompSubProtocolHandler initialization
This change ensures that StompSubProtocolHandler is injected with an
ApplicationEventPublisher for both the Java and XML config.

Issue: SPR-11825
2014-05-30 12:04:32 -04:00
Juergen Hoeller 5ed9bedf32 Introducing GroovyWebApplicationContext along the lines of XmlWebApplicationContext and GenericGroovyApplicationContext
Also includes minor dependency updates such as Groovy 2.3.2.

Issue: SPR-11371
2014-05-30 17:35:00 +02:00
Juergen Hoeller e4aabd5288 MarshallingView unwraps JAXBElement value for Marshaller.supports(Class) check
Issue: SPR-11827
2014-05-30 17:27:34 +02:00
Juergen Hoeller 4e17685008 Made transform test pass on Windows 2014-05-30 17:27:14 +02:00
Stephane Nicoll e4ad9c5204 polishing
If no converter is found, the MessageConversionException now contains
the message instance that could not be handled

Issue: SPR-11817
2014-05-30 14:53:09 +02:00
Sam Brannen 987806f7c2 Polishing 2014-05-30 14:35:01 +02:00
Sam Brannen 8ee94a4392 Eclipse project deps come after third-party libs
Prior to this commit, the ordering of classpath entries in the
generated Eclipse .classpath files did not align with the intended
dependency management configured in the Gradle build. Specifically,
project dependencies often came before dependencies on third-party
libraries required for the given project, causing the project not to
compile in Eclipse.

This commit fixes this issue by introducing new functionality in the
ide.gradle script that moves all project dependencies to the end of the
generated classpath.

Issue: SPR-11836
2014-05-30 13:31:02 +02:00
Sam Brannen 4b291c665c Upgrade spring-test tests to Hibernate 4
This commit upgrades Hibernate-based integration tests in the
spring-test module to use Hibernate 4 instead of 3 and Hibernate
Validator 5 instead of 4. This streamlines and simplifies our
dependency management at the same time.

Issue: SPR-11834
2014-05-29 18:18:55 +02:00
Sam Brannen d46b29e71f Upgrade Eclipse project settings for Java 8
- Set the eclipse.jdt source and target compatibility to 1.8 in
   ide.gradle.

 - Updated the instructions in import-into-eclipse.sh to reflect minimum
   requirements for building Spring Framework 4.1 against Java 8 with
   Eclipse and STS

Issue: SPR-11831
2014-05-29 17:26:25 +02:00
Stephane Nicoll 12a9df8a1c Throw a proper exception if no convert is found
Prior to this commit, no exception was raised if a message could not
be converted to the requested payload because no suitable converter
were found.

This commit adds an explicit check if the converted payload is null.

Issue: SPR-11817
2014-05-28 16:48:48 +02:00
Sam Brannen 8dec1db914 Tweak JmsMessagingTemplateTests for Eclipse 4.4
Eclipse 4.4 early release versions have issues with generics.

This commit slightly tweaks the use of generics in
JmsMessagingTemplateTests so that the class compiles in the latest
builds of Eclipse 4.4
2014-05-28 12:45:26 +02:00
Rossen Stoyanchev 3944f7ad97 Fix animalSniffer warning 2014-05-27 21:27:35 -04:00
Rossen Stoyanchev 6966e89578 Add ResourceTransformer and CSS link implementation
This change adds a ResourceTransformer that can be invoked in a chain
after resource resolution. The CssLinkResourceTransformer modifies a
CSS file being served in order to update its @import and url() links
(e.g. to images or other CSS files) to match the resource resolution
strategy (e.g. adding MD5 content-based hashes).

Issue: SPR-11800
2014-05-27 20:54:22 -04:00
Sam Brannen e3e8a3eb40 Fix grammar in Javadoc for Propagation 2014-05-28 00:17:03 +02:00
Juergen Hoeller 8220832c4e Polishing 2014-05-27 18:29:51 +02:00
Juergen Hoeller 22a38d4547 Revised GsonFactoryBean's configuration properties; made prettyPrinting test pass on Windows
Issue: SPR-9488
2014-05-27 18:26:48 +02:00
Juergen Hoeller d9f8ee886e Introduced EmbeddedValueResolutionSupport base class for AnnotationFormatterFactory implementations 2014-05-27 18:15:34 +02:00
Juergen Hoeller 8f2ed66b4a ServletRequestAttributes considers standard Number subclasses (as defined in NumberUtils) as immutable
Issue: SPR-11738
2014-05-27 17:43:31 +02:00
Stephane Nicoll bb6e07bd3a polishing 2014-05-26 13:39:38 +02:00
Stephane Nicoll 05041ff39a Update JMS documentation
This commit updates the JMS documentation regarding the side effects
of not using the  cache with a non durable subscriber.

Issue: SPR-7883
2014-05-26 10:40:32 +02:00
Stephane Nicoll 9fabcad3dd Revisit JmsMessagingTemplate
This commit revisits JmsMessagingTemplate and adds support for
receiving operations as well. JmsMessageSendingOperations has been
renamed to JmsMessageOperations.

The messaging abstraction did not split receiving and request-reply
operations. AbstractMessageReceivingTemplate has been created to hold
only the receiving operations.

Issue: SPR-11772
2014-05-26 09:35:03 +02:00
Brian Clozel 31c07dbfa4 Merge pull request #546 from gcuisinier/master
fix javadoc link for RestController and PropertySource
2014-05-24 22:39:20 +02:00
Gildas Cuisinier 8d306bfbc0 fix javadoc link for RestController and PropertySource 2014-05-24 18:29:52 +02:00
Rossen Stoyanchev e3a6fce403 Add Gson converter to MVC config
Issue: SPR-9488
2014-05-22 22:23:00 -04:00
Rossen Stoyanchev bc3ca2dea1 Polish 2014-05-22 22:21:52 -04:00
Roy Clarkson 811330f5c8 Add GsonHttpMessageConverter
This commit adds support to read and write JSON using the Google Gson
library. GsonHttpMessageConverter offers default Gson configuration, but
can be customized by using GsonFactoryBean. GsonFactoryBean includes
several convenience properties for configuring the internal GsonBuilder
and the resulting Gson object.

By default Gson converts byte arrays to JSON arrays instead of a Base64
encoded string. GsonBase64ByteArrayJsonTypeAdapter provides support to
read and write Base64 encoded byte arrays, and can be enabled in
GsonFactoryBean.

RestTemplate will enable GsonHttpMessageConverter only if Jackson 2 is
not found on the class path, because by default GsonHttpMessageConverter
supports the same media types as Jackson.

Issue: SPR-9488
2014-05-22 22:21:52 -04:00
Stephane Nicoll 9952973e01 Add missing cache-resolver attribute
Prior to this commit, CacheResolver could not be configured through
the XML namespace (i.e. cache:annotation-driven). This is now the
case.

Issue: SPR-11490
2014-05-21 09:03:18 +02:00
Rossen Stoyanchev 1338d46a6e Add JSONP support for MappingJackson2MessageConverter
Issue: SPR-9899
2014-05-20 12:13:44 -04:00
Stephane Nicoll 05e96ee448 Cache provider related exceptions handling
This commit adds the necessary infrastructure to handle exceptions
thrown by a cache provider in both Spring's and JCache's caching
abstractions.

Both interceptors can be configured with a CacheErrorHandler that
defines several callbacks on typical cache operations. In particular,
handleCacheGetError can be implemented in such a way that an
exception thrown by the provider is handled as a cache miss by the
caching abstraction.

The handler can be configured with both CachingConfigurer and the
XML namespace (error-handler property)

Issue: SPR-9275
2014-05-20 16:39:34 +02:00
Juergen Hoeller c7d1c49d6d Servlet 3 multipart request implements getParameterMap defensively as well
Issue: SPR-11074
2014-05-20 11:10:16 +02:00
Juergen Hoeller 6fef8b996c Polishing 2014-05-20 10:46:06 +02:00
Rossen Stoyanchev 8e096aeef5 Disable URL resolution in DTD declarations
Issue: SPR-11768
2014-05-19 22:45:12 -04:00
Juergen Hoeller 61b47ba7d9 Consistent MvcUriComponentsBuilder assertion handling 2014-05-20 01:43:01 +02:00
Juergen Hoeller a16eeabc4e Further UriComponentsBuilder javadoc revision
(cherry picked from commit 54636b3)
2014-05-20 01:26:08 +02:00
Juergen Hoeller d9b39ad691 Consistent use of IllegalStateException instead of InternalError for UnsupportedEncodingException cause 2014-05-20 00:37:09 +02:00
Juergen Hoeller 73c9d09024 UriComponentsBuilder.toUriString instead of RequestContext.getMvcUrl (breaking cyclic dependency between web.servlet.support and web.servlet.mvc) 2014-05-20 00:35:09 +02:00
Juergen Hoeller 03ce4a5024 Polishing 2014-05-20 00:31:24 +02:00
Juergen Hoeller cfc720db25 Latest patches from ASM trunk 2014-05-19 22:29:27 +02:00
Juergen Hoeller 295a6aeed6 StringUtils.parseLocaleString parses variant correctly when variant contains country code
This commit also includes a JUnit 4 style revision of StringUtilsTests and ObjectUtilsTests.

Issue: SPR-11806
2014-05-19 22:29:19 +02:00
Juergen Hoeller 1285467fe6 Consistently log Class.getName() instead of Class.toString(), avoiding double class term in log message
Issue: SPR-11804
2014-05-19 22:29:10 +02:00
Rossen Stoyanchev 51fc3b4aaf Refactor @JsonView support w/ ResponseBodyInterceptor
The newly added support for ResponseBodyInterceptor is a good fit for
the (also recently added) support for the Jackson @JsonView annotation.

This change refactors the original implementation of @JsonView support
for @ResponseBody and ResponseEntity controller methods this time
implemented as an ResponseBodyInterceptor.

Issue: SPR-7156
2014-05-19 15:47:16 -04:00
Rossen Stoyanchev 96b18c8dc2 Add ResponseBodyInterceptor
This change introduces a new ResponseBodyInterceptor interface that can
be used to modify the response after @ResponseBody or ResponseEntity
methods but before the body is actually written to the response with the
selected HttpMessageConverter.

The RequestMappingHandlerAdapter and ExceptionHandlerExceptionResolver
each have a property to configure such interceptors. In addition both
RequestMappingHandlerAdapter and ExceptionHandlerExceptionResolver
detect if any @ControllerAdvice bean implements ResponseBodyInterceptor
and use it accordingly.

Issue: SPR-10859
2014-05-19 15:47:16 -04:00
Juergen Hoeller f73a8baec7 Building against latest javax.el 2.2.x version 2014-05-19 20:31:22 +02:00
Juergen Hoeller 2619955fc3 Consistently log Class.getName() instead of Class.toString(), avoiding double class term in log message
Issue: SPR-11804
2014-05-19 20:18:50 +02:00
Juergen Hoeller 41ed228450 Refined check for NoClassDefFoundError in getTestExecutionListeners()
Issue: SPR-11804
2014-05-19 16:31:23 +02:00
Juergen Hoeller 6188550a48 ServletRequestAttributes skips well-known immutable values when updating accessed session attributes
Issue: SPR-11738
2014-05-19 16:00:29 +02:00
Juergen Hoeller 3c45bc4a50 Building against Groovy 2.3.1 2014-05-19 15:54:19 +02:00
Stephane Nicoll 7469159bf1 Add MessageSendingOperations for JMS
This commit adds a JMS implementation of MessageSendingOperations,
allowing to send JMS messages using Spring's standard Messaging
abstraction.

MessagingMessageConverter is a standard JMS's MessageConverter that
can convert Spring's Message to JMS message and vice versa. Existing
infrastructure has been updated to use this implementation.

Issue: SPR-11772
2014-05-19 10:46:13 +02:00
Juergen Hoeller 82336c320d Servlet 3 multipart request implements getParameterNames defensively (for WebLogic 12 compatibility)
This commit also includes lazy resolution support for StandardServletMultipartResolver, along the lines of existing lazy mode in CommonsMultipartResolver.

Issue: SPR-11074
Issue: SPR-11730
2014-05-18 20:26:02 +02:00
Juergen Hoeller f29d6eb5f6 Properly evaluate @Conditional in case of multiple imports for same config class (fixing regression in Spring Boot)
Issue: SPR-11788
(cherry picked from commit 7d78c65)
2014-05-18 02:05:18 +02:00
Juergen Hoeller ea88bc2c81 Servlet/PortletResponse supported as a resolvable dependency now (in particular for web controllers)
This feature required support for response exposure on Servlet/PortletRequestAttributes, instead of just in the Servlet/PortletWebRequest subclasses.

Issue: SPR-11795
2014-05-16 18:06:29 +02:00
Juergen Hoeller 5faacd5a3d JmsResourceHolder checks for nested DataSource transactions as well (for Oracle AQ compatibility)
Issue: SPR-11791
2014-05-16 15:08:09 +02:00
Juergen Hoeller 52f44b340e Properly evaluate @Conditional in case of multiple imports for same config class
Issue: SPR-11788
2014-05-16 15:06:22 +02:00
Sebastien Deleuze be0b69cbf1 Add support for Jackson serialization views
Spring MVC now supports Jackon's serialization views for rendering
different subsets of the same POJO from different controller
methods (e.g. detailed page vs summary view).

Issue: SPR-7156
2014-05-15 23:03:33 -04:00
Rossen Stoyanchev 673a497923 Await TcpClient shutdown in STOMP broker relay 2014-05-15 22:00:17 -04:00
Rossen Stoyanchev 8ee4651038 Improve StringUtils.cleanPath
Issue: SPR-11793
2014-05-15 17:26:52 -04:00
Rossen Stoyanchev 3bdf8aefa9 Polish 2014-05-15 13:59:36 -04:00
Arjen Poutsma 0499fcbeb2 Move ResponseEntityBuilder into ResponseEntity
Moved the ResponseEntityBuilder into the ResponseEntity itself.

Issue: SPR-11752
2014-05-15 13:41:25 -04:00
Stephane Nicoll 8614df8bd4 Add error code for SAP Hana
This commit adds a SQLErrorCodes instance for SAP DB, based on a
contribution from Andrew Clemons

Mostly based on SAP Hana SPS 07 with a few additional codes that are
only present is previous versions:

* -813 - Cannot connect to host somehost:30115 [Connection refused]
* -709 - Unknown host somehost:30115 [somehost], -709.]
* -708 - Cannot connect to jdbc:sap://somehost:30115 [Receive of connect failed.]
* -11210 - Invalid column index XYZ.

Issue: SPR-11770
2014-05-15 17:36:55 +02:00
Juergen Hoeller 0d22719e06 Latest possible dependency updates for spring-orm 2014-05-15 17:07:20 +02:00
Juergen Hoeller 7230d6a1aa Polishing 2014-05-15 17:07:13 +02:00
Stephane Nicoll 6560aed1c8 Avoid JMSException in listener execution
This commit avoids throwing JMSException from the listener execution
as this is not allowed per spec. Our SessionAwareMessageListener
gives a callback that can throw JMSException and we have "abused" it
so far.

Typical message processing goes in those 3 steps:
* Unmarshall the javax.jms.Message to the requested type
* Invoke the actual user method (including processing of method
  arguments)
* Send a reply message, if any

Those three steps have been harmonized so that they don't throw a
JMSException anymore. For the later case, introduced
ReplyFailureException as a general exception indicating the
reply message could not have been sent.

Issue: SPR-11778
2014-05-15 16:36:08 +02:00
Juergen Hoeller b0f0d2f289 Polishing 2014-05-15 14:39:06 +02:00
Juergen Hoeller 0728e32e7f ResultSetWrappingSqlRowSet preserves first matching column per name (as defined in ResultSet's javadoc)
Issue: SPR-11786
2014-05-15 14:27:44 +02:00
Juergen Hoeller 551950cdc0 Consistent use of ClassUtils.forName instead of class.getClassLoader().loadClass
Issue: SPR-11780
2014-05-15 09:56:11 +02:00
Brian Clozel f651065b5e Polish 2014-05-14 18:37:31 +02:00
Brian Clozel a072b3f20d Fix RestTemplate documentation for gzip encoding
Prior to this commit, RestTemplate's documentation advised to a
DecompressingHttpClient decorator along with Apache's HttpClient in
order to support gzipped responses.

Since this is now deprecated as of Apache HttpClient 4.3+, this commits
update the documentation with HttpClientBuilder.
2014-05-14 18:04:46 +02:00
Arjen Poutsma eb65a37f4b Provide builder for ResponseEntity
This commit introduces a ResponseEntityBuilder, which allows for
building of a ResponseEntity through a fluent API.

Issue: SPR-11752
2014-05-14 08:26:22 -04:00
Stephane Nicoll 04e6575db6 polishing
This commit adds an explicit error message when the BackOff instance
has returned BackOffExecution#STOP which basically means that no
further attempts are allowed.

This error message is a convenience way to notify that the container is
about to be shut down.

Issue: SPR-11746
2014-05-13 14:44:14 +02:00
Juergen Hoeller 2750ab6734 Undertow-related polishing 2014-05-12 23:14:16 +02:00
Juergen Hoeller b43fc7ac0f Building against Quartz 2.2.1 and JavaMail 1.5.2 2014-05-12 22:31:27 +02:00
Juergen Hoeller b64b308f62 Building against Groovy 2.3 instead of 1.8; updated several dependencies to latest maintenance releases (JasperReports 5.5.2, Tiles 3.0.4, Jetty 9.1.5) 2014-05-12 20:39:07 +02:00
Juergen Hoeller e04fb15a5e Compilation compatibility with JasperReports 5.5.2 2014-05-12 20:34:38 +02:00
Juergen Hoeller 782d10c66f JasperReports-related polishing 2014-05-12 20:27:53 +02:00
Rossen Stoyanchev 2c4cbb617e Add WebSocket scope
This change adds support for a custom "websocket" scope.

WebSocket-scoped beans may be injected into controllers with message
handling methods as well as channel interceptor registered on the
"inboundClientChannel".

Issue: SPR-11305
2014-05-11 12:31:17 -04:00
Rossen Stoyanchev 66c63c374b Ensure handshake attrs are copied in SockJS session 2014-05-09 17:01:37 -04:00
Juergen Hoeller b4d447fc3d isLiteConfigurationCandidate considers @ComponentScan and @ImportResource as indicators as well
Issue: SPR-11769
2014-05-09 21:43:37 +02:00
Stephane Nicoll 89fc3c0257 Add BackOffExecution to isolate state
This commit separates the BackOff configuration from an actual
 execution. BackOffExecution now contains all the state of a
 particular execution and BackOff is only meant to start (i.e.
 create) a new execution.

 The method "reset" has been removed as its no longer necessary:
 when an execution does not need to be used for a given operation
 anymore it can be simply discarded.

 Issue: SPR-11746
2014-05-09 16:39:01 +02:00
Stephane Nicoll 49040a2925 Add back-off attribute to JMS namespace
This commit adds a "back-off" attribute to the jms:listener-container
 element so that a BackOff instance can be provided for users of the
 XML namespace.

 Issue: SPR-11746
2014-05-09 15:05:43 +02:00
Stephane Nicoll 6a0483128a Configurable back off for listener recovery
Prior to this commit, DefaultMessageListenerContainer was recovering
on failure using a fixed time interval, potentially in an infinite way.

This commit adds an extra "backoff" property to the container that
permits to fine tune the recovery interval using a BackOff instance.

FixedBackOff provides a fixed interval between two attempts and a
maximum number of retries. ExponentialBackOff increases an initial
interval until a maximum interval has been reached. A BackOff instance
can return a special "STOP" time value that indicates that no further
attemps should be made. DefaultMessageListenerContainer uses this
value to stop the container.

protected method "sleepInbetweenRecoveryAttempts" has been renamed
to "applyBackOff" and now returns a boolean that indicate if the
back off has been applied and a new attempt should now be made.

Issue: SPR-11746
2014-05-09 11:56:15 +02:00
Rossen Stoyanchev 97fb308b6b Allow use of @SendToUser even w/o authenticated user
Before this change, subscribing to a user destination and use of
@SendToUser annotation required an authenticated user.

This change makes it possible to subscribe to a user destination from
WebSocket sessions without an authenticated user. In such cases the
destination is associated with one session only rather than with a
user (and all their sessions).

It is then also possible to send a message to a user destination
via "/user/{sessionId}/.." rather than "/user/{user}/...".

That means @SendToUser works relying on the session id of the input
message, effectively sending a reply to destination private to the
session.

A key use case for this is handling an exception with an
@MessageExceptionHandler method and sending a reply with @SendToUser.

Issue: SPR-11309
2014-05-08 22:06:57 -04:00
Juergen Hoeller 3c7bb9c279 Fixed Xml/AnnotationBeanConfigurerTests after AbstractBeanConfigurerTests removal 2014-05-08 17:21:26 +02:00
Juergen Hoeller 6c1f8de5fa Eventual removal of deprecated AbstractBeanConfigurerAspect, BeanReferenceFactoryBean and CommonsLogFactoryBean 2014-05-08 16:24:22 +02:00
Juergen Hoeller 8c9116fd4b Additional tests for configuration class importing via ASM
Issue: SPR-11647
2014-05-08 16:22:24 +02:00
Stephane Nicoll 580e52372f Add support of YAML
This commit migrates the YAML support available in Spring Boot to
the core framework. YAML documents can be loaded either as a
properties object or as a map.

Issue: SPR-9897
2014-05-08 12:02:20 +02:00
Stephane Nicoll 7b7fe9aa17 JMS documentation update
This commit updates the documentation of the JMS chapter
with the newly annotated endpoint infrastructure
2014-05-07 12:00:29 +02:00
Sebastien Deleuze 98738c0bbb Avoid ConcurrentModificationException
Removal of cached destination is now moved outside the for loop
that removes subscriptions to avoid ConcurrentModificationException.

Also since updateCache is a LinkedHashMap with accessOrder=true,
a simple access with updateCache.get() modify the map.
By iterating over updateCache.entrySet(), we avoid this update.

Issue: SPR-11755
2014-05-06 20:19:09 -04:00
Rossen Stoyanchev 426b77b834 Support path segment URI var expansion in UrlTag
Before this change UrlTag expanded URI vars and encoded them using
UriUtils.encodePath.

This change makes it possible to expand using
UriUtils.encodePathSegment, which means a "/" is encoded as "%2F".

To expand with path segment semantics, prefix the URI var name "/":

<spring:url value="/url/path/{/var}">
    <spring:param name="var" value="my/Id" />
</spring:url>

Issue: SPR-11401
2014-05-06 19:41:17 -04:00
Rossen Stoyanchev 9d479feadd Add naming strategy for @MVC request mappings.
This change adds a strategy for assigning a default name to an
@RequestMapping controller method. The @RequestMapping annotation
itself now has a name attribute allowing the explicit assignment
of a mapping name.

This is mainly intended for use in EL expressions in views. The
RequestContext class now provides a getMvcUrl method that internally
delegates to MvcUriComponents to look up the handler method.

See the Javadoc of MvcUriComponents.fromMappingName.

Issue: SPR-5779
2014-05-06 15:27:53 -04:00
Juergen Hoeller 381ccde48d IdToEntityConverter defensively handles access to getDeclaredMethods
Issue: SPR-11758
2014-05-06 18:49:16 +02:00
Juergen Hoeller bea34ea41c GenericTypeResolver returns null for entirely unresolvable type arguments only
Issue: SPR-11763
2014-05-06 18:06:26 +02:00
Sam Brannen db5d651057 Exclude Jackson from Jasper Reports dependencies 2014-05-06 17:34:51 +02:00
Stephane Nicoll 43e4dbadfe Revert merge commit e246010df7 2014-05-06 16:30:13 +02:00
Stephane Nicoll e246010df7 Merge pull request #102 from remast/master
* pull102:
  polishing
  Add exception translator for EclipseLink exceptions
2014-05-06 16:05:50 +02:00
Stephane Nicoll e18308851d polishing
Updating pull request for SPR-9541
2014-05-06 16:04:39 +02:00
Jan Stamer 371e3a7ac0 Add exception translator for EclipseLink exceptions
Issue: SPR-9541
2014-05-06 16:01:51 +02:00
Stephane Nicoll 658f7f58df Add missing @since tag 2014-05-05 15:45:45 +02:00
Rossen Stoyanchev ae30a60d03 Relax websocket xsd attr types for ${} style props
Issue: SPR-11624
2014-05-02 15:17:04 -04:00
Brian Clozel 5a1b7c6ce7 Update IDEA guidelines
* propdeps-plugin is now in version 0.0.6
* Using IntelliJ 13.1
2014-05-02 12:27:46 +02:00
Stephane Nicoll 6551aa61fa Fix typo
Issue: SPR-11571
2014-05-02 09:57:00 +02:00
Rossen Stoyanchev cb2834c10a Fix compile error 2014-05-01 13:40:23 -04:00
Rossen Stoyanchev 8e1a5b9999 Merge pull request #535 from matsev/SPR-11749 2014-05-01 13:38:53 -04:00
Mattias Severson f2991da6d6 Made string constants in HttpHeaders public
Issue: SPR-11749
2014-05-01 13:37:39 -04:00
Rossen Stoyanchev 676282c66e Support ListenableFuture on @RequestMapping methods
Issue: SPR-11695
2014-05-01 12:28:59 -04:00
Rossen Stoyanchev 0d2aa51576 Update ContentNegotiationManager for unknown path exts
This change refines the logic of "mapping" content negotiation
strategies with regards to how to handle cases where no mapping is
found.

The request parameter strategy now treats request parameter values that
do not match any mapped media type as 406 errors.

The path extension strategy provides a new flag called
"ignoreUnknownExtensions" (true by default) that when set to false also
results in a 406. The same flag is also exposed through the
ContentNegotiationManagerFactoryBean and the MVC Java config.

Issue: SPR-10170
2014-05-01 12:03:06 -04:00
Rossen Stoyanchev c50887c877 Merge pull request #487 from cloudmark/SPR-11506 2014-05-01 10:36:54 -04:00
Rossen Stoyanchev 9598a1e2ef Update @SendToUser and related code
Issue: SPR-11506
2014-05-01 10:35:35 -04:00
Mark Galea 088b80f4c5 Add singleSession attribute to @SendToUser
Added the ability to send a message only to one user session. Given a
user has two tabs open and the client sends a message to the server
from tab 1, it is now possible to send a reply message to only 1 tab
instead of the default mode of targetting all known user sessions.

Issue: SPR-11506
2014-05-01 10:33:14 -04:00
Juergen Hoeller a653c06711 Fixed invalid code tag in javadoc
(cherry picked from commit a139670)
2014-05-01 00:53:10 +02:00
Juergen Hoeller a842b933a9 Upgraded to propdeps plugin 0.0.6 2014-04-30 22:51:44 +02:00
Juergen Hoeller 842a8a851d AnnotationMetadataReadingVisitor passes metaAnnotationMap into getMergedAnnotationAttributes algorithm, for finding out about applicable overrides
Issue: SPR-11649
2014-04-30 22:51:36 +02:00
Juergen Hoeller 39e1342302 Revised definition of @UsesX annotations 2014-04-30 22:51:22 +02:00
Rossen Stoyanchev 5dcd55b23b Log ignored CONNECT messages at error level
Issue: SPR-11673
2014-04-30 11:11:43 -04:00
Rossen Stoyanchev d52f07aa1b Log STOMP ERROR frames at error level
Issue: SPR-11673
2014-04-30 11:00:27 -04:00
Rossen Stoyanchev 1958ae3a62 Update log level
Issue: SPR-11673
2014-04-30 10:26:51 -04:00
Rossen Stoyanchev 209bb4ee4b Fix Javadoc typo 2014-04-30 10:06:57 -04:00
Stephane Nicoll bd85c916eb Integrate animal sniffer
Animal sniffer provides tools to assist verifying that classes
compiled with a newer JDK are compatible with an older JDK.

This integratesthe latest version of the tool (1.11) that
permits the use of custom annotations. Added @UsesJava7,
@UsesJava8 and @UsesSunHttpServer and annotated the few places
where we rely on a specific environment.

The verification process can be invoked by running the 'sniff'
task.

Issue: SPR-11604

polishing
2014-04-30 13:51:01 +02:00
Juergen Hoeller e84f61bb9d Upgraded to Gradle 1.12 and updated several dependencies to latest maintenance releases 2014-04-30 13:44:06 +02:00
Juergen Hoeller adb616b67b JRubyScriptUtils defensively handles null Nodes in findClassNode (fails against JRuby 1.7.12 otherwise)
Issue: SPR-11747
2014-04-30 13:40:11 +02:00
Juergen Hoeller c124d3b6c6 Spring Framework build uses Reactor 1.1 GA now, getting rid of last remaining pre-GA dependency
(cherry picked from commit 27b2ed1)
2014-04-30 12:00:16 +02:00
Stephane Nicoll 90081f787f Fix Cacheable javadoc
This commits fixes a confusing phrasing of Cacheable javadoc that
mentioned explicitly that the method signature is used to compute the
key for the cache.

Issue: SPR-11736
2014-04-30 11:40:54 +02:00
Rossen Stoyanchev 6a9b229249 Split destination cache into access vs update
Issue: SPR-11657
2014-04-29 22:15:18 -04:00
Rossen Stoyanchev c879796e00 Add cache limit to DefaultSessionRegistry and polish
Issue: SPR-11657
2014-04-29 18:16:09 -04:00
Sebastien Deleuze 1054080b24 Fix DefaultSubscriptionRegistry when using pattern destinations
DestinationCache is now used for both plain and pattern
destinations. It stores internally the subscriptions map for each
cached destination. Subscriptions are initially created when there
is no cache for the requested destination, and are updated when
subscriptions change.

Issue: SPR-11657
2014-04-29 18:16:09 -04:00
Juergen Hoeller 02aca9c754 Polishing 2014-04-30 00:01:07 +02:00
Rossen Stoyanchev 58adc150c9 Fix STOMP broker relay int test issue
Avoid dependency on order in which expected messages are received.
2014-04-29 17:17:10 -04:00
Rossen Stoyanchev 56d26443e2 Fix one more issue in STOMP broker relay int test 2014-04-29 16:36:43 -04:00
Rossen Stoyanchev 13be6d2321 Fix issue in STOMP broker relay integration test 2014-04-29 15:48:39 -04:00
Rossen Stoyanchev 01fe2923ee Simplify STOMP broker relay integration test
This change simplifies the implementation of the "test" EventPublisher
and MessageHandler used in the STOMP broker relay integration tests.
The updated implementations use a time-limted poll on a BlockingQueue.
2014-04-29 13:22:23 -04:00
Juergen Hoeller dc2e62fab9 Polishing 2014-04-29 19:07:45 +02:00
Juergen Hoeller 1b2014a52d AbstractAutoProxyCreator and AbstractAdvisingBeanPostProcessor consistently not considering configuration callbacks and internal language interfaces as reasonable proxy interfaces
Issue: SPR-11715
2014-04-29 19:07:29 +02:00
Juergen Hoeller e510f6393a Polishing 2014-04-29 15:45:56 +02:00
Juergen Hoeller a0658c5832 AbstractAdvisingBeanPostProcessor uses target class check for existing proxy but checks against actual exposed object otherwise (catching introductions)
Issue: SPR-11725
2014-04-29 15:44:55 +02:00
Rossen Stoyanchev 26a9c4bc01 Polish STOMP documentation 2014-04-28 22:35:10 -04:00
Chexpir 77a250ef92 Update RequestMapping javadoc typo. 2014-04-28 22:13:17 -04:00
Sebastien Deleuze 59a050f3b9 Include more guidance about DispatcherServlet context
Also update <mvc:annotation-driven /> comment in XSD.

Issue: SPR-11703
2014-04-28 22:07:55 -04:00
Rossen Stoyanchev 86785f340e Document STOMP-related ApplicationContext events
Issue: SPR-11672
2014-04-28 22:02:45 -04:00
Juergen Hoeller be5f2a8b4e Polishing 2014-04-28 23:37:07 +02:00
Juergen Hoeller ab24dda4ff Revised @PropertySource parsing for consistent PropertySource naming, avoiding accidental overriding by name
Issue: SPR-11637
2014-04-28 23:24:57 +02:00
Juergen Hoeller ce4912b627 AspectJExpressionPointcut defensively handles fallback expression parsing
Issue: SPR-9335
2014-04-28 21:49:36 +02:00
Juergen Hoeller 8e6e6c22af Test for base package specified as config location
Issue: SPR-11647
2014-04-28 21:31:38 +02:00
Juergen Hoeller 299776fd80 Test for overriding singleton @Bean method with scoped proxy in configuration subclass
Issue: SPR-10744
2014-04-28 14:02:02 +02:00
Juergen Hoeller 0ef29b204b GenericBeanDefinition's toString() takes parent definition into account
Issue: SPR-9671
2014-04-28 13:59:51 +02:00
Juergen Hoeller 6f2e61b19f Polishing
(cherry picked from commit c97c246)
2014-04-28 00:47:50 +02:00
Juergen Hoeller 63733c4805 AbstractMarshaller avoids SAXSource workaround when processExternalEntities=true
Issue: SPR-11737
(cherry picked from commit 93d13cf)
(cherry picked from commit 4196e6c)
2014-04-28 00:47:39 +02:00
Juergen Hoeller 6cb45f714e General defensiveness about the bootstrap ClassLoader (i.e. null ClassLoader)
Issue: SPR-11721
(cherry picked from commit 59cef3c)
2014-04-28 00:47:21 +02:00
Juergen Hoeller ce1954da1e Configuration class processing explicitly skips java.* classes since we'll never find @Bean annotations there anyway
Issue: SPR-11718
(cherry picked from commit c8ea80d)
2014-04-28 00:33:14 +02:00
Juergen Hoeller 310bdbcb15 @Bean processing explicitly ignores bridge methods (for method overrides with return type narrowing on JDK 8)
Issue: SPR-11718
(cherry picked from commit 656fc52)
2014-04-28 00:33:04 +02:00
Rossen Stoyanchev 1f630a5fb9 Add CachingResourceResolver 2014-04-25 16:49:49 -04:00
Rossen Stoyanchev e24b876164 Remove "Public" from the ResourceUrlProvider name
Also respect HandlerMapping order in ResourceUrlProvider
2014-04-25 16:49:32 -04:00
Rossen Stoyanchev 70fd33236f Upgrade to reactor 1.1 RC1 2014-04-25 12:27:36 -04:00
Rossen Stoyanchev 1200755125 Fix issue with subscribe destination
The original fix for SPR-11423:
https://github.com/spring-projects/spring-framework/commit/32e5f57e647022d9ea82c03670936bf31f8467de

was insufficient when using an external broker since the original
destination header has to be in the "native headers" map (i.e. with
STOMP headers) in order to be included in messages broadcast by
the broker.
2014-04-25 12:26:04 -04:00
Brian Clozel 564a555619 Fix NPE in PrefixResourceResolver
The ResourceResolverChain can return null, so PrefixResourceResolver
should be skipped in that case.
2014-04-25 15:15:53 +02:00
Stephane Nicoll c319b5fa82 Merge pull request #530 from panchenko/master
* pull530:
  Add space separator in JsonPathExpectationsHelper
2014-04-25 14:52:32 +03:00
Alex Panchenko 09341b996e Add space separator in JsonPathExpectationsHelper 2014-04-25 18:04:54 +07:00
Rossen Stoyanchev f5cbd9aa4a Switch to vararg for configuring ResourceResolver's 2014-04-25 00:01:24 -04:00
Stephane Nicoll 6aa16b745f polishing
This commit removes the queue attribute of the JmsListener annotation
as this information should be provided by the container factory and not
by each individual listener endpoints.

There was a side effect that an annotation value cannot be null, which
was forcing the container to be a queue-based container by default.

Issue: SPR-9882
2014-04-24 16:56:51 +03:00
Brian Clozel c4843577ba Fix PrefixResourceResolver implementation
The configured prefix should not begin with a "/", since
PublicResourceUrlProvider is already taking path mapping into account
when resolving resources and URLs.
2014-04-24 14:43:26 +02:00
Stephane Nicoll bded025d9f @SendTo support for jms listener endpoints
This commit replaces the "responseDestination" attribute on the
JmsListener annotation by a support of the standard SendTo annotation.

Issue: SPR-11707
2014-04-24 15:33:24 +03:00
Stephane Nicoll 4b0aba63df Default JmsListenerContainerFactory lookup
Prior to this commit, the default JmsListenerContainerFactory to use
must be explicitly set. Since having a single container factory is a
fairly common use case, we look up the default one automatically
using the bean name "jmsListenerContainerFactory".

It is still possible to provide an explicit default but since it refers
more to "the" container factory to use, the parameter has been
renamed to "containerFactory" which is shorter and more explicit.

The lookup strategy is lazy: if all endpoints are providing an
explicit container factory and no container factory with the
"jmsListenerContainerFactory" bean name exists, no exception
will be thrown.

Issue : SPR-11706
2014-04-24 10:41:03 +03:00
Rossen Stoyanchev 08f0395033 Rename Reactor support classes
This change modifies the names of the Reactor support classes in order
to align with the same changes in the 4.0.x line which now supports
both Reactor 1.1 and 1.0.

Issue: SPR-11636
2014-04-23 23:46:43 -04:00
Juergen Hoeller 9353332a61 Polishing 2014-04-23 23:55:43 +02:00
Juergen Hoeller c05ab3e2e8 General defensiveness about the bootstrap ClassLoader (i.e. null ClassLoader)
Issue: SPR-11721
2014-04-23 23:54:55 +02:00
Juergen Hoeller ec7d80b851 Polishing 2014-04-23 23:03:47 +02:00
Juergen Hoeller 21f9ca088d ServletServerHttpResponse specifically checks for Servlet 3.0's HttpServletResponse.getHeader(String) method
Issue: SPR-11712
2014-04-23 22:47:54 +02:00
Rossen Stoyanchev b1158aa913 Polish logging in resource handling 2014-04-23 15:43:39 -04:00
Juergen Hoeller 9cb5f48459 SpringProperties falls back to ClassLoader.getSystemResource when loaded in bootstrap ClassLoader
Issue: SPR-11721
2014-04-23 18:18:46 +02:00
Juergen Hoeller 794e859e68 checkNotModified leniently handles IE-10-style If-Modified-Since values and silently proceeds if header value cannot be parsed at all
Issue: SPR-11727
2014-04-23 18:16:43 +02:00
Juergen Hoeller 5b47816183 DispatcherServlet skips multipart resolution if error page exception attribute indicates MultipartException as root cause
Issue: SPR-11716
2014-04-23 14:36:14 +02:00
Juergen Hoeller fb66567bba Final patches from ASM 5.0.2 release
Issue: SPR-11212
2014-04-23 14:25:50 +02:00
Sam Brannen 6b20439202 Avoid deprecation warnings in ValidatorFactoryTests
ValidatorFactoryTests now uses Bean Validation 1.1 APIs where
appropriate.
2014-04-22 23:48:20 -04:00
Juergen Hoeller 3f392e32f5 ShallowEtagHeaderFilter writes body early on sendError/sendRedirect and interprets setContentLength/setBufferSize as a hint for capacity increase
Issue: SPR-11705
Issue: SPR-11717
2014-04-22 23:14:48 +02:00
Juergen Hoeller 700c3b257f Polishing 2014-04-22 20:48:26 +02:00
Juergen Hoeller 5ab7076118 considerInterfaces should work without considerInherited as well
Issue: SPR-11719
2014-04-22 20:47:33 +02:00
Juergen Hoeller 945335d14b AnnotationTypeFilter prevents ASM-based loading of java.* interfaces as well
Issue: SPR-11719
2014-04-22 20:46:36 +02:00
Rossen Stoyanchev 10e1a80540 Update Javadoc for PublicResourceUrlProvider 2014-04-22 11:34:46 -04:00
Sam Brannen 3d18cfeab6 Polish static resource handling mechanism
- ResourceResolver and ResourceResolverChain now have a consistent API
  with regard to method names and terminology.

- ResourceResolver and ResourceResolverChain now accept
  List<? extends Resource> instead of List<Resource> for simplified
  programmatic use.

- Improved Javadoc across the package.

- Formatted code to align with standards.

- Removed all references to ResourceUrlPathTranslator.

Issue: SPR-10933
2014-04-22 10:48:12 -04:00
Phillip Webb cd13b4882c Merge pull request #513 from HejiaHo/master
* pull513:
  Fix ConcurrentReferenceHashMap max constraints
2014-04-22 14:58:19 +01:00
HejiaHo 16410cad4d Fix ConcurrentReferenceHashMap max constraints
Update calculateShift to respect the maximum upper bound as well as the
minimum value.

Issue: SPR-11720
2014-04-22 14:56:05 +01:00
Stephane Nicoll 5559209233 Remove useless imports 2014-04-22 09:57:12 +02:00
Stephane Nicoll 36456f0f62 Merge pull request #527 from marschall/spring-context-support-warnings
* spring-context-support-warnings:
  Clean up spring-context-support tests warnings
2014-04-22 07:32:20 +02:00
Philippe Marschall 1e8c799a60 Clean up spring-context-support tests warnings
Clean up compiler warnings in the tests of spring-context-support.

This commit:
* adds type parameters to all the types except `Cache` (mostly `List`
  and `Map`)
* removes unused imports
2014-04-22 07:32:11 +02:00
Stephane Nicoll 21de937b2f Merge pull request #526 from marschall/spring-jms-test-warnings
* spring-jms-test-warnings:
  Clean up spring-jms tests warnings
2014-04-22 07:31:53 +02:00
Philippe Marschall 404eb48f95 Clean up spring-jms tests warnings
Clean up compiler warnings in the tests of spring-jms. This commit
adds type parameters to all the types (mostly `List` and `Map`).

I am not too sure about the `Map` type parameters in
`MessageContentsDelegate` and `ResponsiveMessageDelegate` however the
respective methods seem unused.
2014-04-22 07:31:43 +02:00
Stephane Nicoll 7cbfc4d2f8 Merge pull request #525 from marschall/spring-tx-test-warnings
* spring-tx-test-warnings:
  Clean up spring-tx tests warnings
2014-04-22 07:31:22 +02:00
Philippe Marschall 4ff1e197e3 Clean up spring-tx tests warnings
Clean up compiler warnings in the tests of spring-tx. This commit
adds type parameters to all the types (mostly `List` and `Map`). In
addition it uses Java 5 autoboxing to get rid of several `new Integer`
(except in cases where it's needed).

After this commit the only warnings in spring-tx left are in
`TransactionAttributeSourceTests`` that code would never compile with
generics.
2014-04-22 07:31:12 +02:00
Stephane Nicoll 917e044e54 Merge pull request #524 from marschall/spring-web-warnings
* spring-web-warnings:
  Clean up spring-web tests warnings
2014-04-22 07:30:52 +02:00
Philippe Marschall d00d0b0274 Clean up spring-web tests warnings
Clean up compiler warnings in the tests of spring-web. This commit
adds type parameters to all the types (mostly `List` and `Map`).

After this commit the only warnings in spring-web left are in
* `MockExpressionEvaluator` this can't be fixed until JSP-EL is fixed
* `Jackson2ObjectMapperFactoryBeanTests#testSetModules` that code
   would never compile with generics.
2014-04-22 07:30:42 +02:00
Stephane Nicoll b3492f61c2 Merge pull request #523 from marschall/spring-expression-warnings
* spring-expression-warnings:
  Clean up spring-expression tests warnings
2014-04-22 07:30:24 +02:00
Philippe Marschall 84f6d17c7b Clean up spring-expression tests warnings
Clean up compiler warnings in the tests of spring-expression. This
commit adds type parameters to some of the types (mostly `List` and
`Map`). Some of them can't be cleaned up, some tests are even
specifically for raw types.
2014-04-22 07:30:17 +02:00
Stephane Nicoll 54db2fba48 Merge pull request #522 from marschall/spring-webmvc-portlet-warnings
* spring-webmvc-portlet-warnings:
  Clean up spring-webmvc-portlet tests warnings
2014-04-22 07:29:56 +02:00
Philippe Marschall 5ae1c8a242 Clean up spring-webmvc-portlet tests warnings
Clean up compiler warnings in the tests of spring-webmvc-portlet. This
commit adds type parameters to all the types (mostly `List` and `Map`).

After this commit the only warnings in spring-web left are the
subclasses of `MyCommandProvidingFormController`.
2014-04-22 07:29:48 +02:00
Stephane Nicoll 75d4512326 Merge pull request #521 from marschall/spring-websocket-imports
* spring-websocket-imports:
  Clean up spring-websocket imports
2014-04-22 07:29:03 +02:00
Philippe Marschall e98d3f790b Clean up spring-websocket imports
spring-websocket is full of unused imports. This commit removes all
unused imports from spring-websocket.
2014-04-22 07:28:56 +02:00
Stephane Nicoll c15997340b Merge pull request #520 from marschall/spring-messaging-imports
* spring-messaging-imports:
  Clean up spring-messaging imports
2014-04-22 07:28:20 +02:00
Philippe Marschall eb5f397a39 Clean up spring-messaging imports
spring-messaging is full of unused imports. This commit removes all
unused imports from spring-messaging.
2014-04-22 07:28:10 +02:00
Stephane Nicoll 9ca9363c2f Merge pull request #519 from marschall/spring-aop-warnings
* spring-aop-warnings:
  Clean up spring-aop tests warnings
2014-04-22 07:27:53 +02:00
Philippe Marschall fc3df29b2e Clean up spring-aop tests warnings
Clean up compiler warnings in the tests of spring-aop. This commit
adds type parameters to all the types.
2014-04-22 07:27:43 +02:00
Stephane Nicoll e014608e99 Merge pull request #518 from marschall/spring-jdbc-warning
* spring-jdbc-warning:
  Clean up spring-jdbc tests warnings
2014-04-22 07:27:24 +02:00
Philippe Marschall 7a7be598c2 Clean up spring-jdbc tests warnings
Clean up compiler warnings in the tests of spring-jdbc. This commit
adds type parameters to all the types (mostly `List` and `Map`). In
addition it uses Java 5 autoboxing to get rid of all of the following

 * new Integer
 * new Long
 * new Float
 * new Double
 * new Boolean
 * new String

This should be unnoticeably faster since interning can be uses for
Integer and such.

After this commit the only warnings in spring-jdbc left are:

* raw type warning in `MapDataSourceLookupTests`, that code would never
  compile with generics
* deprecation warning for `#queryForInt` and `#queryForLong`
2014-04-22 07:18:04 +02:00
Sam Brannen 408fe42df2 Fix Javadoc syntax error in HttpRequestHandler 2014-04-22 01:01:07 -04:00
Sam Brannen 5365171f24 Fix Javadoc formatting in ResourceHttpRequestHandler 2014-04-22 00:53:34 -04:00
Juergen Hoeller 66bae99d86 EQ/NE do not fail in case of a Comparable mismatch and apply a specific CharSequence equality check
Issue: SPR-11708
2014-04-21 23:29:40 +02:00
Juergen Hoeller 566e0fb317 AspectJ 1.8 final used in build and also declared as recommended user version now
Issue: SPR-11212
2014-04-21 21:22:12 +02:00
Sam Brannen e1720d89fc Don't mutate annotation metadata when merging attrs
Prior to this commit, invoking the getMergedAnnotationAttributes()
method in AnnotationReadingVisitorUtils resulted in mutation of the
internal state of the ASM-based annotation metadata supplied to the
method.

This commit fixes this issue by making a copy of the original
AnnotationAttributes for the target annotation before merging attribute
values from the meta-annotation hierarchy.

This commit also introduces a slight performance improvement by
avoiding duplicate processing of the attributes of the target
annotation.

Issue: SPR-11710
2014-04-21 12:46:38 -04:00
Sam Brannen a0b6175d78 Update doc for DefaultAopProxyFactory re: CGLIB 2014-04-21 11:11:14 -04:00
Stephane Nicoll 50c5cc0bd2 polishing
JmsLister is intended to be available on method or as a
meta-annotation.

Issue: SPR-9882
2014-04-18 12:08:33 +02:00
Phillip Webb 3a5de00c41 Fixup .mailmap file
Fix a few issues with the `.mailmap` file.
2014-04-18 11:00:15 +01:00
Phillip Webb 1697cd15b2 Merge pull request #516 from GrahamLea/mailmap
* mailmap:
  Added a Git .mailmap file
2014-04-18 09:49:16 +01:00
Graham Lea a5e1932c2c Added a Git .mailmap file 2014-04-18 09:48:48 +01:00
Juergen Hoeller 086f1eda71 Polishing 2014-04-17 22:39:47 +02:00
Juergen Hoeller 48c977afdc Polishing 2014-04-17 21:19:47 +02:00
Juergen Hoeller 28887750b0 MessageConversionException offers constructor without cause argument now, plus related polishing
Issue: SPR-11653
2014-04-17 21:19:37 +02:00
Juergen Hoeller 47c57a84c5 Hessian 4.0.7 (May 2010) superseded by 4.0.33 (Dec 2012) as new baseline
Issue: SPR-11396
2014-04-17 20:59:29 +02:00
Juergen Hoeller 4612559446 Automated tests against Hibernate ORM 4.3 and Hibernate Validator 5
Issue: SPR-11704
2014-04-17 16:24:10 +02:00
Stephane Nicoll 713dd60fa7 JMS annotation-driven endpoints.
This commit adds the support of JMS annotated endpoint. Can be
activated both by @EnableJms or <jms:annotation-driven/> and
detects methods of managed beans annotated with @JmsListener,
either directly or through a meta-annotation.

Containers are created and managed under the cover by a registry
at application startup time. Container creation is delegated to a
JmsListenerContainerFactory that is identified by the containerFactory
attribute of the JmsListener annotation. Containers can be
retrieved from the registry using a custom id that can be specified
directly on the annotation.

A "factory-id" attribute is available on the container element of
the XML namespace. When it is present, the configuration defined at
the namespace level is used to build a JmsListenerContainerFactory
that is exposed with the value of the "factory-id" attribute. This can
be used as a smooth migration path for users having listener containers
defined at the namespace level. It is also possible to migrate all
listeners to annotated endpoints and yet keep the
<jms:listener-container> or <jms:jca-listener-container> element to
share the container configuration.

The configuration can be fine-tuned by implementing the
JmsListenerConfigurer interface which gives access to the registrar
used to register endpoints. This includes a programmatic registration
of endpoints in complement to the declarative approach. A default
JmsListenerContainerFactory can also be specified to be used if no
containerFactory has been set on the annotation.

Annotated methods can have flexible method arguments that are similar
to what @MessageMapping provides. In particular, jms listener endpoint
methods can fully use the messaging abstraction, including convenient
header accessors. It is also possible to inject the raw
javax.jms.Message and the Session for more advanced use cases. The
payload can be injected as long as the conversion service is able to
convert it from the original type of the JMS payload. By
default, a DefaultJmsHandlerMethodFactory is used but it can be
configured further to support additional method arguments or to
customize conversion and validation support.

The return type of an annotated method can also be an instance of
Spring's Message abstraction. Instead of just converting the payload,
such response type allows to communicate standard and custom headers.

The JmsHeaderMapper infrastructure from Spring integration has also
been migrated to the Spring framework. SimpleJmsHeaderMapper is based
on SI's DefaultJmsHeaderMapper. The simple implementation maps all
JMS headers so that the generated Message abstraction has all the
information stored in the protocol specific message.

Issue: SPR-9882
2014-04-17 09:16:06 +02:00
Rossen Stoyanchev 6cb9a144db Expand static resource handling mechanism
An initial commit with expanded support for static resource handling:

- Add ResourceResolver strategy for resolving a request to a Resource
  along with a few implementations.

- Add PublicResourceUrlProvider to get URLs for client-side use.

- Add ResourceUrlEncodingFilter and
  PublicResourceUrlProviderExposingInterceptor along with initial
  MVC Java config support.

Issue: SPR-10933
2014-04-16 23:42:44 -04:00
Juergen Hoeller f5cce14fe7 XStreamMarshaller supports custom NameCoder strategy
Issue: SPR-11702
2014-04-16 23:08:32 +02:00
Juergen Hoeller 53162e78e4 WebSocket intro correctly points to annotations section
Issue: SPR-11667
2014-04-16 18:07:25 +02:00
Juergen Hoeller 90309ab0b5 CglibAopProxy detects package-visible methods when defined in a different ClassLoader
Issue: SPR-11618
2014-04-16 18:07:10 +02:00
Juergen Hoeller 7bc2168aa7 Default use of target 1.8 for Spring test compilation
Issue: SPR-11699
2014-04-16 12:54:20 +02:00
Juergen Hoeller 637ad20cd3 Latest ASM patches (fix for bug 317151 from ASM HEAD; ASM's 1.8 bytecode processing needs to be lenient with CGLIB 3.1)
Issue: SPR-11212
2014-04-16 12:35:26 +02:00
Stephane Nicoll 49e3c2a0c7 Merge pull request #514 from bijukunjummen/cpdocfix
* cpdocfix:
  Minor doc fix to sample for @ConstructorProperties
2014-04-15 18:11:11 +02:00
Biju Kunjummen 973780efd5 Minor doc fix to sample for @ConstructorProperties 2014-04-15 18:10:46 +02:00
Juergen Hoeller 9a59292603 SchedulerFactoryBean supports non-durable jobs when accompanied by a trigger
Issue: SPR-11689
2014-04-15 15:24:49 +02:00
Stephane Nicoll 397aa82984 Fix expression cache
Prior to this commit, only the java.lang.reflect.Method was used to
identify an annotated method. As a result, if different annotations
were placed on different methods (method overriding, interface
implementation) only the first one (cached) was used.

LazyParamAwareEvaluationContext was affected by the exact
same problem and has been also fixed.

Issue: SPR-11692
2014-04-15 09:14:41 +02:00
Rossen Stoyanchev 4e1781ae8c Use strategy for customizing ID/TIMESTAMP generation
Issue: SPR-11468
2014-04-14 21:03:13 -04:00
Rossen Stoyanchev 95e25d4e6f Add strategy for customizing ID/TIMESTAMP headers
Introduce MessageHeaderInitializer strategy for initializing a
MessageHeaderAccessor.

Add IdTimestampMessageHeaderInitializer implementation that provides
control over ID and timestamp header generation.

Disable ID and timestamp by default in SimpMessageHeaderAccessor and
therefore its sub-class StompHeaderAccessor.

Issue: SPR-11468
2014-04-14 13:52:55 -04:00
Sam Brannen 09248a0b37 Polish ResourceDatabasePopulatorTests 2014-04-14 10:54:33 +02:00
Rossen Stoyanchev 65b17b80ba Switch BufferingStompDecoder to decoration
The BufferingStompDecoder now decorates rather than extend
StompDecoder. This allows a single StompDecoder instance to be
configured and extended independantly while buffering remains a
separate concern.
2014-04-13 21:35:22 -04:00
Rossen Stoyanchev 1369ff4d84 Merge pull request #512 from rstoyanchev/message-headers
Enhance MessageHeaderAccessor support and optimize message creation
2014-04-13 18:52:05 -04:00
Rossen Stoyanchev fda9c633c4 Support mutable headers in MessagingTemplate
AbstractMessageConverter and messaging template implementations now
detect and use mutable headers if passed in.

The SimpMessagingTemplate is optimized to supporting using a single
MessageHeaders instance while preparing a message.

This commit also updates code using the SimpMessagingTemplate to take
advantage of its new capabilities.

Issue: SPR-11468
2014-04-13 18:43:25 -04:00
Rossen Stoyanchev ae942ffdb8 Make use of enhanced MessageHeaderAccessor support
Mutate rather than re-create headers when decoding STOMP messages
before a message is sent on a message channel.

Use MessageBuilder.createMessage to ensure the fully prepared
MessageHeaders is used directly MessageHeaderAccessor instance.

Issue: SPR-11468
2014-04-13 18:43:25 -04:00
Rossen Stoyanchev 4867546aec Enhance MessageHeaderAccessor support
Refine semantics of ID and TIMESTAMP headers provided to protected
MessageHeaders constructor.

Refactor internal implementation of MessageHeaderAccessor.

Support mutating headers from a single thread while a message is being
built (e.g. StompDecoder creating message + then adding session id).

Improve immutablity in NativeMessageHeaderAccessor and in
StompHeaderAccessor.

Optimize object creation for initializing messages and subsequent
accessing their headers.

Introduce MessageHeaderAccessorFactory support to enable applying a
common strategies for ID and TIMESTAMP generation to every message.

Add MessageBuilder shortcut factory method for creating messages from
payload and a full-prepared MessageHeaders instance. Also add
equivalent constructors to GenericMessage and ErrorMessage.

Issue: SPR-11468
2014-04-13 18:43:25 -04:00
Sam Brannen b4995f7e4f Add script after setting scripts in RsrcDbPopultr
Prior to this commit it was impossible to add additional scripts to a
ResourceDatabasePopulator after setScripts() had been invoked.

This commit fixes this by ensuring that the internal scripts list
continues to be modifiable when setScripts() is invoked.

Issue: SPR-11691
2014-04-13 15:06:47 +02:00
Sam Brannen 84f678a06b Assert preconditions in ResourceDatabasePopulator
Prior to this commit ResourceDatabasePopulator did not assert any
preconditions for constructor and method arguments. Consequently,
errors would not be encountered until the populator was executed.

This commit addresses this issue by ensuring that preconditions for
constructor and method arguments are asserted immediately, throwing
IllegalArgumentExceptions where appropriate.

Issue: SPR-11690
2014-04-13 14:51:42 +02:00
Sam Brannen 8fecee8c8a Improve Javadoc in SQL script support classes 2014-04-13 12:31:07 +02:00
Sam Brannen 910dd41467 Polish AbstractDatabasePopulatorTests 2014-04-11 18:43:36 +02:00
Sam Brannen 1753f5d1ca Make EOF_STATEMENT_SEPARATOR XML friendly
Changed the value of ScriptUtils.EOF_STATEMENT_SEPARATOR from
"<<< END OF SCRIPT >>>" to "^^^ END OF SCRIPT ^^^" so that the angle
brackets do not have to be escaped in XML configuration files.

Issue: SPR-11687
2014-04-11 18:19:21 +02:00
Sam Brannen cc0ae3a881 Support EOF as statement separator in SQL scripts
Prior to Spring Framework 4.0.3, it was possible to supply a bogus
statement separator (i.e., a separator string that does not exist in
the configured SQL scripts) to ResourceDatabasePopulator with the side
effect that the entire contents of a script file would be interpreted
as a single SQL statement.

This undocumented feature was never intentional; however, some
developers came to rely on it. Unfortunately, changes made in
conjunction with SPR-9531 and SPR-11560 caused such scenarios to no
longer work.

This commit introduces first-class support for executing scripts which
contain a single statement that spans multiple lines but is not
followed by an explicit statement separator.

Specifically, ScriptUtils.EOF_STATEMENT_SEPARATOR may now be specified
as a 'virtual' statement separator to denote that a script contains a
single statement and no actual separator.

Issue: SPR-11687
2014-04-11 17:44:27 +02:00
Sam Brannen 2a937a369e Polish Javadoc in SimpleApplicationEventMulticaster 2014-04-11 15:57:21 +02:00
Juergen Hoeller 49916bb7eb SimpleApplicationEventMulticaster supports a configurable ErrorHandler strategy
Issue: SPR-11551
2014-04-10 23:06:25 +02:00
Juergen Hoeller 14f9dbf70c Consistent documentation for both 'close' and 'shutdown' as inferred destroy methods
Issue: SPR-11680
2014-04-10 22:52:22 +02:00
Sam Brannen ba3c7e588f Polish Javadoc for SpringJUnit4ClassRunner 2014-04-10 18:28:36 +02:00
Juergen Hoeller 3fef35879d JDBC 3.0 related javadoc overhaul
Issue: SPR-11600
2014-04-10 15:52:26 +02:00
Juergen Hoeller 37679384e8 JdbcUtils uses JDBC 4.1 getObject(int, Class) for unknown ResultSet value types
Comes with general JDBC 3.0+ baseline upgrade, removing defensive measures.

Issue: SPR-11600
2014-04-10 15:51:44 +02:00
Sam Brannen 1bbc55d63c Polish Javadoc for TestContextBootstrapper 2014-04-10 01:09:51 +02:00
Sam Brannen 5df60a2bec Update ContextLoaderUtils Javadoc due to recent refactoring 2014-04-10 00:56:23 +02:00
Sam Brannen e1a1e1205f Simplify DefaultBootstrapContext.toString() 2014-04-10 00:44:14 +02:00
Sam Brannen b308659cda Merge from sbrannen/SPR-9955
* SPR-9955:
  Introduce context bootstrap strategy in the TCF
2014-04-10 00:36:02 +02:00
Sam Brannen a281bdbfc5 Introduce context bootstrap strategy in the TCF
Work done in conjunction with SPR-5243 and SPR-4588 introduced physical
package cycles in the spring-test module. The work performed in
conjunction with SPR-9924 uses reflection to resolve these physical
package cycles; however, prior to this commit the logical package
cycles still remain.

Furthermore, over time it has become apparent that the Spring
TestContext Framework (TCF) could better serve application developers
and especially third-party framework developers by providing a more
flexible mechanism for "bootstrapping" the TCF. For example, prior to
this commit, default TestExecutionListeners could only be registered by
subclassing TestContextManager (and SpringJUnit4ClassRunner if using
JUnit). Similarly, the default ContextLoader could only be set by
subclassing SpringJUnit4ClassRunner for JUnit and by copying and
modifying AbstractTestNGSpringContextTests for TestNG.

This commit addresses the aforementioned issues by introducing a
bootstrap strategy in the TestContext framework that is responsible for
determining default TestExecutionListeners and the default
ContextLoader in an extensible fashion. The new TestContextBootstrapper
SPI also provides a mechanism for supporting various types of
MergedContextConfiguration depending on the feature set of the context
loaders supported by the strategy.

The following provides an overview of the most significant changes in
this commit.

 - Introduced TestContextBootstrapper strategy SPI, BootstrapContext,
   and @BootstrapWith.

 - Introduced AbstractTestContextBootstrapper,
   DefaultTestContextBootstrapper, and WebTestContextBootstrapper
   implementations of the TestContextBootstrapper SPI and extracted
   related reflection code from ContextLoaderUtils & TestContextManager.

 - Introduced BootstrapUtils for retrieving the TestContextBootstrapper
   from @BootstrapWith, falling back to a default if @BootstrapWith is
   not present.

 - @WebAppConfiguration is now annotated with
   @BootstrapWith(WebTestContextBootstrapper.class).

 - CacheAwareContextLoaderDelegate is now an interface with a new
   DefaultCacheAwareContextLoaderDelegate implementation class.

 - Introduced closeContext(MergedContextConfiguration, HierarchyMode) in
   CacheAwareContextLoaderDelegate.

 - DefaultTestContext now uses CacheAwareContextLoaderDelegate instead
   of interacting directly with the ContextCache.

 - DefaultTestContext now delegates to a TestContextBootstrapper for
   building the MergedContextConfiguration.

 - TestContextManager now delegates to TestContextBootstrapper for
   retrieving TestExecutionListeners.

 - Deleted TestContextManager(Class, String) constructor and
   SpringJUnit4ClassRunner.getDefaultContextLoaderClassName(Class)
   method since default ContextLoader support is now implemented by
   TestContextBootstrappers.

 - Extracted ActiveProfilesUtils from ContextLoaderUtils.

 - Extracted ApplicationContextInitializerUtils from ContextLoaderUtils.

 - MetaAnnotationUtils is now a public utility class in the test.util
   package.

 - Removed restriction in @ActiveProfiles that a custom resolver cannot
   be used with the 'value' or 'profiles' attributes.

 - Introduced DefaultActiveProfilesResolver.

Issue: SPR-9955
2014-04-10 00:15:34 +02:00
Juergen Hoeller 196cdef182 AbstractMarshaller uses general exception message in extracted buildDocument() method
Issue: SPR-11635
(cherry picked from commit f7a17ad)
2014-04-09 20:58:07 +02:00
Juergen Hoeller a167a1b591 JBoss "vfszip" resources need to be treated as jar URLs
Issue: SPR-11676
(cherry picked from commit 196f629)
2014-04-09 20:57:28 +02:00
Juergen Hoeller 9e1631db03 Dropped JCache RC repo (now unused); minor dependency upgrades
Issue: SPR-11640
2014-04-09 17:43:23 +02:00
Juergen Hoeller 0041e245a1 Polishing
Issue: SPR-11637
2014-04-09 17:43:09 +02:00
Juergen Hoeller d4b0ae9787 AbstractMarshaller pre-implements getDefaultEncoding() method
Issue: SPR-11635
2014-04-09 17:42:56 +02:00
Juergen Hoeller 8f28a9352e XStreamMarshaller lazily creates fallback XppDriver
Issue: SPR-11635
2014-04-09 17:42:45 +02:00
Juergen Hoeller 8d8766dda9 SelectTag correctly detects multiple="true" again
Issue: SPR-11678
2014-04-09 17:42:32 +02:00
Stephane Nicoll 1f2d5b5d89 Fix typos 2014-04-09 17:35:45 +02:00
Stephane Nicoll 2b89c1a87e Improve cache abstraction chapter
This commit is a general update of the Cache Abstraction chapter.

Existing sections have been updated with recent improvements made in
that area, in particular:
* Guava and JSR-107 caches support
* New @CacheConfig annotation allowing to share some key customizations
  at class-level
* CacheResolver interface used to resolve the cache(s) to use at
  runtime
* Update section on @CachePut

This commit also describes the support of standard JCache annotations,
i.e. JSR-107.

Issues: SPR-11490, SPR-11316, SPR-10629, SPR-9616, SPR-8696
2014-04-09 11:28:18 +02:00
Stephane Nicoll 3cda355e7f polishing
This commit fixes the handling of cached exceptions in the JSR-107
advisor. Such exceptions are now properly propagated instead of being
wrapped in a RuntimeException.

Issue: SPR-9616
2014-04-08 17:04:25 +02:00
Stephane Nicoll 0bdece700f Update to JCache 1.0.0 final
Issue: SPR-11640
2014-04-08 15:17:54 +02:00
Sam Brannen d53b498839 Don't encode '@' in {@code} tags 2014-04-08 15:01:54 +02:00
Sam Brannen a1486e46c8 Don't encode '@' in {@code} tags 2014-04-08 14:36:49 +02:00
Stephane Nicoll a198026469 fix CI build
Prior to this commit, AnnotatedJCacheableService contained an annotated
method demonstrating a failure scenario. This could break depending on
the order of the methods array as AopUtils creates the proxy if the
pointcut matches by checking each method.

On the CI server, the first method was this invalid use case so
checking if the proxy has to be created lead to an unexpected
exception. This scenario has been moved to its own private class now.
2014-04-08 11:37:41 +02:00
Stephane Nicoll 7b5e9e8c8e polishing
This fixes a Java6 backward compatible issue introduced in the JCache
implementation.

This commit also adds new representative tests.

Issue: SPR-9616
2014-04-07 21:43:05 +02:00
Stephane Nicoll f3b8a4103e Use CacheResolver in Spring abstraction
Prior to this commit, the CacheResolver was not used by Spring's
caching abstraction. This commit provides the necessary configuration
options to tune how a cache is resolved for a given operation.

CacheResolver can be customized globally, at the operation level or at
the class level. This breaks the CachingConfigurer class and a support
implementation is provided that implements all methods so that the
default is taken if it's not overridden. The JSR-107 support has been
updated as well, with a similar support class.

In particular, the static and runtime information of a cache
operation were mixed which prevents any forms of caching. As the
CacheResolver and the KeyGenerator can be customized, every operation
call lead to a lookup in the context for the bean.

This commit adds CacheOperationMetadata, a static holder of all
the non-runtime metadata about a cache operation. This is used
as an input source for the existing CacheOperationContext.

Caching the operation metadata in an AspectJ aspect can have side
effects as the aspect is static instance for the current ClassLoader.
The metadata cache needs to be cleared when the context shutdowns.
This is essentially a test issue only as in practice each application
runs in its class loader. Tests are now closing the context properly
to honor the DisposableBean callback.

Issue: SPR-11490
2014-04-07 15:41:09 +02:00
Stephane Nicoll 47a4327193 Add JSR-107 cache annotations support
This commit adds support for the JSR-107 cache annotations alongside
the Spring's cache annotations, that is @CacheResult, @CachePut,
@CacheRemove and @CacheRemoveAll as well as related annotations
@CacheDefaults, @CacheKey and @CacheValue.

Spring's caching configuration infrastructure detects the presence of
the JSR-107 API and Spring's JCache implementation. Both
@EnableCaching and the cache namespace are able to configure the
required JCache infrastructure when necessary. Both proxy mode
and AspectJ mode are supported.

As JSR-107 permits the customization of the CacheResolver to use for
both regular and exception caches, JCacheConfigurer has been
introduced as an extension of CachingConfigurer and permits to define
those.

If an exception is cached and should be rethrown, it is cloned and
the call stack is rewritten so that it matches the calling thread each
time. If the exception cannot be cloned, the original exception is
returned.

Internally, the interceptors uses Spring's caching abstraction by default
with an adapter layer when a JSR-107 component needs to be called.
This is the case for CacheResolver and CacheKeyGenerator.

The implementation uses Spring's CacheManager abstraction behind the
scene. The standard annotations can therefore be used against any
CacheManager implementation.

Issue: SPR-9616
2014-04-07 12:07:20 +02:00
Sam Brannen 4cd075bb96 Introduce test for PropertySource + ACI in the TCF
This commit introduces an explicit integration test to verify that a
PropertySource can be set via a custom ApplicationContextInitializer in
the Spring TestContext Framework.

Issue: SPR-11666
2014-04-06 20:09:22 +02:00
Sam Brannen 711c95d7d2 Clean up warnings due to removal of legacy Jackson support 2014-04-06 16:28:28 +02:00
Sam Brannen d9ee44cd7c Clean up warnings that show up in Gradle build 2014-04-06 16:25:28 +02:00
Sam Brannen 0eba1f818f Use unconstrained quotes in reference manual
Prior to this commit, there were numerous places in the reference
manual, where we would see output similar to the following:

    BeanDefinition`s with the `ApplicationContext

This commit addresses this issue by using unconstrained quotes
(e.g., ++XYZ++s) instead of backticks (e.g., `XYZ`s) when the formatted
text is immediately followed by an "s".

This commit also corrects a few typos and corrects natural English
pluralization of Java code elements in the reference manual where
appropriate -- for example, "@Controllers" becomes "@Controller classes,
etc.

Issue: SPR-11650
2014-04-04 22:02:26 +02:00
Rossen Stoyanchev 2e674c24f8 Merge pull request #509 from garyrussel/SPR-11468 2014-04-04 15:51:28 -04:00
Rossen Stoyanchev ddf8750511 Update MessageHeaders constructor
Following discussion with Gary.

Issue: SPR-11468
2014-04-04 15:49:53 -04:00
Gary Russell 1eee339c15 Relax constraints in MessageHeaders for subclasses
Allow a subclass to modify MessageHeaders as well as override the
strategy for or even skip having `ID` and `TIMESTAMP` headers.

Issue: SPR-11468
2014-04-04 14:39:09 -04:00
Rossen Stoyanchev 5e925ac03c Rename header for "orig" destination
The UserDestinationMessageHandler adds a header providing a hint for
what the original destination a user may have used when subscribing.
That is then used when writing messages back to WebSocket clients to
ensure they dont see the internally used, transformed user destination.

This change moves the header name constatn to make it more broadly
applicable. For example SPR-11645.
2014-04-04 14:31:49 -04:00
Brian Clozel ec0b672baa Fix @SubscribeMapping MESSAGE response destination
Prior to this commit, @SubscribeMapping mapped methods (backed with
@SendTo* annotations, or not) would send MESSAGEs with the wrong
destination. Instead of using the original SUBSCRIBE destination, it
would use the lookup path computed from the configured prefixes in the
application.

This commit fixes this issue - now @SubscribeMapping MESSAGEs use the
original SUBSCRIBE destination.

Issue: SPR-11648
2014-04-04 12:43:35 +02:00
Rossen Stoyanchev 990f5bb720 Improve handling of disconnects in STOMP broker relay
Issue: SPR-11655
2014-04-03 22:31:10 -04:00
Stephane Nicoll 3e74d3b2fb Add putIfAbsent on Cache abstraction
This commit adds a putIfAbsent method to the Cache interface. This
method offers an atomic put if the key is not already associated in
the cache.

Issue: SPR-11400
2014-04-03 11:37:22 +02:00
Juergen Hoeller 8ed490c4d7 Polishing 2014-04-02 20:57:28 +02:00
Juergen Hoeller 8006696613 Revised ByteArrayOutputStream handling in MarshallingView and co
Issue: SPR-11646
2014-04-02 20:57:09 +02:00
Juergen Hoeller 3a96f16654 ResourceHttpRequestHandler explicitly closes a Resource's InputStream
Issue: SPR-11644
2014-04-02 20:54:12 +02:00
Stephane Nicoll 54f66e451d Merge pull request #508 from wilkinsona/update-java-ee-dependencies
* update-java-ee-dependencies:
  Update Java EE spec dependencies
2014-04-02 15:37:20 +02:00
Andy Wilkinson fd5672cba1 Update Java EE spec dependencies
This commit updates a number of dependencies upon Java EE specs to
use the preferred artifacts. Part of this change has been to move
to new API artifacts for both JSTL and JavaMail. In both cases these
new API jars are genuine API jars, i.e. they no longer contain an
implementation. Where needed, implementation dependencies have been
added.
2014-04-02 15:33:39 +02:00
Stephane Nicoll db23ec733b Add exception handling of asynchronous method
Prior to this commit, an exception thrown by an @Async void method
was not further processed as there is no way to transmit that
exception to the caller.

The AsyncUncaughtExceptionHandler is a new strategy interface that
can be implemented to handle unexpected exception thrown during the
invocation of such asynchronous method.

The handler can be specified using either the XML namespace or by
implementing the AsyncConfigurer interface with the EnableAsync
annotation.

Issue: SPR-8995
2014-04-02 15:04:40 +02:00
Rossen Stoyanchev 59703981c4 Upgrade to reactor 1.1.0 snapshots
Issue: SPR-11636
2014-04-01 22:05:10 -04:00
Sam Brannen 89a4c291c3 Remove trailing whitespace 2014-04-01 20:22:25 +02:00
Sam Brannen 8e89aff48f Merge from sbrannen/SPR-11634
* SPR-11634:
  Support classes AND locations in @ContextConfiguration
2014-04-01 19:48:48 +02:00
Sam Brannen 1f017c4acb Support classes AND locations in @ContextConfiguration
Prior to this commit, the Spring TestContext Framework did not support
the declaration of both 'locations' and 'classes' within
@ContextConfiguration at the same time.

This commit addresses this in the following manner:

 - ContextConfigurationAttributes no longer throws an
   IllegalArgumentException if both 'locations' and 'classes' are
   supplied to its constructor.

 - Concrete SmartContextLoader implementations now validate the
   supplied MergedContextConfiguration before attempting to load the
   ApplicationContext. See validateMergedContextConfiguration().

 - Introduced tests for hybrid context loaders like the one used in
   Spring Boot. See HybridContextLoaderTests.

 - Updated the Testing chapter of the reference manual so that it no
   longer states that locations and classes cannot be used
   simultaneously, mentioning Spring Boot as well.

 - The Javadoc for @ContextConfiguration has been updated accordingly.

 - Added hasLocations(), hasClasses(), and hasResources() convenience
   methods to MergedContextConfiguration.

Issue: SPR-11634
2014-04-01 19:45:56 +02:00
Rossen Stoyanchev 8edbdf4ddb Fix failing test 2014-04-01 13:33:09 -04:00
Rossen Stoyanchev a6b167dd48 Merge pull request #505 from sdeleuze/SPR-11621 2014-04-01 13:18:15 -04:00
Rossen Stoyanchev 8ab8448148 Move tests to packages where they belong 2014-04-01 13:16:51 -04:00
Rossen Stoyanchev 73ecbc047c Update JettyWebSocketSession
Ensure the JettyWebSocket session can return the Principal and accepted
WebSocket sub-protocol even after the session is closed.

Issue: SPR-11621
2014-04-01 13:13:55 -04:00
Sebastien Deleuze a805f12374 Make WebSocket fields available after it is closed
Update some native WebSocket session getters to return basic
information after it is closed. It is required for example in
SubProtocolWebSocketHandler#afterConnectionEstablished() or
StompSubProtocolHandler#afterSessionStarted().

Issue: SPR-11621
2014-04-01 11:26:22 -04:00
Rossen Stoyanchev ea762b2c74 Merge pull request #503 from sdeleuze/SPR-11623 2014-04-01 11:20:25 -04:00
Rossen Stoyanchev 6ec3de6029 Fix issue with default executor for broker channel
The default for the broker channel should be "no executor".

Issue: SPR-11623
2014-04-01 11:18:28 -04:00
Sebastien Deleuze fb7d81c4a2 Fix <websocket:interceptors> default configuration
Adding a ChannelInterceptor does not suppress default executor
settings anymore in the XML namespace.

Issue: SPR-11623
2014-04-01 10:49:09 -04:00
Stephane Nicoll 119dfd9cf9 Fix cache decoration
Prior to this commit, a cache that is added on-the-fly is not properly
decorated by the provided CacheManager implementation that supports
it (EhCache and JCache).

This commits adds an extra getMissingCache method to
the AbstractCacheManager that can be extended to provide a cache that
may exist in the native cache manager but is not yet known by the
spring abstraction.

Issue: SPR-11518
2014-04-01 14:48:35 +02:00
Stephane Nicoll dcf5f4a6a3 javax.annotation.Priority alternative to @Order
This commit rationalizes the use of @Order so that the standard
@Priority annotation can be used instead. The handling of both
annotations are now defined in OrderUtils.

This also updates the link to the JavaEE API so that we refer to
JavaEE7 instead of JavaEE6.

Issue: SPR-11639
2014-04-01 14:30:47 +02:00
Stephane Nicoll 5fe8f52c02 Add support for @Priority
This commit adds support for @Priority to filter multiple candidates
for autowiring. When multiple candidates are available for a given
bean, the bean annotated with @Primary is used. If none exists, the
one with the higher value for the @Priority annotation is used. If
two beans have the same priority a NoUniqueBeanDefinitionException
is thrown, just as if two beans are annotated with @Primary.

The underlying code for #getBean and #resolveDependency has been
merged as this feature is available for both dependency injection
and bean lookup by type.

Issue: SPR-10548
2014-03-31 15:49:10 +02:00
Stephane Nicoll 48221798f5 Improving test coverage of determinePrimaryCandidate
Added tests checking the behaviour of autowiring by type when the @Primary
annotation is present on the class. Fixed also some inconsistencies in the
code.
2014-03-31 15:37:55 +02:00
Stephane Nicoll 3c28301ded Provide common cache config
Prior to this commit, common cache operation settings had to be
repeated for every operation: cache name(s), custom cache manager
and custom key manager.

This commit introduces the @CacheConfig annotation to bet set at
class-level (either directly or as a meta-annotation). As the cache
name(s) can be rationalized there, the "value" of the various
annotations are no longer mandatory.

CacheAnnotationParser has an API breakage to be able to retrieve
information at class-level.

Issue: SPR-11316
2014-03-31 15:20:50 +02:00
Stephane Nicoll 6a8cf28665 polishing 2014-03-31 14:26:05 +02:00
Stephane Nicoll f06cad91c0 Add custom cache manager per cache operation
It is now possible to specify the CacheManager to use per operation.
The related cache annotation now has an extra attribute that defines
the name of the CacheManager bean to use.  The cache manager that
was previously used is therefore a 'default' cache manager (i.e. the
one to use if no custom cache manager has been set on the operation).

Issue: SPR-8696
2014-03-31 14:17:54 +02:00
Stephane Nicoll 81c208098f Custom KeyGenerator
This commit adds an extra parameter to the base @Cache method
annotations: keyGenerator. This parameter holds the name of the
KeyGenerator bean to use to compute the key for that specific
caching endpoint.

This gives therefore a third way to customize the key. These are:
1. Default KeyGenerator (global for all endpoints)
2. The 'key' attribute of the annotation, giving the SpEL expression to use
3. The 'keyGenerator' attribute of the annotation

The annotation attributes are therefore exclusive. Trying to specify
them both will result in an IllegalStateException.

The KeyGenerator to use for a given operation is cached on startup
so that multiple calls to it does not resolve the instance to use over and
over again.

Issue: SPR-10629
2014-03-31 14:15:27 +02:00
Sam Brannen 906321dcdd Fix broken tests in QuartzSupportTests
This commit ensures that QuartzSupportTests and its related
configuration are compatible with Quartz 2.1.7.

 - Test jobs are now durable where required.

 - Deleted legacy tests that attempted to use a Runnable instead of a
   Job as a jobClass for a JobDetail.

 - Replaced quartz-hsql.sql with current version for Quartz 2.1.7.

Issue: SPR-11630
2014-03-30 18:05:38 +02:00
Sam Brannen 5d049e0de8 Introduce execute(DataSource) in ResrcDbPopulator
To simplify common use cases, this commit introduces a new
execute(DataSource) method in ResourceDatabasePopulator that complements
the existing populate(Connection) method.

Issue: SPR-11629
2014-03-30 17:01:48 +02:00
Sam Brannen b766686c40 Remove duplicate @since tags in EmbeddedDatabaseBuilder 2014-03-30 16:08:44 +02:00
Sam Brannen c38600762d Reinject Servlet mocks between TestNG test methods
Prior to this commit, if multiple test methods were executed in a
subclass of AbstractTestNGSpringContextTests annotated with
@WebAppConfiguration, then injected Servlet API mocks would only
reference the mocks created for the first test method. Subsequent test
methods could therefore never reference the current mocks, and there
was a discrepancy between the state of the injected mocks and the mock
set in the RequestContextHolder.

This commit addresses this issue by ensuring that dependencies
(including updated mocks) are injected into the test instance before
the next test method if the ServletTestExecutionListener resets the
request attributes in RequestContextHolder.

Issue: SPR-11626
2014-03-29 21:14:49 +01:00
Sam Brannen 34eb4dba3a Update Javadoc for supported JBoss VFS version 2014-03-28 21:54:23 +01:00
Sam Brannen 6aaae7e258 Execute as many Jasper Reports tests in IDE as possible
Previously, the use of Assume.group(CUSTOM_COMPILATION) in
AbstractJasperReportsTests excluded all JR tests when executing in the
IDE (e.g., Eclipse). This commit executes the assumption only where
necessary.
2014-03-28 14:06:00 +01:00
Sam Brannen db66ef0f6f Clean up warnings in spring-webmvc 2014-03-28 13:34:43 +01:00
Rossen Stoyanchev 75439c7836 Polish 2014-03-27 20:35:00 -04:00
Rossen Stoyanchev e3ef3fffa5 Optimize STOMP encoding/decoding hotspots 2014-03-27 20:15:53 -04:00
Rossen Stoyanchev ace6bd2418 Improve shutdown responsiveness of SubProtocolWSH
Proactively notify all active WebSocket sessions when a shutdown is
progress. Sessions then can ignore further attempts to send messages
and also stop stop trying to flush messages right away.
2014-03-27 20:15:53 -04:00
Juergen Hoeller ea1e27efa2 Require Jackson 2.0+, EhCache 2.5+, Quartz 2.1.4+
Issue: SPR-11262
2014-03-27 21:59:23 +01:00
Sam Brannen 5c577451f3 Polish Javadoc in abstract base test classes in the TCF 2014-03-27 14:49:26 +01:00
Rossen Stoyanchev d19e30e2f8 Increase log level in spring-messaging tests 2014-03-27 09:21:08 -04:00
Sam Brannen c924d659eb Update code templates in Eclipse settings
- Copyright is now through 2014.

 - Added "@since 4.1" tag to types.
2014-03-27 11:20:53 +01:00
Phillip Webb 0b115d7a1e Switch master to 4.1.0.BUILD-SNAPSHOT 2014-03-26 22:38:39 -07:00
1796 changed files with 99645 additions and 23790 deletions
+14
View File
@@ -0,0 +1,14 @@
Juergen Hoeller <jhoeller@vmware.com> jhoeller <jhoeller@vmware.com>
<rstoyanchev@gopivotal.com> <rstoyanchev@vmware.com>
<pwebb@gopivotal.com> <pwebb@vmware.com>
<cbeams@gopivotal.com> <cbeams@vmware.com>
<cbeams@gopivotal.com> <cbeams@gmail.com>
<apoutsma@gopivotal.com> <apoutsma@vmware.com>
<apoutsma@gopivotal.com> <poutsma@mac.com>
<ogierke@gopivotal.com> <ogierke@vmware.com>
<dsyer@gopivotal.com> <david_syer@hotmail.com>
<dsyer@gopivotal.com> <dsyer@vmware.com>
<aclement@gopivotal.com> <andrew.clement@gmail.com>
<aclement@gopivotal.com> <aclement@vmware.com>
<dmitry.katsubo@gmail.com> <dmitry.katsubo@gmai.com>
Nick Williams <nicholas@nicholaswilliams.net> Nicholas Williams <nicholas@nicholaswilliams.net>
+279 -129
View File
@@ -3,40 +3,65 @@ buildscript {
maven { url "http://repo.springsource.org/plugins-release" }
}
dependencies {
classpath("org.springframework.build.gradle:propdeps-plugin:0.0.5")
classpath("org.springframework.build.gradle:propdeps-plugin:0.0.7")
classpath("org.asciidoctor:asciidoctor-gradle-plugin:0.7.0")
classpath("org.springframework.build.gradle:docbook-reference-plugin:0.2.8")
classpath("io.spring.gradle:docbook-reference-plugin:0.3.0")
classpath("ws.antonov.gradle.plugins:gradle-plugin-protobuf:0.9.1")
}
}
ext {
linkHomepage = 'https://projects.spring.io/spring-framework'
linkCi = 'https://build.spring.io/browse/SPR'
linkIssue = 'https://jira.spring.io/browse/SPR'
linkScmUrl = 'https://github.com/spring-projects/spring-framework'
linkScmConnection = 'scm:git:git://github.com/spring-projects/spring-framework.git'
linkScmDevConnection = 'scm:git:ssh://git@github.com:spring-projects/spring-framwork.git'
}
configure(allprojects) { project ->
group = "org.springframework"
version = qualifyVersionIfNecessary(version)
ext.aspectjVersion = "1.7.4"
ext.groovyVersion = "1.8.9"
ext.aspectjVersion = "1.8.1"
ext.eclipseLinkVersion = "2.4.2"
ext.ehcacheVersion = "2.8.3"
ext.ehcacheJCacheVersion = "1.0.0"
ext.groovyVersion = "2.3.6"
ext.hibernate3Version = "3.6.10.Final"
ext.hibernate4Version = "4.2.11.Final"
ext.hibValVersion = "4.3.1.Final"
ext.hibernate4Version = "4.3.6.Final"
ext.hibVal4Version = "4.3.2.Final"
ext.hibVal5Version = "5.1.2.Final"
ext.hsqldbVersion = "2.3.2"
ext.jackson1Version = "1.9.13"
ext.jackson2Version = "2.3.1"
ext.jasperReportsVersion = "5.5.1"
ext.jettyVersion = "9.1.3.v20140225"
ext.jodaVersion = "2.3"
ext.jackson2Version = "2.4.1"
ext.gsonVersion = "2.3"
ext.jasperReportsVersion = "5.6.0"
ext.jettyVersion = "9.2.2.v20140723"
ext.jodaVersion = "2.4"
ext.junitVersion = "4.11"
ext.slf4jVersion = "1.7.6"
ext.openJpaVersion = "2.2.2" // 2.3.0 not Java 8 compatible (based on ASM 4)
ext.slf4jVersion = "1.7.7"
ext.snakeYamlVersion = "1.13"
ext.snifferVersion = "1.11"
ext.tiles2Version = "2.2.2"
ext.tiles3Version = "3.0.3"
ext.tiles3Version = "3.0.4"
ext.tomcatVersion = "8.0.9"
ext.xstreamVersion = "1.4.7"
ext.protobufVersion = "2.5.0"
ext.woodstoxVersion = "4.1.6"
ext.gradleScriptDir = "${rootProject.projectDir}/gradle"
ext.gradleScriptDir = "${rootProject.projectDir}/gradle"
apply plugin: "propdeps"
apply plugin: "java"
apply plugin: "test-source-set-dependencies"
apply from: "${gradleScriptDir}/ide.gradle"
configurations {
sniffer
javaApiSignature
}
compileJava.options*.compilerArgs = [
"-Xlint:serial", "-Xlint:varargs", "-Xlint:cast", "-Xlint:classfile",
"-Xlint:dep-ann", "-Xlint:divzero", "-Xlint:empty", "-Xlint:finally",
@@ -58,8 +83,9 @@ configure(allprojects) { project ->
}
compileTestJava {
sourceCompatibility=1.7
targetCompatibility=1.7
sourceCompatibility=1.8
targetCompatibility=1.8
options.compilerArgs += "-parameters"
}
sourceSets.test.resources.srcDirs = ["src/test/resources", "src/test/java"]
@@ -71,14 +97,12 @@ configure(allprojects) { project ->
include(["**/*Tests.class", "**/*Test.class"])
// Since we set scanForTestClasses to false, we need to filter out inner
// classes with the "$" pattern; otherwise, using -Dtest.single=MyTests to
// run MyTests by itself will fail if MyTests contains any inner classes.
// run MyTests by itself will fail if MyTests contains any inner classes.
exclude(["**/Abstract*.class", '**/*$*'])
}
repositories {
maven { url "http://repo.spring.io/libs-release" }
maven { url "http://repo.spring.io/milestone" } // for AspectJ 1.8.0.M1
maven { url "https://oss.sonatype.org/content/repositories/releases" } // javax.cache
}
dependencies {
@@ -89,12 +113,53 @@ configure(allprojects) { project ->
exclude group:'org.hamcrest', module:'hamcrest-core'
}
testCompile("org.hamcrest:hamcrest-all:1.3")
testCompile("org.mockito:mockito-core:1.9.5")
sniffer("org.codehaus.mojo:animal-sniffer-ant-tasks:${snifferVersion}")
javaApiSignature("org.codehaus.mojo.signature:java16:1.1@signature") // as from JDK 6 update 18
}
task copyJavaApiSignature(type: Copy) {
ext.to = file("$buildDir/javaApiSignature/")
description "Copy the resolved Animal Sniffer signature dependency artifact to a known location and name"
from configurations.javaApiSignature
into to
rename '.*signature', 'javaApi.signature'
}
task sniff {
group = "Verification"
description = "Checks the Java API signatures"
dependsOn compileJava
dependsOn copyJavaApiSignature
inputs.dir sourceSets.main.output.classesDir
inputs.dir copyJavaApiSignature.to
outputs.upToDateWhen { true }
doLast {
ant.taskdef(
name: 'animalSniffer',
classname: 'org.codehaus.mojo.animal_sniffer.ant.CheckSignatureTask',
classpath: configurations.sniffer.asPath
)
ant.animalSniffer(
signature: "$buildDir/javaApiSignature/javaApi.signature",
classpath: sourceSets.main.compileClasspath.asPath) {
path(path: sourceSets.main.output.classesDir)
annotation(className: "org.springframework.lang.UsesJava7")
annotation(className: "org.springframework.lang.UsesJava8")
annotation(className: "org.springframework.lang.UsesSunHttpServer")
}
}
}
ext.javadocLinks = [
"http://docs.oracle.com/javase/7/docs/api/",
"http://docs.oracle.com/javaee/6/api/",
"http://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ
"http://docs.oracle.com/javase/8/docs/api/",
"http://docs.oracle.com/javaee/7/api/",
"http://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ
"http://pic.dhe.ibm.com/infocenter/wasinfo/v7r0/topic/com.ibm.websphere.javadoc.doc/web/apidocs/",
"http://glassfish.java.net/nonav/docs/v3/api/",
"http://docs.jboss.org/jbossas/javadoc/4.0.5/connector/",
@@ -108,11 +173,10 @@ configure(allprojects) { project ->
"http://aopalliance.sourceforge.net/doc/",
"http://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/",
"http://ehcache.org/apidocs/",
"http://quartz-scheduler.org/api/2.1.7/",
"http://jackson.codehaus.org/1.9.4/javadoc/",
"http://quartz-scheduler.org/api/2.2.0/",
"http://fasterxml.github.com/jackson-core/javadoc/2.3.0/",
"http://fasterxml.github.com/jackson-databind/javadoc/2.3.0/",
"http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs"
"http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/"
] as String[]
}
@@ -120,6 +184,20 @@ configure(subprojects - project(":spring-build-src")) { subproject ->
apply plugin: "merge"
apply from: "${gradleScriptDir}/publish-maven.gradle"
configurations {
jacoco
}
dependencies {
jacoco("org.jacoco:org.jacoco.agent:0.7.1.201405082137:runtime")
}
gradle.taskGraph.whenReady {taskGraph ->
if (taskGraph.hasTask(':sonarRunner')) {
test.jvmArgs "-javaagent:${configurations.jacoco.asPath}=destfile=${buildDir}/jacoco.exec,includes=org.springframework.*"
}
}
jar {
manifest.attributes["Created-By"] =
"${System.getProperty("java.version")} (${System.getProperty("java.specification.vendor")})"
@@ -243,11 +321,13 @@ project("spring-core") {
compile(files(cglibRepackJar))
compile("commons-logging:commons-logging:1.1.3")
optional("commons-codec:commons-codec:1.9")
optional("org.aspectj:aspectjweaver:${aspectjVersion}")
optional("net.sf.jopt-simple:jopt-simple:4.6")
optional("log4j:log4j:1.2.17")
testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}")
testCompile("xmlunit:xmlunit:1.5")
testCompile("org.codehaus.woodstox:wstx-asl:3.2.7") {
testCompile("org.codehaus.woodstox:woodstox-core-asl:${woodstoxVersion}") {
exclude group: "stax", module: "stax-api"
}
}
@@ -264,12 +344,6 @@ project("spring-core") {
include "org/springframework/objenesis/**"
}
}
compileTestJava {
sourceCompatibility=1.8
targetCompatibility=1.8
compileTestJava.options.compilerArgs += "-parameters"
}
}
project("spring-beans") {
@@ -279,9 +353,11 @@ project("spring-beans") {
compile(project(":spring-core"))
compile(files(project(":spring-core").cglibRepackJar))
optional("javax.inject:javax.inject:1")
optional("javax.el:javax.el-api:2.2.4")
optional("javax.el:javax.el-api:2.2.5")
optional("org.yaml:snakeyaml:${snakeYamlVersion}")
testCompile("log4j:log4j:1.2.17")
}
testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}")
}
}
project("spring-beans-groovy") {
@@ -321,7 +397,7 @@ project("spring-aop") {
compile("aopalliance:aopalliance:1.0")
optional("org.aspectj:aspectjweaver:${aspectjVersion}")
optional("commons-pool:commons-pool:1.6")
optional("com.jamonapi:jamon:2.4")
optional("com.jamonapi:jamon:2.76")
}
}
@@ -371,12 +447,12 @@ project("spring-context") {
optional("javax.enterprise.concurrent:javax.enterprise.concurrent-api:1.0")
optional("org.eclipse.persistence:javax.persistence:2.0.0")
optional("javax.validation:validation-api:1.0.0.GA")
optional("org.hibernate:hibernate-validator:${hibValVersion}")
optional("org.hibernate:hibernate-validator:${hibVal4Version}")
optional("joda-time:joda-time:${jodaVersion}")
optional("org.aspectj:aspectjweaver:${aspectjVersion}")
optional("org.codehaus.groovy:groovy-all:${groovyVersion}")
optional("org.beanshell:bsh:2.0b4")
optional("org.jruby:jruby:1.7.10")
optional("org.jruby:jruby:1.7.12")
testCompile("javax.inject:javax.inject-tck:1")
testCompile("commons-dbcp:commons-dbcp:1.4")
testCompile("org.slf4j:slf4j-api:${slf4jVersion}")
@@ -384,10 +460,6 @@ project("spring-context") {
// pick up RmiInvocationWrapperRTD.xml in src/main
sourceSets.main.resources.srcDirs += "src/main/java"
test {
jvmArgs = ["-disableassertions:org.aspectj.weaver.UnresolvedType"] // SPR-7989
}
}
project("spring-messaging") {
@@ -397,8 +469,8 @@ project("spring-messaging") {
compile(project(":spring-beans"))
compile(project(":spring-core"))
compile(project(":spring-context"))
optional("org.projectreactor:reactor-core:1.0.1.RELEASE")
optional("org.projectreactor:reactor-tcp:1.0.1.RELEASE")
optional("org.projectreactor:reactor-core:1.1.3.RELEASE")
optional("org.projectreactor:reactor-net:1.1.3.RELEASE")
optional("org.eclipse.jetty.websocket:websocket-server:${jettyVersion}") {
exclude group: "javax.servlet", module: "javax.servlet-api"
}
@@ -417,9 +489,9 @@ project("spring-messaging") {
testCompile("org.eclipse.jetty:jetty-webapp:${jettyVersion}") {
exclude group: "javax.servlet", module: "javax.servlet-api"
}
testCompile("org.apache.tomcat.embed:tomcat-embed-core:8.0.3")
testCompile("org.apache.tomcat.embed:tomcat-embed-websocket:8.0.3")
testCompile("org.apache.tomcat.embed:tomcat-embed-logging-juli:8.0.3")
testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}")
testCompile("org.apache.tomcat.embed:tomcat-embed-websocket:${tomcatVersion}")
testCompile("org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}")
testCompile("commons-dbcp:commons-dbcp:1.4")
testCompile("log4j:log4j:1.2.17")
testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}")
@@ -458,8 +530,9 @@ project("spring-oxm") {
dependencies {
compile(project(":spring-beans"))
compile(project(":spring-core"))
optional("org.codehaus.castor:castor-xml:1.3.2") {
optional("org.codehaus.castor:castor-xml:1.3.3") {
exclude group: 'stax', module: 'stax-api'
exclude group: "org.springframework", module: "spring-context"
}
optional("org.apache.xmlbeans:xmlbeans:2.6.0") {
exclude group: 'stax', module: 'stax-api'
@@ -489,13 +562,13 @@ project("spring-jms") {
compile(project(":spring-beans"))
compile(project(":spring-aop"))
compile(project(":spring-context"))
compile(project(":spring-messaging"))
compile(project(":spring-tx"))
provided("javax.jms:jms-api:1.1-rev-1")
optional(project(":spring-oxm"))
optional("aopalliance:aopalliance:1.0")
optional("javax.transaction:javax.transaction-api:1.2")
optional("javax.resource:connector-api:1.5")
optional("org.codehaus.jackson:jackson-mapper-asl:${jackson1Version}")
optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}")
}
}
@@ -509,11 +582,11 @@ project("spring-jdbc") {
compile(project(":spring-tx"))
optional(project(":spring-context")) // for JndiDataSourceLookup
optional("javax.transaction:javax.transaction-api:1.2")
optional("c3p0:c3p0:0.9.1.2")
optional("com.mchange:c3p0:0.9.2.1")
optional("org.hsqldb:hsqldb:${hsqldbVersion}")
optional("com.h2database:h2:1.0.71")
optional("org.apache.derby:derby:10.10.1.1")
optional("org.apache.derby:derbyclient:10.10.1.1")
optional("com.h2database:h2:1.4.180")
optional("org.apache.derby:derby:10.10.2.0")
optional("org.apache.derby:derbyclient:10.10.2.0")
}
}
@@ -526,23 +599,30 @@ project("spring-context-support") {
compile(project(":spring-context"))
optional(project(":spring-jdbc")) // for Quartz support
optional(project(":spring-tx")) // for Quartz support
optional("javax.mail:mail:1.4.7")
optional("javax.cache:cache-api:1.0.0-RC1")
optional("com.google.guava:guava:16.0.1")
optional("net.sf.ehcache:ehcache-core:2.6.5")
optional("org.quartz-scheduler:quartz:1.8.6") {
exclude group: "org.slf4j", module: "slf4j-log4j12"
}
optional("javax.mail:javax.mail-api:1.5.2")
optional("javax.cache:cache-api:1.0.0")
optional("com.google.guava:guava:17.0")
optional("net.sf.ehcache:ehcache:${ehcacheVersion}")
optional("org.quartz-scheduler:quartz:2.2.1")
optional("org.codehaus.fabric3.api:commonj:1.1.0")
optional("org.apache.velocity:velocity:1.7")
optional("org.freemarker:freemarker:2.3.20")
optional("com.lowagie:itext:2.1.7")
optional("net.sf.jasperreports:jasperreports:$jasperReportsVersion")
testCompile("org.apache.poi:poi:3.9")
optional("net.sf.jasperreports:jasperreports:$jasperReportsVersion") {
exclude group: "com.fasterxml.jackson.core", module: "jackson-annotations"
exclude group: "com.fasterxml.jackson.core", module: "jackson-core"
exclude group: "com.fasterxml.jackson.core", module: "jackson-databind"
exclude group: "org.olap4j", module: "olap4j"
exclude group: "xml-apis", module: "xml-apis"
}
testCompile(project(":spring-context"))
testCompile("org.apache.poi:poi:3.10-FINAL")
testCompile("commons-beanutils:commons-beanutils:1.8.0") // for Velocity/JasperReports
testCompile("commons-digester:commons-digester:1.8.1") // for Velocity/JasperReports
testCompile("org.hsqldb:hsqldb:${hsqldbVersion}")
testCompile("org.slf4j:slf4j-api:${slf4jVersion}")
testCompile("org.ehcache:jcache:${ehcacheJCacheVersion}")
testRuntime("com.sun.mail:javax.mail:1.5.2")
}
// pick up **/*.types files in src/main
@@ -551,6 +631,22 @@ project("spring-context-support") {
project("spring-web") {
description = "Spring Web"
apply plugin: "groovy"
// Re-generate Protobuf classes from *.proto files and move them in test sources
if (project.hasProperty('genProtobuf')) {
apply plugin: 'protobuf'
task updateGenProtobuf(type:Copy, dependsOn: ":spring-web:generateTestProto") {
from "${project.buildDir}/generated-sources/test/"
into "${projectDir}/src/test/java"
doLast {
project.delete "${project.buildDir}/generated-sources/test"
}
}
tasks.getByPath("compileTestJava").dependsOn "updateGenProtobuf"
}
dependencies {
compile(project(":spring-aop")) // for JaxWsPortProxyFactoryBean
@@ -559,19 +655,20 @@ project("spring-web") {
compile(project(":spring-core"))
provided("javax.servlet:javax.servlet-api:3.0.1")
optional(project(":spring-oxm")) // for MarshallingHttpMessageConverter
optional("javax.servlet.jsp:jsp-api:2.2")
optional("javax.servlet.jsp:javax.servlet.jsp-api:2.2.1")
optional("javax.portlet:portlet-api:2.0")
optional("javax.el:javax.el-api:2.2.4")
optional("javax.el:javax.el-api:2.2.5")
optional("javax.faces:javax.faces-api:2.2")
optional("aopalliance:aopalliance:1.0")
optional("com.caucho:hessian:4.0.7")
optional("org.codehaus.groovy:groovy-all:${groovyVersion}")
optional("com.caucho:hessian:4.0.38")
optional("commons-fileupload:commons-fileupload:1.3.1")
optional("org.apache.httpcomponents:httpclient:4.3.3")
optional("org.apache.httpcomponents:httpclient:4.3.4")
optional("org.apache.httpcomponents:httpasyncclient:4.0.1")
optional("org.codehaus.jackson:jackson-mapper-asl:${jackson1Version}")
optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}")
optional("rome:rome:1.0")
optional("taglibs:standard:1.1.2")
optional("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:${jackson2Version}")
optional("com.google.code.gson:gson:${gsonVersion}")
optional("com.rometools:rome:1.5.0")
optional("org.eclipse.jetty:jetty-servlet:${jettyVersion}") {
exclude group: "javax.servlet", module: "javax.servlet-api"
}
@@ -579,9 +676,14 @@ project("spring-web") {
exclude group: "javax.servlet", module: "javax.servlet-api"
}
optional("log4j:log4j:1.2.17")
optional("com.googlecode.protobuf-java-format:protobuf-java-format:1.2")
optional("com.google.protobuf:protobuf-java:${protobufVersion}")
testCompile(project(":spring-context-support")) // for JafMediaTypeFactory
testCompile("xmlunit:xmlunit:1.5")
testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}")
testCompile("org.apache.taglibs:taglibs-standard-jstlel:1.2.1") {
exclude group: "org.apache.taglibs", module: "taglibs-standard-spec"
}
}
// pick up ContextLoader.properties in src/main
@@ -599,12 +701,14 @@ project("spring-websocket") {
optional(project(":spring-webmvc"))
optional("javax.servlet:javax.servlet-api:3.1.0")
optional("javax.websocket:javax.websocket-api:1.0")
optional("org.apache.tomcat:tomcat-websocket:8.0.3") {
optional("org.apache.tomcat:tomcat-websocket:${tomcatVersion}") {
exclude group: "org.apache.tomcat", module: "tomcat-websocket-api"
exclude group: "org.apache.tomcat", module: "tomcat-servlet-api"
}
optional("org.glassfish.tyrus:tyrus-websocket-core:1.2.1")
optional("org.glassfish.tyrus:tyrus-container-servlet:1.2.1")
optional("org.glassfish.tyrus:tyrus-spi:1.3.5")
optional("org.glassfish.tyrus:tyrus-core:1.3.5")
optional("org.glassfish.tyrus:tyrus-server:1.3.5")
optional("org.glassfish.tyrus:tyrus-container-servlet:1.3.5")
optional("org.eclipse.jetty:jetty-webapp:${jettyVersion}") {
exclude group: "javax.servlet", module: "javax.servlet"
}
@@ -612,20 +716,21 @@ project("spring-websocket") {
exclude group: "javax.servlet", module: "javax.servlet"
}
optional("org.eclipse.jetty.websocket:websocket-client:${jettyVersion}")
optional("io.undertow:undertow-core:1.0.0.Final")
optional("io.undertow:undertow-servlet:1.0.0.Final") {
optional("org.eclipse.jetty:jetty-client:${jettyVersion}")
optional("io.undertow:undertow-core:1.0.15.Final")
optional("io.undertow:undertow-servlet:1.0.15.Final") {
exclude group: "org.jboss.spec.javax.servlet", module: "jboss-servlet-api_3.1_spec"
exclude group: "org.jboss.spec.javax.annotation", module: "jboss-annotations-api_1.2_spec"
}
optional("io.undertow:undertow-websockets-jsr:1.0.0.Final") {
optional("io.undertow:undertow-websockets-jsr:1.0.15.Final") {
exclude group: "org.jboss.spec.javax.websocket", module: "jboss-websocket-api_1.0_spec"
}
optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}")
testCompile("org.apache.tomcat.embed:tomcat-embed-core:8.0.3")
testCompile("org.apache.tomcat.embed:tomcat-embed-websocket:8.0.3")
testCompile("org.apache.tomcat.embed:tomcat-embed-logging-juli:8.0.3")
testCompile("org.projectreactor:reactor-core:1.0.1.RELEASE")
testCompile("org.projectreactor:reactor-tcp:1.0.1.RELEASE")
testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}")
testCompile("org.apache.tomcat.embed:tomcat-embed-websocket:${tomcatVersion}")
testCompile("org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}")
testCompile("org.projectreactor:reactor-core:1.1.3.RELEASE")
testCompile("org.projectreactor:reactor-net:1.1.3.RELEASE")
testCompile("log4j:log4j:1.2.17")
testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}")
}
@@ -643,9 +748,9 @@ project("spring-orm") {
optional(project(":spring-context"))
optional(project(":spring-web"))
optional("aopalliance:aopalliance:1.0")
optional("org.eclipse.persistence:javax.persistence:2.0.0")
optional("org.eclipse.persistence:org.eclipse.persistence.core:2.4.0")
optional("org.eclipse.persistence:org.eclipse.persistence.jpa:2.4.0") {
optional("org.eclipse.persistence:javax.persistence:2.0.5")
optional("org.eclipse.persistence:org.eclipse.persistence.core:${eclipseLinkVersion}")
optional("org.eclipse.persistence:org.eclipse.persistence.jpa:${eclipseLinkVersion}") {
exclude group: 'org.eclipse.persistence', module: 'javax.persistence'
}
optional("org.hibernate:hibernate-core:${hibernate3Version}") {
@@ -655,17 +760,18 @@ project("spring-orm") {
optional("org.hibernate:hibernate-entitymanager:${hibernate3Version}") {
exclude group: 'org.hibernate.javax.persistence', module: 'hibernate-jpa-2.0-api'
}
optional("org.apache.openjpa:openjpa:2.2.1") {
optional("org.apache.openjpa:openjpa:${openJpaVersion}") {
exclude group: 'junit', module: 'junit'
exclude group: 'org.apache.geronimo.specs', module: 'geronimo-jpa_2.0_spec'
exclude group: 'org.apache.geronimo.specs', module: 'geronimo-jta_1.1_spec'
exclude group: 'org.apache.geronimo.specs', module: 'geronimo-jms_1.1_spec'
}
optional("javax.jdo:jdo-api:3.0") {
optional("javax.jdo:jdo-api:3.0.1") {
exclude group: 'javax.transaction', module: 'transaction-api'
}
optional("javax.servlet:javax.servlet-api:3.0.1")
testCompile("commons-dbcp:commons-dbcp:1.4")
testCompile("org.aspectj:aspectjweaver:${aspectjVersion}")
testCompile("org.hsqldb:hsqldb:${hsqldbVersion}")
testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}")
}
@@ -682,6 +788,11 @@ project("spring-orm-hibernate4") {
optional("org.hibernate:hibernate-core:${hibernate4Version}")
optional("org.hibernate:hibernate-entitymanager:${hibernate4Version}")
optional("javax.servlet:javax.servlet-api:3.0.1")
optional("aopalliance:aopalliance:1.0")
testCompile("javax.validation:validation-api:1.1.0.GA")
testCompile("org.hibernate:hibernate-validator:${hibVal5Version}")
testCompile("javax.el:javax.el-api:2.2.5")
testCompile("org.glassfish.web:javax.el:2.2.6")
}
}
@@ -698,36 +809,41 @@ project("spring-webmvc") {
provided("javax.servlet:javax.servlet-api:3.0.1")
optional(project(":spring-context-support")) // for Velocity support
optional(project(":spring-oxm")) // for MarshallingView
optional("javax.servlet.jsp:jsp-api:2.2")
optional("javax.servlet:jstl:1.2")
optional("javax.servlet.jsp:javax.servlet.jsp-api:2.2.1")
optional("javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.1")
optional("net.sourceforge.jexcelapi:jxl:2.6.12")
optional("org.apache.poi:poi:3.9")
optional("org.apache.poi:poi:3.10-FINAL")
optional("org.apache.velocity:velocity:1.7")
optional("velocity-tools:velocity-tools-view:1.4")
optional("org.freemarker:freemarker:2.3.20")
optional("org.codehaus.groovy:groovy-all:${groovyVersion}")
optional("com.lowagie:itext:2.1.7")
optional("net.sf.jasperreports:jasperreports:$jasperReportsVersion") {
exclude group: "com.fasterxml.jackson.core", module: "jackson-annotations"
exclude group: "com.fasterxml.jackson.core", module: "jackson-core"
exclude group: "com.fasterxml.jackson.core", module: "jackson-databind"
exclude group: "org.olap4j", module: "olap4j"
exclude group: "xml-apis", module: "xml-apis"
}
optional("org.codehaus.jackson:jackson-mapper-asl:${jackson1Version}")
optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}")
optional("rome:rome:1.0")
optional("org.apache.tiles:tiles-api:${tiles2Version}")
optional("org.apache.tiles:tiles-core:${tiles2Version}") {
optional("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:${jackson2Version}")
optional("com.rometools:rome:1.5.0")
optional("javax.el:javax.el-api:2.2.5")
optional("org.apache.tiles:tiles-api:${tiles3Version}")
optional("org.apache.tiles:tiles-core:${tiles3Version}") {
exclude group: "org.slf4j", module: "jcl-over-slf4j"
}
optional("org.apache.tiles:tiles-servlet:${tiles2Version}") {
optional("org.apache.tiles:tiles-servlet:${tiles3Version}") {
exclude group: "org.slf4j", module: "jcl-over-slf4j"
}
optional("org.apache.tiles:tiles-jsp:${tiles2Version}") {
optional("org.apache.tiles:tiles-jsp:${tiles3Version}") {
exclude group: "org.slf4j", module: "jcl-over-slf4j"
}
optional("org.apache.tiles:tiles-el:${tiles2Version}") {
optional("org.apache.tiles:tiles-el:${tiles3Version}") {
exclude group: "org.slf4j", module: "jcl-over-slf4j"
}
optional("org.apache.tiles:tiles-extras:${tiles2Version}") {
optional("org.apache.tiles:tiles-extras:${tiles3Version}") {
exclude group: "org.slf4j", module: "jcl-over-slf4j"
exclude group: "org.apache.velocity", module: "velocity-tools"
exclude group: "org.springframework", module: "spring-web"
}
testCompile(project(":spring-aop"))
@@ -748,8 +864,8 @@ project("spring-webmvc") {
exclude group: "javax.servlet", module: "javax.servlet"
}
testCompile("javax.validation:validation-api:1.0.0.GA")
testCompile("org.hibernate:hibernate-validator:${hibValVersion}")
testCompile("org.apache.httpcomponents:httpclient:4.3.3")
testCompile("org.hibernate:hibernate-validator:${hibVal4Version}")
testCompile("org.apache.httpcomponents:httpclient:4.3.4")
testCompile("commons-fileupload:commons-fileupload:1.3.1")
testCompile("commons-io:commons-io:1.3")
testCompile("joda-time:joda-time:${jodaVersion}")
@@ -760,32 +876,32 @@ project("spring-webmvc") {
sourceSets.main.resources.srcDirs += "src/main/java"
}
project("spring-webmvc-tiles3") {
description = "Spring Framework Tiles3 Integration"
project("spring-webmvc-tiles2") {
description = "Spring Framework Tiles2 Integration"
merge.into = project(":spring-webmvc")
dependencies {
provided(project(":spring-context"))
provided(project(":spring-web"))
provided("javax.servlet:javax.servlet-api:3.0.1")
optional("javax.servlet.jsp:jsp-api:2.2")
optional("javax.servlet:jstl:1.2")
optional("javax.el:javax.el-api:2.2.4")
optional("org.apache.tiles:tiles-api:${tiles3Version}")
optional("org.apache.tiles:tiles-core:${tiles3Version}") {
optional("javax.servlet.jsp:javax.servlet.jsp-api:2.2.1")
optional("javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.1")
optional("org.apache.tiles:tiles-api:${tiles2Version}")
optional("org.apache.tiles:tiles-core:${tiles2Version}") {
exclude group: "org.slf4j", module: "jcl-over-slf4j"
}
optional("org.apache.tiles:tiles-servlet:${tiles3Version}") {
optional("org.apache.tiles:tiles-servlet:${tiles2Version}") {
exclude group: "org.slf4j", module: "jcl-over-slf4j"
}
optional("org.apache.tiles:tiles-jsp:${tiles3Version}") {
optional("org.apache.tiles:tiles-jsp:${tiles2Version}") {
exclude group: "org.slf4j", module: "jcl-over-slf4j"
}
optional("org.apache.tiles:tiles-el:${tiles3Version}") {
optional("org.apache.tiles:tiles-el:${tiles2Version}") {
exclude group: "org.slf4j", module: "jcl-over-slf4j"
}
optional("org.apache.tiles:tiles-extras:${tiles3Version}") {
optional("org.apache.tiles:tiles-extras:${tiles2Version}") {
exclude group: "org.slf4j", module: "jcl-over-slf4j"
exclude group: "org.apache.velocity", module: "velocity-tools"
exclude group: "org.springframework", module: "spring-web"
}
testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}")
@@ -827,29 +943,29 @@ project("spring-test") {
optional("org.testng:testng:6.8.8")
optional("javax.inject:javax.inject:1")
optional("javax.servlet:javax.servlet-api:3.0.1")
optional("javax.servlet.jsp:jsp-api:2.2")
optional("javax.servlet:jstl:1.2")
optional("javax.servlet.jsp:javax.servlet.jsp-api:2.2.1")
optional("javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.1")
optional("org.apache.taglibs:taglibs-standard-jstlel:1.2.1") {
exclude group: "org.apache.taglibs", module: "taglibs-standard-spec"
}
optional("javax.portlet:portlet-api:2.0")
optional("javax.el:javax.el-api:2.2.4")
optional("org.eclipse.persistence:javax.persistence:2.0.0")
optional("javax.el:javax.el-api:2.2.5")
optional("org.aspectj:aspectjweaver:${aspectjVersion}")
optional("org.codehaus.groovy:groovy-all:${groovyVersion}")
optional("org.hamcrest:hamcrest-core:1.3")
optional("com.jayway.jsonpath:json-path:0.9.0")
optional("org.skyscreamer:jsonassert:1.2.3")
optional("xmlunit:xmlunit:1.5")
testCompile(project(":spring-context-support"))
testCompile(project(":spring-oxm"))
testCompile(project(":spring-webmvc-tiles3"))
testCompile("javax.mail:mail:1.4.7")
testCompile("javax.mail:javax.mail-api:1.5.2")
testCompile("javax.ejb:ejb-api:3.0")
testCompile("org.hibernate:hibernate-core:${hibernate3Version}") {
exclude group: 'org.hibernate.javax.persistence', module: 'hibernate-jpa-2.0-api'
}
testCompile("org.hibernate:hibernate-entitymanager:${hibernate3Version}")
testCompile("org.hibernate:hibernate-validator:${hibValVersion}")
testCompile("org.hibernate:hibernate-core:${hibernate4Version}")
testCompile("org.hibernate:hibernate-entitymanager:${hibernate4Version}")
testCompile("org.hibernate:hibernate-validator:${hibVal5Version}")
testCompile("com.thoughtworks.xstream:xstream:${xstreamVersion}")
testCompile("org.codehaus.jackson:jackson-mapper-asl:${jackson1Version}")
testCompile("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}")
testCompile("rome:rome:1.0")
testCompile("com.rometools:rome:1.5.0")
testCompile("org.apache.tiles:tiles-api:${tiles3Version}")
testCompile("org.apache.tiles:tiles-core:${tiles3Version}") {
exclude group: "org.slf4j", module: "jcl-over-slf4j"
@@ -863,6 +979,7 @@ project("spring-test") {
task testNG(type: Test) {
useTestNG()
scanForTestClasses = false
include(["**/testng/**/*Tests.class", "**/testng/**/*Test.class"])
// Show STD_OUT & STD_ERR of the test JVM(s) on the console:
// testLogging.showStandardStreams = true
@@ -889,19 +1006,20 @@ project("spring-aspects") {
dependencies {
aspects(project(":spring-orm"))
ajc("org.aspectj:aspectjtools:1.8.0.RC2") // needed for ajc on JDK 8 only
rt("org.aspectj:aspectjrt:1.8.0.RC2") // needed for ajc on JDK 8 only
compile("org.aspectj:aspectjweaver:${aspectjVersion}") // exposing regular AspectJ version to users
ajc("org.aspectj:aspectjtools:${aspectjVersion}")
rt("org.aspectj:aspectjrt:${aspectjVersion}")
compile("org.aspectj:aspectjweaver:${aspectjVersion}")
provided("org.eclipse.persistence:javax.persistence:2.0.0")
optional(project(":spring-aop")) // for @Async support
optional(project(":spring-beans")) // for @Configurable support
optional(project(":spring-context")) // for @Enable* support
optional(project(":spring-context-support")) // for JavaMail support
optional(project(":spring-context-support")) // for JavaMail and JSR-107 support
optional(project(":spring-orm")) // for JPA exception translation support
optional(project(":spring-tx")) // for JPA, @Transactional support
optional("javax.cache:cache-api:1.0.0")
testCompile(project(":spring-core")) // for CodeStyleAspect
testCompile(project(":spring-test"))
testCompile("javax.mail:mail:1.4.7")
testCompile("javax.mail:javax.mail-api:1.5.2")
}
eclipse.project {
@@ -946,6 +1064,22 @@ project("spring-framework-bom") {
}
}
apply plugin: 'sonar-runner'
sonarRunner {
sonarProperties {
property "sonar.projectName", "Spring Framework"
property "sonar.profile", "Spring Framework"
property "sonar.jacoco.reportPath", "${buildDir.name}/jacoco.exec"
property "sonar.links.homepage", linkHomepage
property "sonar.links.ci", linkCi
property "sonar.links.issue", linkIssue
property "sonar.links.scm", linkScmUrl
property "sonar.links.scm_dev", linkScmDevConnection
property "sonar.java.coveragePlugin", "jacoco"
}
}
configure(rootProject) {
description = "Spring Framework"
@@ -973,6 +1107,7 @@ configure(rootProject) {
reference {
sourceDir = asciidoctor.outputDir
pdfFilename = "spring-framework-reference.pdf"
epubFilename = "spring-framework-reference.epub"
expandPlaceholders = ""
}
@@ -1075,7 +1210,7 @@ configure(rootProject) {
classifier = "schema"
description = "Builds -${classifier} archive containing all " +
"XSDs for deployment at http://springframework.org/schema."
duplicatesStrategy 'exclude'
subprojects.each { subproject ->
def Properties schemas = new Properties();
@@ -1175,7 +1310,7 @@ configure(rootProject) {
task wrapper(type: Wrapper) {
description = "Generates gradlew[.bat] scripts"
gradleVersion = "1.11"
gradleVersion = "1.12"
doLast() {
def gradleOpts = "-XX:MaxMetaspaceSize=1024m -Xmx1024m"
@@ -1191,6 +1326,21 @@ configure(rootProject) {
}
configure([project(':spring-build-src'), project(':spring-framework-bom')]) {
sonarRunner {
skipProject = true
}
}
configure(project(':spring-core')) {
sonarRunner {
sonarProperties {
property "sonar.exclusions",
"src/main/java/org/springframework/cglib/**/*,src/main/java/org/springframework/asm/**/*"
}
}
}
/*
* Support publication of artifacts versioned by topic branch.
* CI builds supply `-P BRANCH_NAME=<TOPIC>` to gradle at build time.
@@ -21,13 +21,10 @@ import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ProjectDependency;
import org.gradle.api.artifacts.maven.Conf2ScopeMapping
import org.gradle.api.plugins.MavenPlugin
import org.gradle.api.tasks.*
import org.gradle.plugins.ide.eclipse.EclipsePlugin
import org.gradle.plugins.ide.eclipse.model.EclipseClasspath;
import org.gradle.plugins.ide.idea.IdeaPlugin
import org.gradle.api.invocation.*
/**
* Gradle plugin that allows projects to merged together. Primarily developed to
* allow Spring to support multiple incompatible versions of third-party
@@ -71,18 +68,18 @@ class MergePlugin implements Plugin<Project> {
Configuration runtimeMerge = project.configurations.create("runtimeMerge")
// Ensure the IDE can reference merged projects
project.eclipse.classpath.plusConfigurations += [runtimeMerge]
project.idea.module.scopes.PROVIDED.plus += runtimeMerge
project.eclipse.classpath.plusConfigurations += [ runtimeMerge ]
project.idea.module.scopes.PROVIDED.plus += [ runtimeMerge ]
// Hook to perform the actual merge logic
project.afterEvaluate{
if(it.merge.into != null) {
if (it.merge.into != null) {
setup(it)
}
}
// Hook to build runtimeMerge dependencies
if(!attachedProjectsEvaluated) {
if (!attachedProjectsEvaluated) {
project.gradle.projectsEvaluated{
postProcessProjects(it)
}
@@ -102,7 +99,7 @@ class MergePlugin implements Plugin<Project> {
// invoking a task will invoke the task with the same name on 'into' project
["sourcesJar", "jar", "javadocJar", "javadoc", "install", "artifactoryPublish"].each {
def task = project.tasks.findByPath(it)
if(task) {
if (task) {
task.enabled = false
task.dependsOn(project.merge.into.tasks.findByPath(it))
}
@@ -120,7 +117,7 @@ class MergePlugin implements Plugin<Project> {
private void setupMaven(Project project) {
project.configurations.each { configuration ->
Conf2ScopeMapping mapping = project.conf2ScopeMappings.getMapping([configuration])
if(mapping.scope) {
if (mapping.scope) {
Configuration intoConfiguration = project.merge.into.configurations.create(
project.name + "-" + configuration.name)
configuration.excludeRules.each {
@@ -131,7 +128,7 @@ class MergePlugin implements Plugin<Project> {
configuration.dependencies.each {
def intoCompile = project.merge.into.configurations.getByName("compile")
// Protect against changing a compile scope dependency (SPR-10218)
if(!intoCompile.dependencies.contains(it)) {
if (!intoCompile.dependencies.contains(it)) {
intoConfiguration.dependencies.add(it)
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,6 @@ import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ProjectDependency;
/**
* Gradle plugin that automatically updates testCompile dependencies to include
* the test source sets of project dependencies.
@@ -41,11 +40,10 @@ class TestSourceSetDependenciesPlugin implements Plugin<Project> {
}
}
private void collectProjectDependencies(Set<ProjectDependency> projectDependencies,
Project project) {
for(def configurationName in ["compile", "optional", "provided", "testCompile"]) {
private void collectProjectDependencies(Set<ProjectDependency> projectDependencies, Project project) {
for (def configurationName in ["compile", "optional", "provided", "testCompile"]) {
Configuration configuration = project.getConfigurations().findByName(configurationName)
if(configuration) {
if (configuration) {
configuration.dependencies.findAll { it instanceof ProjectDependency }.each {
projectDependencies.add(it)
collectProjectDependencies(projectDependencies, it.dependencyProject)
+1 -1
View File
@@ -1 +1 @@
version=4.0.4.BUILD-SNAPSHOT
version=4.1.0.RC2
+15 -5
View File
@@ -5,19 +5,19 @@ import org.gradle.plugins.ide.eclipse.model.SourceFolder
apply plugin: "propdeps-eclipse"
apply plugin: "propdeps-idea"
// Until eclipse fully supports Java 8 use 1.7 source level
eclipse.jdt {
sourceCompatibility = 1.7
targetCompatibility = 1.7
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
// Replace classpath entries with project dependencies (GRADLE-1116)
// http://issues.gradle.org/browse/GRADLE-1116
eclipse.classpath.file.whenMerged { classpath ->
def regexp = /.*?\/([^\/]+)\/build\/[^\/]+\/(?:main|test)/ // only match those that end in main or test (avoids removing necessary entries like build/classes/jaxb)
def projectOutputDependencies = classpath.entries.findAll { entry -> entry.path =~ regexp }
projectOutputDependencies.each { entry ->
def matcher = (entry.path =~ regexp)
if(matcher) {
if (matcher) {
def projectName = matcher[0][1]
def path = "/${projectName}"
if(!classpath.entries.find { e -> e instanceof ProjectDependency && e.path == path }) {
@@ -47,10 +47,20 @@ eclipse.classpath.file.whenMerged { classpath ->
}
}
// Ensure project dependencies come after 3rd-party libs (SPR-11836)
// https://jira.spring.io/browse/SPR-11836
eclipse.classpath.file.whenMerged { classpath ->
classpath.entries.findAll { it instanceof ProjectDependency }.each {
// delete from original position
classpath.entries.remove(it)
// append to end of classpath
classpath.entries.add(it)
}
}
// Allow projects to be used as WPT modules
eclipse.project.natures "org.eclipse.wst.common.project.facet.core.nature"
// Include project specific settings
task eclipseSettings(type: Copy) {
from rootProject.files(
Binary file not shown.
+2 -2
View File
@@ -1,6 +1,6 @@
#Wed Feb 12 23:28:21 CET 2014
#Wed Apr 30 13:06:57 CEST 2014
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=http\://services.gradle.org/distributions/gradle-1.11-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-1.12-all.zip
+51 -35
View File
@@ -1,22 +1,38 @@
STS_TEST_VERSION='2.9.2.RELEASE'
cd `dirname $0`
clear
cat <<EOM
-----------------------------------------------------------------------
Spring Framework Eclipse/STS project import guide
------------------------------------------------------------------------
Spring Framework - Eclipse/STS Project Import Guide
This script will guide you through the process of importing the
Spring Framework sources into Eclipse/STS. It is recommended that you
have a recent version of the SpringSource Tool Suite (this script has
been tested against STS $STS_TEST_VERSION), but at the minimum you will
need Eclipse + AJDT.
This script will guide you through the process of importing the Spring
Framework projects into Eclipse or the Spring Tool Suite (STS). It is
recommended that you have a recent version of Eclipse or STS. As a bare
minimum you will need Eclipse with full Java 8 support, AJDT, and the
Groovy Compiler.
If you need to download and install STS, please do that now by
visiting http://spring.io/tools/sts/all
This script has been tested against:
Otherwise, press enter and we'll begin.
- Eclipse: integration build I20140528-2000 (Luna 4.4)
- STS: nightly build 3.6.0.CI-B1890632 (Luna 4.4)
- AJDT: CI build AJDT-AE44-66 (Luna 4.4)
If you need to download and install Eclipse or STS, please do that now
by visiting one of the following sites:
- Eclipse downloads: http://download.eclipse.org/eclipse/downloads/
- STS downloads: http://spring.io/tools/sts/all
- STS nightly builds: http://dist.springsource.com/snapshot/STS/nightly-distributions.html
If you need to install a recent CI build for AJDT (i.e., so that the
spring-aspects module properly compiles in Eclipse/STS), click on the
"Artifacts" tab of a successful CI build and copy the link to the
"update site" which you can then use to install or update AJDT within
Eclipse/STS.
- AJDT CI builds: https://build.spring.io/browse/AJDT-AE44
Once Eclipse/STS is installed, press enter, and we'll begin.
EOM
read
@@ -31,14 +47,14 @@ COMMAND="./gradlew cleanEclipse :spring-oxm:compileTestJava eclipse -x :eclipse"
cat <<EOM
-----------------------------------------------------------------------
------------------------------------------------------------------------
STEP 1: Generate subproject Eclipse metadata
The first step will be to generate Eclipse project metadata for each
of the spring-* subprojects. This happens via the built-in
"Gradle wrapper" script (./gradlew in this directory). If this is your
first time using the Gradle wrapper, this step may take a few minutes
while a Gradle distribution is downloaded for you.
The first step will be to generate Eclipse project metadata for each of
the spring-* subprojects. This happens via the built-in "Gradle wrapper"
script (./gradlew in this directory). If this is your first time using
the Gradle wrapper, this step may take a few minutes while a Gradle
distribution is downloaded for you.
The command run will be:
@@ -53,16 +69,16 @@ $COMMAND || exit
cat <<EOM
-----------------------------------------------------------------------
------------------------------------------------------------------------
STEP 2: Import subprojects into Eclipse/STS
Within Eclipse/STS, do the following:
File > Import... > Existing Projects into Workspace
> When prompted for the 'root directory', provide $PWD
> Press enter. You will see the modules show up under "Projects"
> All projects should be selected/checked. Click Finish.
> When the project import is complete, you should have no errors.
- File > Import... > Existing Projects into Workspace
- When prompted for the 'root directory', provide $PWD.
- Press enter. You will see the modules show up under "Projects".
- All projects should be selected/checked. Click Finish.
- When the project import is complete, you should have no errors.
When the above is complete, return here and press the enter key.
EOM
@@ -73,13 +89,13 @@ COMMAND="./gradlew :eclipse"
cat <<EOM
-----------------------------------------------------------------------
STEP 3: generate root project Eclipse metadata
------------------------------------------------------------------------
STEP 3: Generate root project Eclipse metadata
Unfortunately, Eclipse does not allow for importing project
hierarchies, so we had to skip root project metadata generation in the
during step 1. In this step we simply generate root project metadata
so you can import it in the next step.
Unfortunately, Eclipse does not support importing project hierarchies,
so we had to skip root project metadata generation during step 1. In
this step we simply generate root project metadata so that you can
import it in the next step.
The command run will be:
@@ -93,7 +109,7 @@ read
$COMMAND || exit
cat <<EOM
-----------------------------------------------------------------------
------------------------------------------------------------------------
STEP 4: Import root project into Eclipse/STS
Follow the project import steps listed in step 2 above to import the
@@ -105,14 +121,14 @@ EOM
read
cat <<EOM
-----------------------------------------------------------------------
------------------------------------------------------------------------
STEP 5: Enable Git support for all projects
- In the Eclipse/STS Package Explorer, select all spring* projects.
- Right-click to open the context menu and select Team > Share Project...
- In the Share Project dialog that appears, select Git and press Next
- Check "Use or create repository in parent folder of project"
- Click Finish
- In the Share Project dialog that appears, select Git and press Next.
- Check "Use or create repository in parent folder of project".
- Click Finish.
When complete, you'll have Git support enabled for all projects.
+7 -7
View File
@@ -1,21 +1,21 @@
The following has been tested against Intellij IDEA 12.0
The following has been tested against Intellij IDEA 13.1
## Steps
_Within your locally cloned spring-framework working directory:_
1. Generate IDEA metadata with `./gradlew :spring-oxm:compileTestJava cleanIdea idea`
2. Import into IDEA as usual
3. Set the Project JDK as appropriate
4. Add git support
1. Pre-compile `spring-oxm` with `./gradlew cleanIdea :spring-oxm:compileTestJava`
2. Import into IDEA (File->import project->import from external model->Gradle)
3. Set the Project JDK as appropriate (1.8+)
4. Exclude the `spring-aspects` module (Go to File->Project Structure->Modules)
5. Code away
## Known issues
1. Those steps don't work currently for Intellij IDEA 13+
1. `spring-oxm` should be pre-compiled since it's using repackaged dependencies (see *RepackJar tasks)
2. `spring-aspects` does not compile out of the box due to references to aspect types unknown to IDEA.
See http://youtrack.jetbrains.com/issue/IDEA-64446 for details. In the meantime, the 'spring-aspects'
module has been excluded from the overall project to avoid compilation errors.
should be excluded from the overall project to avoid compilation errors.
3. While all JUnit tests pass from the command line with Gradle, many will fail when run from IDEA.
Resolving this is a work in progress. If attempting to run all JUnit tests from within IDEA, you will
likely need to set the following VM options to avoid out of memory errors:
+1 -1
View File
@@ -21,7 +21,7 @@ include "spring-tx"
include "spring-web"
include "spring-webmvc"
include "spring-webmvc-portlet"
include "spring-webmvc-tiles3"
include "spring-webmvc-tiles2"
include "spring-websocket"
include "spring-framework-bom"
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -240,7 +240,7 @@ public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedence
setArgumentNamesFromStringArray(tokens);
}
public void setArgumentNamesFromStringArray(String[] args) {
public void setArgumentNamesFromStringArray(String... args) {
this.argumentNames = new String[args.length];
for (int i = 0; i < args.length; i++) {
this.argumentNames[i] = StringUtils.trimWhitespace(args[i]);
@@ -29,7 +29,6 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.patterns.NamePattern;
import org.aspectj.weaver.reflect.ReflectionWorld;
import org.aspectj.weaver.reflect.ReflectionWorld.ReflectionWorldException;
import org.aspectj.weaver.reflect.ShadowMatchImpl;
import org.aspectj.weaver.tools.ContextBasedMatcher;
@@ -108,6 +107,8 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
private BeanFactory beanFactory;
private transient ClassLoader pointcutClassLoader;
private transient PointcutExpression pointcutExpression;
private transient Map<Method, ShadowMatch> shadowMatchCache = new ConcurrentHashMap<Method, ShadowMatch>(32);
@@ -185,20 +186,13 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
throw new IllegalStateException("Must set property 'expression' before attempting to match");
}
if (this.pointcutExpression == null) {
this.pointcutExpression = buildPointcutExpression();
this.pointcutClassLoader = (this.beanFactory instanceof ConfigurableBeanFactory ?
((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() :
ClassUtils.getDefaultClassLoader());
this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
}
}
/**
* Build the underlying AspectJ pointcut expression.
*/
private PointcutExpression buildPointcutExpression() {
ClassLoader cl = (this.beanFactory instanceof ConfigurableBeanFactory ?
((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() :
ClassUtils.getDefaultClassLoader());
return buildPointcutExpression(cl);
}
/**
* Build the underlying AspectJ pointcut expression.
*/
@@ -252,23 +246,22 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
public boolean matches(Class<?> targetClass) {
checkReadyToMatch();
try {
return this.pointcutExpression.couldMatchJoinPointsInType(targetClass);
}
catch (ReflectionWorldException rwe) {
logger.debug("PointcutExpression matching rejected target class", rwe);
try {
// Actually this is still a "maybe" - treat the pointcut as dynamic if we don't know enough yet
return getFallbackPointcutExpression(targetClass).couldMatchJoinPointsInType(targetClass);
return this.pointcutExpression.couldMatchJoinPointsInType(targetClass);
}
catch (BCException bce) {
logger.debug("Fallback PointcutExpression matching rejected target class", bce);
return false;
catch (ReflectionWorldException ex) {
logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);
// Actually this is still a "maybe" - treat the pointcut as dynamic if we don't know enough yet
PointcutExpression fallbackExpression = getFallbackPointcutExpression(targetClass);
if (fallbackExpression != null) {
return fallbackExpression.couldMatchJoinPointsInType(targetClass);
}
}
}
catch (BCException ex) {
logger.debug("PointcutExpression matching rejected target class", ex);
return false;
}
return false;
}
@Override
@@ -365,12 +358,19 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
/**
* Get a new pointcut expression based on a target class's loader, rather
* than the default.
* Get a new pointcut expression based on a target class's loader rather than the default.
*/
private PointcutExpression getFallbackPointcutExpression(Class<?> targetClass) {
ClassLoader classLoader = targetClass.getClassLoader();
return (classLoader != null ? buildPointcutExpression(classLoader) : this.pointcutExpression);
try {
ClassLoader classLoader = targetClass.getClassLoader();
if (classLoader != null && classLoader != this.pointcutClassLoader) {
return buildPointcutExpression(classLoader);
}
}
catch (Throwable ex) {
logger.debug("Failed to create fallback PointcutExpression", ex);
}
return null;
}
private RuntimeTestWalker getRuntimeTestWalker(ShadowMatch shadowMatch) {
@@ -396,46 +396,51 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
if (shadowMatch == null) {
synchronized (this.shadowMatchCache) {
// Not found - now check again with full lock...
PointcutExpression fallbackExpression = null;
Method methodToMatch = targetMethod;
PointcutExpression fallbackPointcutExpression = null;
shadowMatch = this.shadowMatchCache.get(methodToMatch);
shadowMatch = this.shadowMatchCache.get(targetMethod);
if (shadowMatch == null) {
try {
shadowMatch = this.pointcutExpression.matchesMethodExecution(targetMethod);
shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch);
}
catch (ReflectionWorld.ReflectionWorldException ex) {
catch (ReflectionWorldException ex) {
// Failed to introspect target method, probably because it has been loaded
// in a special ClassLoader. Let's try the original method instead...
// in a special ClassLoader. Let's try the declaring ClassLoader instead...
try {
fallbackPointcutExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
shadowMatch = fallbackPointcutExpression.matchesMethodExecution(methodToMatch);
}
catch (ReflectionWorld.ReflectionWorldException ex2) {
if (targetMethod == originalMethod) {
shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);
fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
if (fallbackExpression != null) {
shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
}
else {
try {
shadowMatch = this.pointcutExpression.matchesMethodExecution(originalMethod);
}
catch (ReflectionWorld.ReflectionWorldException ex3) {
// Could neither introspect the target class nor the proxy class ->
// let's simply consider this method as non-matching.
methodToMatch = originalMethod;
fallbackPointcutExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
try {
shadowMatch = fallbackPointcutExpression.matchesMethodExecution(methodToMatch);
}
catch (ReflectionWorld.ReflectionWorldException ex4) {
shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);
}
}
catch (ReflectionWorldException ex2) {
fallbackExpression = null;
}
}
if (shadowMatch == null && targetMethod != originalMethod) {
methodToMatch = originalMethod;
try {
shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch);
}
catch (ReflectionWorldException ex3) {
// Could neither introspect the target class nor the proxy class ->
// let's try the original method's declaring class before we give up...
try {
fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
if (fallbackExpression != null) {
shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
}
}
catch (ReflectionWorldException ex4) {
fallbackExpression = null;
}
}
}
if (shadowMatch.maybeMatches() && fallbackPointcutExpression != null) {
if (shadowMatch == null) {
shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);
}
else if (shadowMatch.maybeMatches() && fallbackExpression != null) {
shadowMatch = new DefensiveShadowMatch(shadowMatch,
fallbackPointcutExpression.matchesMethodExecution(methodToMatch));
fallbackExpression.matchesMethodExecution(methodToMatch));
}
this.shadowMatchCache.put(targetMethod, shadowMatch);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,24 +40,24 @@ public class AspectJExpressionPointcutAdvisor extends AbstractGenericPointcutAdv
this.pointcut.setExpression(expression);
}
public String getExpression() {
return this.pointcut.getExpression();
}
public void setLocation(String location) {
this.pointcut.setLocation(location);
}
public void setParameterTypes(Class<?>[] types) {
this.pointcut.setParameterTypes(types);
}
public void setParameterNames(String[] names) {
this.pointcut.setParameterNames(names);
}
public String getLocation() {
return this.pointcut.getLocation();
}
public String getExpression() {
return this.pointcut.getExpression();
public void setParameterTypes(Class<?>[] types) {
this.pointcut.setParameterTypes(types);
}
public void setParameterNames(String... names) {
this.pointcut.setParameterNames(names);
}
}
@@ -50,7 +50,7 @@ import org.springframework.util.ReflectionUtils;
* migrate to {@code ShadowMatch.getVariablesInvolvedInRuntimeTest()}
* or some similar operation.
*
* <p>See <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=151593"/>.
* <p>See <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=151593"/>Bug 151593</a>
*
* @author Adrian Colyer
* @author Ramnivas Laddad
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author 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,8 +19,7 @@ package org.springframework.aop.aspectj.annotation;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.core.annotation.OrderUtils;
import org.springframework.util.ClassUtils;
/**
@@ -110,10 +109,7 @@ public class BeanFactoryAspectInstanceFactory implements MetadataAwareAspectInst
if (Ordered.class.isAssignableFrom(type) && this.beanFactory.isSingleton(this.name)) {
return ((Ordered) this.beanFactory.getBean(this.name)).getOrder();
}
Order order = AnnotationUtils.findAnnotation(type, Order.class);
if (order != null) {
return order.value();
}
return OrderUtils.getOrder(type, Ordered.LOWEST_PRECEDENCE);
}
return Ordered.LOWEST_PRECEDENCE;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,7 @@ package org.springframework.aop.aspectj.annotation;
import org.springframework.aop.aspectj.SimpleAspectInstanceFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.core.annotation.OrderUtils;
/**
* Implementation of {@link MetadataAwareAspectInstanceFactory} that
@@ -51,20 +50,9 @@ public class SimpleMetadataAwareAspectInstanceFactory extends SimpleAspectInstan
return this.metadata;
}
/**
* Determine a fallback order for the case that the aspect instance
* does not express an instance-specific order through implementing
* the {@link org.springframework.core.Ordered} interface.
* <p>The default implementation simply returns {@code Ordered.LOWEST_PRECEDENCE}.
* @param aspectClass the aspect class
*/
@Override
protected int getOrderForAspectClass(Class<?> aspectClass) {
Order order = AnnotationUtils.findAnnotation(aspectClass, Order.class);
if (order != null) {
return order.value();
}
return Ordered.LOWEST_PRECEDENCE;
return OrderUtils.getOrder(aspectClass, Ordered.LOWEST_PRECEDENCE);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,7 @@ package org.springframework.aop.aspectj.annotation;
import org.springframework.aop.aspectj.SingletonAspectInstanceFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.core.annotation.OrderUtils;
/**
* Implementation of {@link MetadataAwareAspectInstanceFactory} that is backed
@@ -53,19 +52,9 @@ public class SingletonMetadataAwareAspectInstanceFactory extends SingletonAspect
return this.metadata;
}
/**
* Check whether the aspect class carries an
* {@link org.springframework.core.annotation.Order} annotation,
* falling back to {@code Ordered.LOWEST_PRECEDENCE}.
* @see org.springframework.core.annotation.Order
*/
@Override
protected int getOrderForAspectClass(Class<?> aspectClass) {
Order order = AnnotationUtils.findAnnotation(aspectClass, Order.class);
if (order != null) {
return order.value();
}
return Ordered.LOWEST_PRECEDENCE;
return OrderUtils.getOrder(aspectClass, Ordered.LOWEST_PRECEDENCE);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author 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,10 +21,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.springframework.aop.Advisor;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.util.ClassUtils;
/**
* Base class for {@link BeanPostProcessor} implementations that apply a
@@ -34,21 +31,12 @@ import org.springframework.util.ClassUtils;
* @since 3.2
*/
@SuppressWarnings("serial")
public abstract class AbstractAdvisingBeanPostProcessor extends ProxyConfig
implements BeanPostProcessor, BeanClassLoaderAware, Ordered {
public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor {
protected Advisor advisor;
protected boolean beforeExistingAdvisors = false;
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
/**
* This should run after all other post-processors, so that it can just add
* an advisor to existing proxies rather than double-proxy.
*/
private int order = Ordered.LOWEST_PRECEDENCE;
private final Map<Class<?>, Boolean> eligibleBeans = new ConcurrentHashMap<Class<?>, Boolean>(64);
@@ -65,20 +53,6 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyConfig
this.beforeExistingAdvisors = beforeExistingAdvisors;
}
@Override
public void setBeanClassLoader(ClassLoader beanClassLoader) {
this.beanClassLoader = beanClassLoader;
}
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
@@ -91,9 +65,11 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyConfig
// Ignore AOP infrastructure such as scoped proxies.
return bean;
}
if (isEligible(bean, beanName)) {
if (bean instanceof Advised) {
Advised advised = (Advised) bean;
if (bean instanceof Advised) {
Advised advised = (Advised) bean;
if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
// Add our local Advisor to the existing proxy's Advisor chain...
if (this.beforeExistingAdvisors) {
advised.addAdvisor(0, this.advisor);
}
@@ -102,32 +78,50 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyConfig
}
return bean;
}
else {
ProxyFactory proxyFactory = new ProxyFactory(bean);
// Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
proxyFactory.copyFrom(this);
proxyFactory.addAdvisor(this.advisor);
return proxyFactory.getProxy(this.beanClassLoader);
}
if (isEligible(bean, beanName)) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
proxyFactory.setTarget(bean);
if (!proxyFactory.isProxyTargetClass()) {
evaluateProxyInterfaces(bean.getClass(), proxyFactory);
}
proxyFactory.addAdvisor(this.advisor);
return proxyFactory.getProxy(getProxyClassLoader());
}
else {
// No async proxy needed.
return bean;
}
// No async proxy needed.
return bean;
}
/**
* Check whether the given bean is eligible for advising with this
* post-processor's {@link Advisor}.
* <p>Implements caching of {@code canApply} results per bean target class.
* <p>Delegates to {@link #isEligible(Class)} for target class checking.
* Can be overridden e.g. to specifically exclude certain beans by name.
* <p>Note: Only called for regular bean instances but not for existing
* proxy instances which implement {@link Advised} and allow for adding
* the local {@link Advisor} to the existing proxy's {@link Advisor} chain.
* For the latter, {@link #isEligible(Class)} is being called directly,
* with the actual target class behind the existing proxy (as determined
* by {@link AopUtils#getTargetClass(Object)}).
* @param bean the bean instance
* @param beanName the name of the bean
* @see AopUtils#getTargetClass(Object)
* @see AopUtils#canApply(Advisor, Class)
* @see #isEligible(Class)
*/
protected boolean isEligible(Object bean, String beanName) {
Class<?> targetClass = AopUtils.getTargetClass(bean);
return isEligible(bean.getClass());
}
/**
* Check whether the given class is eligible for advising with this
* post-processor's {@link Advisor}.
* <p>Implements caching of {@code canApply} results per bean target class.
* @param targetClass the class to check against
* @see AopUtils#canApply(Advisor, Class)
*/
protected boolean isEligible(Class<?> targetClass) {
Boolean eligible = this.eligibleBeans.get(targetClass);
if (eligible != null) {
return eligible;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2014 the original author 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,10 +32,10 @@ public interface AdvisorChainFactory {
* for the given advisor chain configuration.
* @param config the AOP configuration in the form of an Advised object
* @param method the proxied method
* @param targetClass the target class
* @param targetClass the target class (may be {@code null} to indicate a proxy without
* target object, in which case the method's declaring class is the next best option)
* @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
*/
List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class<?> targetClass);
List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class<?> targetClass);
}
@@ -172,7 +172,7 @@ class CglibAopProxy implements AopProxy, Serializable {
}
// Validate the class, writing log messages as necessary.
validateClassIfNecessary(proxySuperClass);
validateClassIfNecessary(proxySuperClass, classLoader);
// Configure CGLIB Enhancer...
Enhancer enhancer = createEnhancer();
@@ -239,11 +239,11 @@ class CglibAopProxy implements AopProxy, Serializable {
* Checks to see whether the supplied {@code Class} has already been validated and
* validates it if not.
*/
private void validateClassIfNecessary(Class<?> proxySuperClass) {
if (logger.isWarnEnabled()) {
private void validateClassIfNecessary(Class<?> proxySuperClass, ClassLoader proxyClassLoader) {
if (logger.isInfoEnabled()) {
synchronized (validatedClasses) {
if (!validatedClasses.containsKey(proxySuperClass)) {
doValidateClass(proxySuperClass);
doValidateClass(proxySuperClass, proxyClassLoader);
validatedClasses.put(proxySuperClass, Boolean.TRUE);
}
}
@@ -251,19 +251,28 @@ class CglibAopProxy implements AopProxy, Serializable {
}
/**
* Checks for final methods on the {@code Class} and writes warnings to the log
* for each one found.
* Checks for final methods on the given {@code Class}, as well as package-visible
* methods across ClassLoaders, and writes warnings to the log for each one found.
*/
private void doValidateClass(Class<?> proxySuperClass) {
if (logger.isWarnEnabled()) {
Method[] methods = proxySuperClass.getMethods();
private void doValidateClass(Class<?> proxySuperClass, ClassLoader proxyClassLoader) {
if (!Object.class.equals(proxySuperClass)) {
Method[] methods = proxySuperClass.getDeclaredMethods();
for (Method method : methods) {
if (!Object.class.equals(method.getDeclaringClass()) && !Modifier.isStatic(method.getModifiers()) &&
Modifier.isFinal(method.getModifiers())) {
logger.warn("Unable to proxy method [" + method + "] because it is final: " +
"All calls to this method via a proxy will be routed directly to the proxy.");
int mod = method.getModifiers();
if (!Modifier.isStatic(mod)) {
if (Modifier.isFinal(mod)) {
logger.info("Unable to proxy method [" + method + "] because it is final: " +
"All calls to this method via a proxy will NOT be routed to the target instance.");
}
else if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod) && !Modifier.isPrivate(mod) &&
proxyClassLoader != null && proxySuperClass.getClassLoader() != proxyClassLoader) {
logger.info("Unable to proxy method [" + method + "] because it is package-visible " +
"across different ClassLoaders: All calls to this method via a proxy will " +
"NOT be routed to the target instance.");
}
}
}
doValidateClass(proxySuperClass.getSuperclass(), proxyClassLoader);
}
}
@@ -604,7 +613,7 @@ class CglibAopProxy implements AopProxy, Serializable {
*/
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
private AdvisedSupport advised;
private final AdvisedSupport advised;
public DynamicAdvisedInterceptor(AdvisedSupport advised) {
this.advised = advised;
@@ -622,8 +631,8 @@ class CglibAopProxy implements AopProxy, Serializable {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// May be null Get as late as possible to minimize the time we
// "own" the target, in case it comes from a pool.
// May be null. Get as late as possible to minimize the time we
// "own" the target, in case it comes from a pool...
target = getTarget();
if (target != null) {
targetClass = target.getClass();
@@ -689,13 +698,13 @@ class CglibAopProxy implements AopProxy, Serializable {
private final MethodProxy methodProxy;
private boolean protectedMethod;
private final boolean publicMethod;
public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments,
Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
this.methodProxy = methodProxy;
this.protectedMethod = Modifier.isProtected(method.getModifiers());
this.publicMethod = Modifier.isPublic(method.getModifiers());
}
/**
@@ -704,11 +713,11 @@ class CglibAopProxy implements AopProxy, Serializable {
*/
@Override
protected Object invokeJoinpoint() throws Throwable {
if (this.protectedMethod) {
return super.invokeJoinpoint();
if (this.publicMethod) {
return this.methodProxy.invoke(this.target, this.arguments);
}
else {
return this.methodProxy.invoke(this.target, this.arguments);
return super.invokeJoinpoint();
}
}
}
@@ -829,8 +838,8 @@ class CglibAopProxy implements AopProxy, Serializable {
// of the target type. If so we know it never needs to have return type
// massage and can use a dispatcher.
// If the proxy is being exposed, then must use the interceptor the
// correct one is already configured. If the target is not static cannot
// use a Dispatcher because the target can not then be released.
// correct one is already configured. If the target is not static, then
// cannot use a dispatcher because the target cannot be released.
if (exposeProxy || !isStatic) {
return INVOKE_TARGET;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,19 +50,21 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class<?> targetClass) {
// This is somewhat tricky... we have to process introductions first,
// This is somewhat tricky... We have to process introductions first,
// but we need to preserve order in the ultimate list.
List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
boolean hasIntroductions = hasMatchingIntroductions(config, targetClass);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
for (Advisor advisor : config.getAdvisors()) {
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) {
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
@@ -78,7 +80,7 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
}
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) {
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
@@ -88,18 +90,19 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
/**
* Determine whether the Advisors contain matching introductions.
*/
private static boolean hasMatchingIntroductions(Advised config, Class<?> targetClass) {
private static boolean hasMatchingIntroductions(Advised config, Class<?> actualClass) {
for (int i = 0; i < config.getAdvisors().length; i++) {
Advisor advisor = config.getAdvisors()[i];
if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (ia.getClassFilter().matches(targetClass)) {
if (ia.getClassFilter().matches(actualClass)) {
return true;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,21 +21,18 @@ import java.io.Serializable;
import org.springframework.aop.SpringProxy;
/**
* Default {@link AopProxyFactory} implementation,
* creating either a CGLIB proxy or a JDK dynamic proxy.
* Default {@link AopProxyFactory} implementation, creating either a CGLIB proxy
* or a JDK dynamic proxy.
*
* <p>Creates a CGLIB proxy if one the following is true
* for a given {@link AdvisedSupport} instance:
* <p>Creates a CGLIB proxy if one the following is true for a given
* {@link AdvisedSupport} instance:
* <ul>
* <li>the "optimize" flag is set
* <li>the "proxyTargetClass" flag is set
* <li>the {@code optimize} flag is set
* <li>the {@code proxyTargetClass} flag is set
* <li>no proxy interfaces have been specified
* </ul>
*
* <p>Note that the CGLIB library classes have to be present on
* the class path if an actual CGLIB proxy needs to be created.
*
* <p>In general, specify "proxyTargetClass" to enforce a CGLIB proxy,
* <p>In general, specify {@code proxyTargetClass} to enforce a CGLIB proxy,
* or specify one or more interfaces to use a JDK dynamic proxy.
*
* @author Rod Johnson
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -75,7 +75,7 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
*/
/** We use a static Log to avoid serialization issues */
private static Log logger = LogFactory.getLog(JdkDynamicAopProxy.class);
private static final Log logger = LogFactory.getLog(JdkDynamicAopProxy.class);
/** Config used to configure this proxy */
private final AdvisedSupport advised;
@@ -215,8 +215,10 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
} else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,6 @@ package org.springframework.aop.framework;
import org.aopalliance.intercept.Interceptor;
import org.springframework.aop.TargetSource;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
@@ -47,9 +46,8 @@ public class ProxyFactory extends ProxyCreatorSupport {
* @param target the target object to be proxied
*/
public ProxyFactory(Object target) {
Assert.notNull(target, "Target object must not be null");
setInterfaces(ClassUtils.getAllInterfaces(target));
setTarget(target);
setInterfaces(ClassUtils.getAllInterfaces(target));
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -154,7 +154,7 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
* @see org.aopalliance.aop.Advice
* @see org.springframework.aop.target.SingletonTargetSource
*/
public void setInterceptorNames(String[] interceptorNames) {
public void setInterceptorNames(String... interceptorNames) {
this.interceptorNames = interceptorNames;
}
@@ -0,0 +1,145 @@
/*
* Copyright 2002-2014 the original author 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.framework;
import org.springframework.beans.factory.Aware;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.Ordered;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
* Base class with common functionality for proxy processors, in particular
* ClassLoader management and the {@link #evaluateProxyInterfaces} algorithm.
*
* @author Juergen Hoeller
* @since 4.1
* @see AbstractAdvisingBeanPostProcessor
* @see org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
*/
@SuppressWarnings("serial")
public class ProxyProcessorSupport extends ProxyConfig implements Ordered, BeanClassLoaderAware, AopInfrastructureBean {
/**
* This should run after all other processors, so that it can just add
* an advisor to existing proxies rather than double-proxy.
*/
private int order = Ordered.LOWEST_PRECEDENCE;
private ClassLoader proxyClassLoader = ClassUtils.getDefaultClassLoader();
private boolean classLoaderConfigured = false;
/**
* Set the ordering which will apply to this class's implementation
* of Ordered, used when applying multiple processors.
* <p>Default value is {@code Integer.MAX_VALUE}, meaning that it's non-ordered.
* @param order the ordering value
*/
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
/**
* Set the ClassLoader to generate the proxy class in.
* <p>Default is the bean ClassLoader, i.e. the ClassLoader used by the containing
* {@link org.springframework.beans.factory.BeanFactory} for loading all bean classes.
* This can be overridden here for specific proxies.
*/
public void setProxyClassLoader(ClassLoader classLoader) {
this.proxyClassLoader = classLoader;
this.classLoaderConfigured = (classLoader != null);
}
/**
* Return the configured proxy ClassLoader for this processor.
*/
protected ClassLoader getProxyClassLoader() {
return this.proxyClassLoader;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
if (!this.classLoaderConfigured) {
this.proxyClassLoader = classLoader;
}
}
/**
* Check the interfaces on the given bean class and apply them to the {@link ProxyFactory},
* if appropriate.
* <p>Calls {@link #isConfigurationCallbackInterface} and {@link #isInternalLanguageInterface}
* to filter for reasonable proxy interfaces, falling back to a target-class proxy otherwise.
* @param beanClass the class of the bean
* @param proxyFactory the ProxyFactory for the bean
*/
protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
boolean hasReasonableProxyInterface = false;
for (Class<?> ifc : targetInterfaces) {
if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&
ifc.getMethods().length > 0) {
hasReasonableProxyInterface = true;
break;
}
}
if (hasReasonableProxyInterface) {
// Must allow for introductions; can't just set interfaces to the target's interfaces only.
for (Class<?> ifc : targetInterfaces) {
proxyFactory.addInterface(ifc);
}
}
else {
proxyFactory.setProxyTargetClass(true);
}
}
/**
* Determine whether the given interface is just a container callback and
* therefore not to be considered as a reasonable proxy interface.
* <p>If no reasonable proxy interface is found for a given bean, it will get
* proxied with its full target class, assuming that as the user's intention.
* @param ifc the interface to check
* @return whether the given interface is just a container callback
*/
protected boolean isConfigurationCallbackInterface(Class<?> ifc) {
return (ifc.equals(InitializingBean.class) || ifc.equals(DisposableBean.class) ||
ObjectUtils.containsElement(ifc.getInterfaces(), Aware.class));
}
/**
* Determine whether the given interface is a well-known internal language interface
* and therefore not to be considered as a reasonable proxy interface.
* <p>If no reasonable proxy interface is found for a given bean, it will get
* proxied with its full target class, assuming that as the user's intention.
* @param ifc the interface to check
* @return whether the given interface is an internal language interface
*/
protected boolean isInternalLanguageInterface(Class<?> ifc) {
return ifc.getName().equals("groovy.lang.GroovyObject");
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,25 +33,18 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.aop.Advisor;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.aop.framework.ProxyConfig;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.ProxyProcessorSupport;
import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry;
import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry;
import org.springframework.aop.target.SingletonTargetSource;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.Aware;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
* {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
@@ -94,9 +87,8 @@ import org.springframework.util.ObjectUtils;
* @see DefaultAdvisorAutoProxyCreator
*/
@SuppressWarnings("serial")
public abstract class AbstractAutoProxyCreator extends ProxyConfig
implements SmartInstantiationAwareBeanPostProcessor, BeanClassLoaderAware, BeanFactoryAware,
Ordered, AopInfrastructureBean {
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
/**
* Convenience constant for subclasses: Return value for "do not proxy".
@@ -115,9 +107,6 @@ public abstract class AbstractAutoProxyCreator extends ProxyConfig
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
/** Default value is same as non-ordered */
private int order = Ordered.LOWEST_PRECEDENCE;
/** Default is global AdvisorAdapterRegistry */
private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
@@ -134,10 +123,6 @@ public abstract class AbstractAutoProxyCreator extends ProxyConfig
private TargetSourceCreator[] customTargetSourceCreators;
private ClassLoader proxyClassLoader = ClassUtils.getDefaultClassLoader();
private boolean classLoaderConfigured = false;
private BeanFactory beanFactory;
private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<Object, Boolean>(64);
@@ -151,21 +136,6 @@ public abstract class AbstractAutoProxyCreator extends ProxyConfig
private final Map<Object, Class<?>> proxyTypes = new ConcurrentHashMap<Object, Class<?>>(16);
/**
* Set the ordering which will apply to this class's implementation
* of Ordered, used when applying multiple BeanPostProcessors.
* <p>Default value is {@code Integer.MAX_VALUE}, meaning that it's non-ordered.
* @param order ordering value
*/
public final void setOrder(int order) {
this.order = order;
}
@Override
public final int getOrder() {
return this.order;
}
/**
* Set whether or not the proxy should be frozen, preventing advice
* from being added to it once it is created.
@@ -205,7 +175,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyConfig
* Ordering is significant: The TargetSource returned from the first matching
* TargetSourceCreator (that is, the first that returns non-null) will be used.
*/
public void setCustomTargetSourceCreators(TargetSourceCreator[] targetSourceCreators) {
public void setCustomTargetSourceCreators(TargetSourceCreator... targetSourceCreators) {
this.customTargetSourceCreators = targetSourceCreators;
}
@@ -216,7 +186,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyConfig
* This is perfectly valid, if "specific" interceptors such as matching
* Advisors are all we want.
*/
public void setInterceptorNames(String[] interceptorNames) {
public void setInterceptorNames(String... interceptorNames) {
this.interceptorNames = interceptorNames;
}
@@ -228,24 +198,6 @@ public abstract class AbstractAutoProxyCreator extends ProxyConfig
this.applyCommonInterceptorsFirst = applyCommonInterceptorsFirst;
}
/**
* Set the ClassLoader to generate the proxy class in.
* <p>Default is the bean ClassLoader, i.e. the ClassLoader used by the
* containing BeanFactory for loading all bean classes. This can be
* overridden here for specific proxies.
*/
public void setProxyClassLoader(ClassLoader classLoader) {
this.proxyClassLoader = classLoader;
this.classLoaderConfigured = (classLoader != null);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
if (!this.classLoaderConfigured) {
this.proxyClassLoader = classLoader;
}
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
@@ -466,7 +418,6 @@ public abstract class AbstractAutoProxyCreator extends ProxyConfig
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
// Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
@@ -491,7 +442,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyConfig
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(this.proxyClassLoader);
return proxyFactory.getProxy(getProxyClassLoader());
}
/**
@@ -508,47 +459,6 @@ public abstract class AbstractAutoProxyCreator extends ProxyConfig
AutoProxyUtils.shouldProxyTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName));
}
/**
* Check the interfaces on the given bean class and apply them to the ProxyFactory,
* if appropriate.
* <p>Calls {@link #isConfigurationCallbackInterface} to filter for reasonable
* proxy interfaces, falling back to a target-class proxy otherwise.
* @param beanClass the class of the bean
* @param proxyFactory the ProxyFactory for the bean
*/
private void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
boolean hasReasonableProxyInterface = false;
for (Class<?> ifc : targetInterfaces) {
if (!isConfigurationCallbackInterface(ifc) && ifc.getMethods().length > 0) {
hasReasonableProxyInterface = true;
break;
}
}
if (hasReasonableProxyInterface) {
// Must allow for introductions; can't just set interfaces to the target's interfaces only.
for (Class<?> ifc : targetInterfaces) {
proxyFactory.addInterface(ifc);
}
}
else {
proxyFactory.setProxyTargetClass(true);
}
}
/**
* Determine whether the given interface is just a container callback and
* therefore not to be considered as a reasonable proxy interface.
* <p>If no reasonable proxy interface is found for a given bean, it will get
* proxied with its full target class, assuming that as the user's intention.
* @param ifc the interface to check
* @return whether the given interface is just a container callback
*/
protected boolean isConfigurationCallbackInterface(Class<?> ifc) {
return (ifc.equals(InitializingBean.class) || ifc.equals(DisposableBean.class) ||
ObjectUtils.containsElement(ifc.getInterfaces(), Aware.class));
}
/**
* Return whether the Advisors returned by the subclass are pre-filtered
* to match the bean's target class already, allowing the ClassFilter check
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -60,7 +60,7 @@ public class BeanNameAutoProxyCreator extends AbstractAutoProxyCreator {
* @see org.springframework.beans.factory.FactoryBean
* @see org.springframework.beans.factory.BeanFactory#FACTORY_BEAN_PREFIX
*/
public void setBeanNames(String[] beanNames) {
public void setBeanNames(String... beanNames) {
Assert.notEmpty(beanNames, "'beanNames' must not be empty");
this.beanNames = new ArrayList<String>(beanNames.length);
for (String mappedName : beanNames) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author 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,10 +21,10 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
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.AsyncListenableTaskExecutor;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.support.TaskExecutorAdapter;
import org.springframework.util.Assert;
@@ -41,6 +41,7 @@ import org.springframework.util.StringUtils;
* bean to be used when executing it, e.g. through an annotation attribute.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1.2
*/
public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
@@ -81,13 +82,14 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
* Set the {@link BeanFactory} to be used when looking up executors by qualifier.
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
/**
* Determine the specific executor to use when executing the given method.
* Should preferably return an {@link AsyncListenableTaskExecutor} implementation.
* @return the executor to use (or {@code null}, but just if no default executor has been set)
*/
protected AsyncTaskExecutor determineAsyncExecutor(Method method) {
@@ -104,8 +106,8 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
else if (executorToUse == null) {
return null;
}
executor = (executorToUse instanceof AsyncTaskExecutor ?
(AsyncTaskExecutor) executorToUse : new TaskExecutorAdapter(executorToUse));
executor = (executorToUse instanceof AsyncListenableTaskExecutor ?
(AsyncListenableTaskExecutor) executorToUse : new TaskExecutorAdapter(executorToUse));
this.executors.put(method, executor);
}
return executor;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,13 +23,17 @@ import java.util.concurrent.Future;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.Ordered;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.concurrent.ListenableFuture;
/**
* AOP Alliance {@code MethodInterceptor} that processes method invocations
@@ -46,12 +50,18 @@ import org.springframework.util.ReflectionUtils;
* (like Spring's {@link org.springframework.scheduling.annotation.AsyncResult}
* or EJB 3.1's {@code javax.ejb.AsyncResult}).
*
* <p>When the return type is {@code java.util.concurrent.Future}, any exception thrown
* during the execution can be accessed and managed by the caller. With {@code void}
* return type however, such exceptions cannot be transmitted back. In that case an
* {@link AsyncUncaughtExceptionHandler} can be registered to process such exceptions.
*
* <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
* @author Stephane Nicoll
* @since 3.0
* @see org.springframework.scheduling.annotation.Async
* @see org.springframework.scheduling.annotation.AsyncAnnotationAdvisor
@@ -60,13 +70,36 @@ import org.springframework.util.ReflectionUtils;
public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport
implements MethodInterceptor, Ordered {
private final Log logger = LogFactory.getLog(getClass());
private AsyncUncaughtExceptionHandler exceptionHandler;
/**
* Create a new {@code AsyncExecutionInterceptor}.
* @param executor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor}
* or {@link java.util.concurrent.ExecutorService}) to delegate to.
* @param defaultExecutor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor}
* or {@link java.util.concurrent.ExecutorService}) to delegate to
* @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use
*/
public AsyncExecutionInterceptor(Executor executor) {
super(executor);
public AsyncExecutionInterceptor(Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) {
super(defaultExecutor);
this.exceptionHandler = exceptionHandler;
}
/**
* Create a new instance with a default {@link AsyncUncaughtExceptionHandler}.
*/
public AsyncExecutionInterceptor(Executor defaultExecutor) {
this(defaultExecutor, new SimpleAsyncUncaughtExceptionHandler());
}
/**
* Supply the {@link AsyncUncaughtExceptionHandler} to use to handle exceptions
* thrown by invoking asynchronous methods with a {@code void} return type.
*/
public void setExceptionHandler(AsyncUncaughtExceptionHandler exceptionHandler) {
this.exceptionHandler = exceptionHandler;
}
@@ -81,46 +114,78 @@ public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport
public Object invoke(final MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
AsyncTaskExecutor executor = determineAsyncExecutor(specificMethod);
AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
if (executor == null) {
throw new IllegalStateException(
"No executor specified and no default executor set on AsyncExecutionInterceptor either");
}
Future<?> result = executor.submit(
new Callable<Object>() {
@Override
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;
Callable<Object> task = new Callable<Object>() {
@Override
public Object call() throws Exception {
try {
Object result = invocation.proceed();
if (result instanceof Future) {
return ((Future<?>) result).get();
}
});
}
catch (Throwable ex) {
handleError(ex, userDeclaredMethod, invocation.getArguments());
}
return null;
}
};
if (Future.class.isAssignableFrom(invocation.getMethod().getReturnType())) {
return result;
Class<?> returnType = invocation.getMethod().getReturnType();
if (ListenableFuture.class.isAssignableFrom(returnType)) {
return ((AsyncListenableTaskExecutor) executor).submitListenable(task);
}
else if (Future.class.isAssignableFrom(returnType)) {
return executor.submit(task);
}
else {
executor.submit(task);
return null;
}
}
/**
* Handles a fatal error thrown while asynchronously invoking the specified
* {@link Method}.
* <p>If the return type of the method is a {@link Future} object, the original
* exception can be propagated by just throwing it at the higher level. However,
* for all other cases, the exception will not be transmitted back to the client.
* In that later case, the current {@link AsyncUncaughtExceptionHandler} will be
* used to manage such exception.
* @param ex the exception to handle
* @param method the method that was invoked
* @param params the parameters used to invoke the method
*/
protected void handleError(Throwable ex, Method method, Object... params) throws Exception {
if (method.getReturnType().isAssignableFrom(Future.class)) {
ReflectionUtils.rethrowException(ex);
}
else {
// Could not transmit the exception to the caller with default executor
try {
this.exceptionHandler.handleUncaughtException(ex, method, params);
}
catch (Throwable ex2) {
logger.error("Exception handler for async method '" + method.toGenericString() +
"' threw unexpected exception itself", ex2);
}
}
}
/**
* 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
* @see #determineAsyncExecutor(Method)
*/
@Override
protected String getExecutorQualifier(Method method) {
@@ -0,0 +1,42 @@
/*
* Copyright 2002-2014 the original author 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;
/**
* A strategy for handling uncaught exceptions thrown from asynchronous methods.
*
* <p>An asynchronous method usually returns a {@link java.util.concurrent.Future}
* instance that gives access to the underlying exception. When the method does
* not provide that return type, this handler can be used to managed such
* uncaught exceptions.
*
* @author Stephane Nicoll
* @since 4.1
*/
public interface AsyncUncaughtExceptionHandler {
/**
* Handle the given uncaught exception thrown from an asynchronous method.
* @param ex the exception thrown from the asynchronous method
* @param method the asynchronous method
* @param params the parameters used to invoked the method
*/
void handleUncaughtException(Throwable ex, Method method, Object... params);
}
@@ -258,7 +258,7 @@ public class CustomizableTraceInterceptor extends AbstractTraceInterceptor {
return returnValue;
}
catch (Throwable ex) {
if(stopWatch.isRunning()) {
if (stopWatch.isRunning()) {
stopWatch.stop();
}
exitThroughException = true;
@@ -268,7 +268,7 @@ public class CustomizableTraceInterceptor extends AbstractTraceInterceptor {
}
finally {
if (!exitThroughException) {
if(stopWatch.isRunning()) {
if (stopWatch.isRunning()) {
stopWatch.stop();
}
writeToLog(logger,
@@ -0,0 +1,42 @@
/*
* Copyright 2002-2014 the original author 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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* A default {@link AsyncUncaughtExceptionHandler} that simply logs the exception.
*
* @author Stephane Nicoll
* @since 4.1
*/
public class SimpleAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
private final Log logger = LogFactory.getLog(SimpleAsyncUncaughtExceptionHandler.class);
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
if (logger.isErrorEnabled()) {
logger.error(String.format("Unexpected error occurred invoking async " +
"method '%s'.", method), ex);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,7 +47,7 @@ public class NameMatchMethodPointcut extends StaticMethodMatcherPointcut impleme
* @see #setMappedNames
*/
public void setMappedName(String mappedName) {
setMappedNames(new String[] {mappedName});
setMappedNames(mappedName);
}
/**
@@ -55,7 +55,7 @@ public class NameMatchMethodPointcut extends StaticMethodMatcherPointcut impleme
* Matching will be the union of all these; if any match,
* the pointcut matches.
*/
public void setMappedNames(String[] mappedNames) {
public void setMappedNames(String... mappedNames) {
this.mappedNames = new LinkedList<String>();
if (mappedNames != null) {
this.mappedNames.addAll(Arrays.asList(mappedNames));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -68,7 +68,7 @@ public class NameMatchMethodPointcutAdvisor extends AbstractGenericPointcutAdvis
* the pointcut matches.
* @see NameMatchMethodPointcut#setMappedNames
*/
public void setMappedNames(String[] mappedNames) {
public void setMappedNames(String... mappedNames) {
this.pointcut.setMappedNames(mappedNames);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -98,7 +98,7 @@ public class RegexpMethodPointcutAdvisor extends AbstractGenericPointcutAdvisor
* @see #setPatterns
*/
public void setPattern(String pattern) {
setPatterns(new String[] {pattern});
setPatterns(pattern);
}
/**
@@ -108,7 +108,7 @@ public class RegexpMethodPointcutAdvisor extends AbstractGenericPointcutAdvisor
* patterns matches, the pointcut matches.
* @see AbstractRegexpMethodPointcut#setPatterns
*/
public void setPatterns(String[] patterns) {
public void setPatterns(String... patterns) {
this.patterns = patterns;
}
@@ -4,4 +4,5 @@ http\://www.springframework.org/schema/aop/spring-aop-3.0.xsd=org/springframewor
http\://www.springframework.org/schema/aop/spring-aop-3.1.xsd=org/springframework/aop/config/spring-aop-3.1.xsd
http\://www.springframework.org/schema/aop/spring-aop-3.2.xsd=org/springframework/aop/config/spring-aop-3.2.xsd
http\://www.springframework.org/schema/aop/spring-aop-4.0.xsd=org/springframework/aop/config/spring-aop-4.0.xsd
http\://www.springframework.org/schema/aop/spring-aop.xsd=org/springframework/aop/config/spring-aop-4.0.xsd
http\://www.springframework.org/schema/aop/spring-aop-4.1.xsd=org/springframework/aop/config/spring-aop-4.1.xsd
http\://www.springframework.org/schema/aop/spring-aop.xsd=org/springframework/aop/config/spring-aop-4.1.xsd
@@ -0,0 +1,410 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://www.springframework.org/schema/aop"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:tool="http://www.springframework.org/schema/tool"
targetNamespace="http://www.springframework.org/schema/aop"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans" schemaLocation="http://www.springframework.org/schema/beans/spring-beans-4.1.xsd"/>
<xsd:import namespace="http://www.springframework.org/schema/tool" schemaLocation="http://www.springframework.org/schema/tool/spring-tool-4.1.xsd"/>
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines the configuration elements for the Spring Framework's AOP support.
]]></xsd:documentation>
</xsd:annotation>
<xsd:element name="config">
<xsd:annotation>
<xsd:documentation><![CDATA[
A section (compartmentalization) of AOP-specific configuration (including
aspects, pointcuts, etc).
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="pointcut" type="pointcutType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
A named pointcut definition.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="advisor" type="advisorType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.aop.Advisor"><![CDATA[
A named advisor definition.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="aspect" type="aspectType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
A named aspect definition.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="proxy-target-class" type="xsd:boolean" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Are class-based (CGLIB) proxies to be created? By default, standard
Java interface-based proxies are created.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="expose-proxy" type="xsd:boolean" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Indicate that the proxy should be exposed by the AOP framework as a
ThreadLocal for retrieval via the AopContext class. Off by default,
i.e. no guarantees that AopContext access will work.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="aspectj-autoproxy">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"><![CDATA[
Enables the use of the @AspectJ style of Spring AOP.
See org.springframework.context.annotation.EnableAspectJAutoProxy Javadoc
for information on code-based alternatives to this XML element.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="include" type="includeType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
Indicates that only @AspectJ beans with names matched by the (regex)
pattern will be considered as defining aspects to use for Spring autoproxying.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="proxy-target-class" type="xsd:boolean" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Are class-based (CGLIB) proxies to be created? By default, standard
Java interface-based proxies are created.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="expose-proxy" type="xsd:boolean" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Indicate that the proxy should be exposed by the AOP framework as a
ThreadLocal for retrieval via the AopContext class. Off by default,
i.e. no guarantees that AopContext access will work.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="scoped-proxy">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation source="java:org.springframework.aop.scope.ScopedProxyFactoryBean"><![CDATA[
Marks a bean definition as being a scoped proxy.
A bean marked as such will be exposed via a proxy, with the 'real'
bean instance being retrieved from some other source (such as a
HttpSession) as and when required.
]]></xsd:documentation>
</xsd:annotation>
<xsd:attribute name="proxy-target-class" type="xsd:boolean" default="true">
<xsd:annotation>
<xsd:documentation><![CDATA[
Are class-based (CGLIB) proxies to be created? This is the default; in order to
switch to standard Java interface-based proxies, turn this flag to "false".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="aspectType">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="pointcut" type="pointcutType">
<xsd:annotation>
<xsd:documentation><![CDATA[
A named pointcut definition.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="declare-parents" type="declareParentsType">
<xsd:annotation>
<xsd:documentation><![CDATA[
Allows this aspect to introduce additional interfaces that the advised
object will transparently implement.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="before" type="basicAdviceType">
<xsd:annotation>
<xsd:documentation><![CDATA[
A before advice definition.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="after" type="basicAdviceType">
<xsd:annotation>
<xsd:documentation><![CDATA[
An after advice definition.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="after-returning" type="afterReturningAdviceType">
<xsd:annotation>
<xsd:documentation><![CDATA[
An after-returning advice definition.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="after-throwing" type="afterThrowingAdviceType">
<xsd:annotation>
<xsd:documentation><![CDATA[
An after-throwing advice definition.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="around" type="basicAdviceType">
<xsd:annotation>
<xsd:documentation><![CDATA[
An around advice definition.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:choice>
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The unique identifier for an aspect.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="ref" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the (backing) bean that encapsulates the aspect.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="order" type="xsd:token">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.core.Ordered"><![CDATA[
Controls the ordering of the execution of this aspect when multiple
advice executes at a specific joinpoint.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="includeType">
<xsd:attribute name="name" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:java.util.regex.Pattern"><![CDATA[
The regular expression defining which beans are to be included in the
list of @AspectJ beans; beans with names matched by the pattern will
be included.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="pointcutType">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="org.springframework.aop.Pointcut"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:attribute name="id" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The unique identifier for a pointcut.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="expression" use="required" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The pointcut expression.
For example : 'execution(* com.xyz.myapp.service.*.*(..))'
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="declareParentsType">
<xsd:attribute name="types-matching" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.aop.aspectj.TypePatternClassFilter"><![CDATA[
The AspectJ type expression that defines what types (classes) the
introduction is restricted to.
An example would be 'org.springframework.beans.ITestBean+'.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="implement-interface" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation source="java:java.lang.Class"><![CDATA[
The fully qualified name of the interface that will be introduced.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="default-impl" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:java.lang.Class"><![CDATA[
The fully qualified name of the class that will be instantiated to serve
as the default implementation of the introduced interface.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="delegate-ref" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the bean that will serve
as the default implementation of the introduced interface.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="basicAdviceType">
<xsd:attribute name="pointcut" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The associated pointcut expression.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="pointcut-ref" type="pointcutRefType">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of an associated pointcut definition.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.aop.Pointcut"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="method" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the method that defines the logic of the advice.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="arg-names" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The comma-delimited list of advice method argument (parameter) names
that will be matched from pointcut parameters.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="afterReturningAdviceType">
<xsd:complexContent>
<xsd:extension base="basicAdviceType">
<xsd:attribute name="returning" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the method parameter to which the return value must
be passed.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="afterThrowingAdviceType">
<xsd:complexContent>
<xsd:extension base="basicAdviceType">
<xsd:attribute name="throwing" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the method parameter to which the thrown exception must
be passed.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="advisorType">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="org.springframework.aop.Advisor"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="advice-ref" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to an advice bean.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.aopalliance.aop.Advice"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="pointcut" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A pointcut expression.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="pointcut-ref" type="pointcutRefType">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to a pointcut definition.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="order" type="xsd:token">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.core.Ordered"><![CDATA[
Controls the ordering of the execution of this advice when multiple
advice executes at a specific joinpoint.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:simpleType name="pointcutRefType">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.aop.Pointcut"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string"/>
</xsd:simpleType>
</xsd:schema>
@@ -38,6 +38,7 @@ import org.aspectj.lang.annotation.DeclarePrecedence;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.aop.Advisor;
@@ -402,8 +403,8 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
@Test
public void testIntroductionOnTargetExcludedByTypePattern() {
LinkedList target = new LinkedList();
List proxy = (List) createProxy(target,
LinkedList<Object> target = new LinkedList<Object>();
List<?> proxy = (List<?>) createProxy(target,
AopUtils.findAdvisorsThatCanApply(
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")),
List.class
@@ -431,7 +432,9 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
// TODO: Why does this test fail? It hasn't been run before, so it maybe never actually passed...
public void XtestIntroductionWithArgumentBinding() {
@Test
@Ignore
public void testIntroductionWithArgumentBinding() {
TestBean target = new TestBean();
List<Advisor> advisors = getFixture().getAdvisors(
@@ -1,85 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.aspectj;
import org.aspectj.lang.annotation.SuppressAjWarnings;
import org.springframework.beans.factory.wiring.BeanConfigurerSupport;
/**
* Abstract superaspect for AspectJ aspects that can perform Dependency
* Injection on objects, however they may be created. Define the beanCreation()
* pointcut in subaspects.
*
* <p>Subaspects may also need a metadata resolution strategy, in the
* {@code BeanWiringInfoResolver} interface. The default implementation
* looks for a bean with the same name as the FQN. This is the default name
* of a bean in a Spring container if the id value is not supplied explicitly.
*
* @author Rob Harrop
* @author Rod Johnson
* @author Adrian Colyer
* @author Ramnivas Laddad
* @since 2.0
* @deprecated as of Spring 2.5.2.
* Use AbstractDependencyInjectionAspect or its subaspects instead.
*/
public abstract aspect AbstractBeanConfigurerAspect extends BeanConfigurerSupport {
/**
* Configured bean before initialization.
*/
@SuppressAjWarnings("adviceDidNotMatch")
before(Object beanInstance) : beanInitialization(beanInstance) {
if (preConstructionConfiguration(beanInstance)) {
configureBean(beanInstance);
}
}
/**
* Configured bean after construction.
*/
@SuppressAjWarnings("adviceDidNotMatch")
after(Object beanInstance) returning : beanCreation(beanInstance) {
if (!preConstructionConfiguration(beanInstance)) {
configureBean(beanInstance);
}
}
/**
* The initialization of a new object.
*
* <p>WARNING: Although this pointcut is non-abstract for backwards
* compatibility reasons, it is meant to be overridden to select
* initialization of any configurable bean.
*/
protected pointcut beanInitialization(Object beanInstance);
/**
* The creation of a new object.
*/
protected abstract pointcut beanCreation(Object beanInstance);
/**
* Are dependencies to be injected prior to the construction of an object?
*/
protected boolean preConstructionConfiguration(Object beanInstance) {
return false; // matches the default in the @Configurable annotation
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,26 +27,18 @@ import org.aspectj.lang.annotation.control.CodeGenerationHint;
* @since 2.5.2
*/
public abstract aspect AbstractDependencyInjectionAspect {
/**
* Select construction join points for objects to inject dependencies
*/
public abstract pointcut beanConstruction(Object bean);
private pointcut preConstructionCondition() :
leastSpecificSuperTypeConstruction() && preConstructionConfiguration();
private pointcut postConstructionCondition() :
mostSpecificSubTypeConstruction() && !preConstructionConfiguration();
/**
* Select deserialization join points for objects to inject dependencies
* Select least specific super type that is marked for DI
* (so that injection occurs only once with pre-construction injection).
*/
public abstract pointcut beanDeserialization(Object bean);
/**
* Select join points in a configurable bean
*/
public abstract pointcut inConfigurableBean();
/**
* Select join points in beans to be configured prior to construction?
* By default, use post-construction injection matching the default in the Configurable annotation.
*/
public pointcut preConstructionConfiguration() : if(false);
public abstract pointcut leastSpecificSuperTypeConstruction();
/**
* Select the most-specific initialization join point
@@ -54,24 +46,29 @@ public abstract aspect AbstractDependencyInjectionAspect {
*/
@CodeGenerationHint(ifNameSuffix="6f1")
public pointcut mostSpecificSubTypeConstruction() :
if(thisJoinPoint.getSignature().getDeclaringType() == thisJoinPoint.getThis().getClass());
if (thisJoinPoint.getSignature().getDeclaringType() == thisJoinPoint.getThis().getClass());
/**
* Select least specific super type that is marked for DI (so that injection occurs only once with pre-construction inejection
* Select join points in beans to be configured prior to construction?
* By default, use post-construction injection matching the default in the Configurable annotation.
*/
public abstract pointcut leastSpecificSuperTypeConstruction();
public pointcut preConstructionConfiguration() : if (false);
/**
* Configure the bean
* Select construction join points for objects to inject dependencies.
*/
public abstract void configureBean(Object bean);
public abstract pointcut beanConstruction(Object bean);
/**
* Select deserialization join points for objects to inject dependencies.
*/
public abstract pointcut beanDeserialization(Object bean);
private pointcut preConstructionCondition() :
leastSpecificSuperTypeConstruction() && preConstructionConfiguration();
/**
* Select join points in a configurable bean.
*/
public abstract pointcut inConfigurableBean();
private pointcut postConstructionCondition() :
mostSpecificSubTypeConstruction() && !preConstructionConfiguration();
/**
* Pre-construction configuration.
@@ -100,4 +97,10 @@ public abstract aspect AbstractDependencyInjectionAspect {
configureBean(bean);
}
/**
* Configure the given bean.
*/
public abstract void configureBean(Object bean);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author 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,49 +20,48 @@ import java.io.ObjectStreamException;
import java.io.Serializable;
/**
* An aspect that injects dependency into any object whose type implements the {@link ConfigurableObject} interface.
* <p>
* This aspect supports injecting into domain objects when they are created for the first time as well as
* upon deserialization. Subaspects need to simply provide definition for the configureBean() method. This
* method may be implemented without relying on Spring container if so desired.
* </p>
* <p>
* There are two cases that needs to be handled:
* An aspect that injects dependency into any object whose type implements the
* {@link ConfigurableObject} interface.
*
* <p>This aspect supports injecting into domain objects when they are created
* for the first time as well as upon deserialization. Subaspects need to simply
* provide definition for the configureBean() method. This method may be
* implemented without relying on Spring container if so desired.
*
* <p>There are two cases that needs to be handled:
* <ol>
* <li>Normal object creation via the '{@code new}' operator: this is
* taken care of by advising {@code initialization()} join points.</li>
* <li>Object creation through deserialization: since no constructor is
* invoked during deserialization, the aspect needs to advise a method that a
* deserialization mechanism is going to invoke. Ideally, we should not
* require user classes to implement any specific method. This implies that
* we need to <i>introduce</i> the chosen method. We should also handle the cases
* where the chosen method is already implemented in classes (in which case,
* the user's implementation for that method should take precedence over the
* introduced implementation). There are a few choices for the chosen method:
* <ul>
* <li>readObject(ObjectOutputStream): Java requires that the method must be
* {@code private}</p>. Since aspects cannot introduce a private member,
* while preserving its name, this option is ruled out.</li>
* <li>readResolve(): Java doesn't pose any restriction on an access specifier.
* Problem solved! There is one (minor) limitation of this approach in
* that if a user class already has this method, that method must be
* {@code public}. However, this shouldn't be a big burden, since
* use cases that need classes to implement readResolve() (custom enums,
* for example) are unlikely to be marked as &#64;Configurable, and
* in any case asking to make that method {@code public} should not
* pose any undue burden.</li>
* </ul>
* The minor collaboration needed by user classes (i.e., that the
* implementation of {@code readResolve()}, if any, must be
* {@code public}) can be lifted as well if we were to use an
* experimental feature in AspectJ - the {@code hasmethod()} PCD.</li>
* <li>Normal object creation via the '{@code new}' operator: this is
* taken care of by advising {@code initialization()} join points.</li>
* <li>Object creation through deserialization: since no constructor is
* invoked during deserialization, the aspect needs to advise a method that a
* deserialization mechanism is going to invoke. Ideally, we should not
* require user classes to implement any specific method. This implies that
* we need to <i>introduce</i> the chosen method. We should also handle the cases
* where the chosen method is already implemented in classes (in which case,
* the user's implementation for that method should take precedence over the
* introduced implementation). There are a few choices for the chosen method:
* <ul>
* <li>readObject(ObjectOutputStream): Java requires that the method must be
* {@code private}</p>. Since aspects cannot introduce a private member,
* while preserving its name, this option is ruled out.</li>
* <li>readResolve(): Java doesn't pose any restriction on an access specifier.
* Problem solved! There is one (minor) limitation of this approach in
* that if a user class already has this method, that method must be
* {@code public}. However, this shouldn't be a big burden, since
* use cases that need classes to implement readResolve() (custom enums,
* for example) are unlikely to be marked as &#64;Configurable, and
* in any case asking to make that method {@code public} should not
* pose any undue burden.</li>
* </ul>
* The minor collaboration needed by user classes (i.e., that the implementation of
* {@code readResolve()}, if any, must be {@code public}) can be lifted as well if we
* were to use an experimental feature in AspectJ - the {@code hasmethod()} PCD.</li>
* </ol>
* <p>
* While having type implement the {@link ConfigurableObject} interface is certainly a valid choice, an alternative
* is to use a 'declare parents' statement another aspect (a subaspect of this aspect would be a logical choice)
* that declares the classes that need to be configured by supplying the {@link ConfigurableObject} interface.
* </p>
*
* <p>While having type implement the {@link ConfigurableObject} interface is certainly
* a valid choice, an alternative is to use a 'declare parents' statement another aspect
* (a subaspect of this aspect would be a logical choice) that declares the classes that
* need to be configured by supplying the {@link ConfigurableObject} interface.
*
* @author Ramnivas Laddad
* @since 2.5.2
@@ -72,35 +71,33 @@ public abstract aspect AbstractInterfaceDrivenDependencyInjectionAspect extends
* Select initialization join point as object construction
*/
public pointcut beanConstruction(Object bean) :
initialization(ConfigurableObject+.new(..)) && this(bean);
initialization(ConfigurableObject+.new(..)) && this(bean);
/**
* Select deserialization join point made available through ITDs for ConfigurableDeserializationSupport
*/
public pointcut beanDeserialization(Object bean) :
execution(Object ConfigurableDeserializationSupport+.readResolve()) &&
this(bean);
execution(Object ConfigurableDeserializationSupport+.readResolve()) && this(bean);
public pointcut leastSpecificSuperTypeConstruction() : initialization(ConfigurableObject.new(..));
// Implementation to support re-injecting dependencies once an object is deserialized
/**
* Declare any class implementing Serializable and ConfigurableObject as also implementing
* ConfigurableDeserializationSupport. This allows us to introduce the readResolve()
* ConfigurableDeserializationSupport. This allows us to introduce the {@code readResolve()}
* method and select it with the beanDeserialization() pointcut.
*
* <p>Here is an improved version that uses the hasmethod() pointcut and lifts
* even the minor requirement on user classes:
*
* <pre class="code">declare parents: ConfigurableObject+ Serializable+
* && !hasmethod(Object readResolve() throws ObjectStreamException)
* implements ConfigurableDeserializationSupport;
* <pre class="code">
* declare parents: ConfigurableObject+ Serializable+
* && !hasmethod(Object readResolve() throws ObjectStreamException)
* implements ConfigurableDeserializationSupport;
* </pre>
*/
declare parents:
ConfigurableObject+ && Serializable+ implements ConfigurableDeserializationSupport;
declare parents: ConfigurableObject+ && Serializable+ implements ConfigurableDeserializationSupport;
/**
* A marker interface to which the {@code readResolve()} is introduced.
@@ -111,7 +108,6 @@ public abstract aspect AbstractInterfaceDrivenDependencyInjectionAspect extends
/**
* Introduce the {@code readResolve()} method so that we can advise its
* execution to configure the object.
*
* <p>Note if a method with the same signature already exists in a
* {@code Serializable} class of ConfigurableObject type,
* that implementation will take precedence (a good thing, since we are
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@ package org.springframework.beans.factory.aspectj;
import java.io.Serializable;
import org.aspectj.lang.annotation.control.CodeGenerationHint;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
@@ -44,48 +44,47 @@ import org.springframework.beans.factory.wiring.BeanConfigurerSupport;
* @see org.springframework.beans.factory.annotation.Configurable
* @see org.springframework.beans.factory.annotation.AnnotationBeanWiringInfoResolver
*/
public aspect AnnotationBeanConfigurerAspect
extends AbstractInterfaceDrivenDependencyInjectionAspect
public aspect AnnotationBeanConfigurerAspect extends AbstractInterfaceDrivenDependencyInjectionAspect
implements BeanFactoryAware, InitializingBean, DisposableBean {
private BeanConfigurerSupport beanConfigurerSupport = new BeanConfigurerSupport();
public void setBeanFactory(BeanFactory beanFactory) {
this.beanConfigurerSupport.setBeanWiringInfoResolver(new AnnotationBeanWiringInfoResolver());
this.beanConfigurerSupport.setBeanFactory(beanFactory);
}
public void afterPropertiesSet() {
this.beanConfigurerSupport.afterPropertiesSet();
}
public void configureBean(Object bean) {
this.beanConfigurerSupport.configureBean(bean);
}
public void destroy() {
this.beanConfigurerSupport.destroy();
}
public pointcut inConfigurableBean() : @this(Configurable);
public pointcut preConstructionConfiguration() : preConstructionConfigurationSupport(*);
declare parents: @Configurable * implements ConfigurableObject;
public void configureBean(Object bean) {
beanConfigurerSupport.configureBean(bean);
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
beanConfigurerSupport.setBeanFactory(beanFactory);
beanConfigurerSupport.setBeanWiringInfoResolver(new AnnotationBeanWiringInfoResolver());
}
public void afterPropertiesSet() throws Exception {
beanConfigurerSupport.afterPropertiesSet();
}
public void destroy() throws Exception {
beanConfigurerSupport.destroy();
}
/*
* An intermediary to match preConstructionConfiguration signature (that doesn't expose the annotation object)
*/
@CodeGenerationHint(ifNameSuffix="bb0")
private pointcut preConstructionConfigurationSupport(Configurable c) : @this(c) && if(c.preConstruction());
private pointcut preConstructionConfigurationSupport(Configurable c) : @this(c) && if (c.preConstruction());
declare parents: @Configurable * implements ConfigurableObject;
/*
* This declaration shouldn't be needed,
* except for an AspectJ bug (https://bugs.eclipse.org/bugs/show_bug.cgi?id=214559)
*/
declare parents: @Configurable Serializable+
implements ConfigurableDeserializationSupport;
declare parents: @Configurable Serializable+ implements ConfigurableDeserializationSupport;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author 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,9 @@ import java.lang.reflect.Method;
import org.aspectj.lang.annotation.SuppressAjWarnings;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.cache.interceptor.CacheAspectSupport;
import org.springframework.cache.interceptor.CacheOperationInvoker;
import org.springframework.cache.interceptor.CacheOperationSource;
/**
@@ -35,9 +37,10 @@ import org.springframework.cache.interceptor.CacheOperationSource;
* relevant Spring cache definition will <i>not</i> be resolved.
*
* @author Costin Leau
* @author Stephane Nicoll
* @since 3.1
*/
public abstract aspect AbstractCacheAspect extends CacheAspectSupport {
public abstract aspect AbstractCacheAspect extends CacheAspectSupport implements DisposableBean {
protected AbstractCacheAspect() {
}
@@ -51,12 +54,17 @@ public abstract aspect AbstractCacheAspect extends CacheAspectSupport {
setCacheOperationSources(cos);
}
@Override
public void destroy() {
clearMetadataCache(); // An aspect is basically a singleton
}
@SuppressAjWarnings("adviceDidNotMatch")
Object around(final Object cachedObject) : cacheMethodExecution(cachedObject) {
MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
Method method = methodSignature.getMethod();
Invoker aspectJInvoker = new Invoker() {
CacheOperationInvoker aspectJInvoker = new CacheOperationInvoker() {
public Object invoke() {
return proceed(cachedObject);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@ package org.springframework.cache.aspectj;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.cache.annotation.AbstractCachingConfiguration;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.cache.config.CacheManagementConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
@@ -35,7 +35,7 @@ import org.springframework.context.annotation.Role;
@Configuration
public class AspectJCachingConfiguration extends AbstractCachingConfiguration {
@Bean(name=AnnotationConfigUtils.CACHE_ASPECT_BEAN_NAME)
@Bean(name = CacheManagementConfigUtils.CACHE_ASPECT_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public AnnotationCacheAspect cacheAspect() {
AnnotationCacheAspect cacheAspect = AnnotationCacheAspect.aspectOf();
@@ -47,4 +47,5 @@ public class AspectJCachingConfiguration extends AbstractCachingConfiguration {
}
return cacheAspect;
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2002-2014 the original author 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.cache.aspectj;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.cache.config.CacheManagementConfigUtils;
import org.springframework.cache.jcache.config.AbstractJCacheConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
/**
* {@code @Configuration} class that registers the Spring infrastructure beans necessary
* to enable AspectJ-based annotation-driven cache management for standard JSR-107
* annotations.
*
* @author Stephane Nicoll
* @since 4.1
* @see org.springframework.cache.annotation.EnableCaching
* @see org.springframework.cache.annotation.CachingConfigurationSelector
*/
@Configuration
public class AspectJJCacheConfiguration extends AbstractJCacheConfiguration {
@Bean(name = CacheManagementConfigUtils.JCACHE_ASPECT_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public JCacheCacheAspect cacheAspect() {
JCacheCacheAspect cacheAspect = JCacheCacheAspect.aspectOf();
cacheAspect.setCacheOperationSource(cacheOperationSource());
return cacheAspect;
}
}
@@ -0,0 +1,124 @@
/*
* Copyright 2002-2014 the original author 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.cache.aspectj;
import java.lang.reflect.Method;
import javax.cache.annotation.CachePut;
import javax.cache.annotation.CacheRemove;
import javax.cache.annotation.CacheRemoveAll;
import javax.cache.annotation.CacheResult;
import org.aspectj.lang.annotation.SuppressAjWarnings;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.cache.interceptor.CacheOperationInvoker;
import org.springframework.cache.jcache.interceptor.JCacheAspectSupport;
/**
* Concrete AspectJ cache aspect using JSR-107 standard annotations.
*
* <p>When using this aspect, you <i>must</i> annotate the implementation class (and/or
* methods within that class), <i>not</i> the interface (if any) that the class
* implements. AspectJ follows Java's rule that annotations on interfaces are <i>not</i>
* inherited.
*
* <p>Any method may be annotated (regardless of visibility). Annotating non-public
* methods directly is the only way to get caching demarcation for the execution of
* such operations.
*
* @author Stephane Nicoll
* @since 4.1
*/
public aspect JCacheCacheAspect extends JCacheAspectSupport {
@SuppressAjWarnings("adviceDidNotMatch")
Object around(final Object cachedObject) : cacheMethodExecution(cachedObject) {
MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
Method method = methodSignature.getMethod();
CacheOperationInvoker aspectJInvoker = new CacheOperationInvoker() {
public Object invoke() {
try {
return proceed(cachedObject);
}
catch (Throwable ex) {
throw new ThrowableWrapper(ex);
}
}
};
try {
return execute(aspectJInvoker, thisJoinPoint.getTarget(), method, thisJoinPoint.getArgs());
}
catch (CacheOperationInvoker.ThrowableWrapper th) {
AnyThrow.throwUnchecked(th.getOriginal());
return null; // never reached
}
}
/**
* Definition of pointcut: matched join points will have JSR-107
* cache management applied.
*/
protected pointcut cacheMethodExecution(Object cachedObject) :
(executionOfCacheResultMethod()
|| executionOfCachePutMethod()
|| executionOfCacheRemoveMethod()
|| executionOfCacheRemoveAllMethod())
&& this(cachedObject);
/**
* Matches the execution of any method with the @{@link CacheResult} annotation.
*/
private pointcut executionOfCacheResultMethod() :
execution(@CacheResult * *(..));
/**
* Matches the execution of any method with the @{@link CachePut} annotation.
*/
private pointcut executionOfCachePutMethod() :
execution(@CachePut * *(..));
/**
* Matches the execution of any method with the @{@link CacheRemove} annotation.
*/
private pointcut executionOfCacheRemoveMethod() :
execution(@CacheRemove * *(..));
/**
* Matches the execution of any method with the @{@link CacheRemoveAll} annotation.
*/
private pointcut executionOfCacheRemoveAllMethod() :
execution(@CacheRemoveAll * *(..));
private static class AnyThrow {
private static void throwUnchecked(Throwable e) {
AnyThrow.<RuntimeException>throwAny(e);
}
@SuppressWarnings("unchecked")
private static <E extends Throwable> void throwAny(Throwable e) throws E {
throw (E)e;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author 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,12 +17,12 @@
package org.springframework.scheduling.aspectj;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.scheduling.annotation.AbstractAsyncConfiguration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.config.TaskManagementConfigUtils;
/**
* {@code @Configuration} class that registers the Spring infrastructure beans necessary
@@ -37,7 +37,7 @@ import org.springframework.scheduling.annotation.EnableAsync;
@Configuration
public class AspectJAsyncConfiguration extends AbstractAsyncConfiguration {
@Bean(name=AnnotationConfigUtils.ASYNC_EXECUTION_ASPECT_BEAN_NAME)
@Bean(name = TaskManagementConfigUtils.ASYNC_EXECUTION_ASPECT_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public AnnotationAsyncExecutionAspect asyncAdvisor() {
AnnotationAsyncExecutionAspect asyncAspect = AnnotationAsyncExecutionAspect.aspectOf();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,7 +37,7 @@ import org.springframework.transaction.config.TransactionManagementConfigUtils;
@Configuration
public class AspectJTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
@Bean(name=TransactionManagementConfigUtils.TRANSACTION_ASPECT_BEAN_NAME)
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ASPECT_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public AnnotationTransactionAspect transactionAspect() {
AnnotationTransactionAspect txAspect = AnnotationTransactionAspect.aspectOf();
@@ -46,4 +46,5 @@ public class AspectJTransactionManagementConfiguration extends AbstractTransacti
}
return txAspect;
}
}
@@ -1,676 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.aspectj;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import junit.framework.TestCase;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.tests.sample.beans.TestBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.UnsatisfiedDependencyException;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.mail.MailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
/**
* @author Adrian Colyer
* @author Rod Johnson
* @author Ramnivas Laddad
* @author Juergen Hoeller
* @author Chris Beams
*/
public abstract class AbstractBeanConfigurerTests extends TestCase {
protected ConfigurableApplicationContext context;
@Override
protected void setUp() throws Exception {
this.context = createContext();
}
protected abstract ConfigurableApplicationContext createContext();
public void testConfigurableWithExplicitBeanName() {
ShouldBeConfiguredBySpring myObject = new ShouldBeConfiguredBySpring();
assertEquals("Rod", myObject.getName());
}
public void testWithoutAnnotation() {
ShouldNotBeConfiguredBySpring myObject = new ShouldNotBeConfiguredBySpring();
assertNull("Name should not have been set", myObject.getName());
}
public void testConfigurableWithImplicitBeanName() {
ShouldBeConfiguredBySpringUsingTypeNameAsBeanName myObject =
new ShouldBeConfiguredBySpringUsingTypeNameAsBeanName();
assertEquals("Rob", myObject.getName());
}
public void testConfigurableUsingAutowireByType() {
ShouldBeConfiguredBySpringUsingAutowireByType myObject =
new ShouldBeConfiguredBySpringUsingAutowireByType();
assertNotNull(myObject.getFriend());
assertEquals("Ramnivas", myObject.getFriend().getName());
}
public void testConfigurableUsingAutowireByName() {
ValidAutowireByName myObject = new ValidAutowireByName();
assertNotNull(myObject.getRamnivas());
assertEquals("Ramnivas", myObject.getRamnivas().getName());
}
public void testInvalidAutowireByName() {
try {
new InvalidAutowireByName();
fail("Autowire by name cannot work");
}
catch (UnsatisfiedDependencyException ex) {
// Ok
}
}
public void testNewAspectAppliesToArbitraryNonAnnotatedPojo() {
ArbitraryExistingPojo aep = new ArbitraryExistingPojo();
assertNotNull(aep.friend);
assertEquals("Ramnivas", aep.friend.getName());
}
public void testNewAspectThatWasNotAddedToSpringContainer() {
try{
new ClassThatWillNotActuallyBeWired();
}
catch (IllegalStateException ex) {
assertTrue(ex.getMessage().indexOf("BeanFactory") != -1);
}
}
public void testInjectionOnDeserialization() throws Exception {
ShouldBeConfiguredBySpring domainObject = new ShouldBeConfiguredBySpring();
domainObject.setName("Anonymous");
ShouldBeConfiguredBySpring deserializedDomainObject =
serializeAndDeserialize(domainObject);
assertEquals("Dependency injected on deserialization","Rod",deserializedDomainObject.getName());
}
public void testInjectionOnDeserializationForClassesThatContainsPublicReadResolve() throws Exception {
ShouldBeConfiguredBySpringContainsPublicReadResolve domainObject = new ShouldBeConfiguredBySpringContainsPublicReadResolve();
domainObject.setName("Anonymous");
ShouldBeConfiguredBySpringContainsPublicReadResolve deserializedDomainObject =
serializeAndDeserialize(domainObject);
assertEquals("Dependency injected on deserialization","Rod",deserializedDomainObject.getName());
assertEquals("User readResolve should take precedence", 1, deserializedDomainObject.readResolveInvocationCount);
}
// See ShouldBeConfiguredBySpringContainsPrivateReadResolve
// public void testInjectionOnDeserializationForClassesThatContainsPrivateReadResolve() throws Exception {
// ShouldBeConfiguredBySpringContainsPrivateReadResolve domainObject = new ShouldBeConfiguredBySpringContainsPrivateReadResolve();
// domainObject.setName("Anonymous");
// ShouldBeConfiguredBySpringContainsPrivateReadResolve deserializedDomainObject =
// serializeAndDeserialize(domainObject);
// assertEquals("Dependency injected on deserialization","Rod",deserializedDomainObject.getName());
// }
public void testNonInjectionOnDeserializationForSerializedButNotConfigured() throws Exception {
SerializableThatShouldNotBeConfiguredBySpring domainObject = new SerializableThatShouldNotBeConfiguredBySpring();
domainObject.setName("Anonymous");
SerializableThatShouldNotBeConfiguredBySpring deserializedDomainObject =
serializeAndDeserialize(domainObject);
assertEquals("Dependency injected on deserialization","Anonymous",deserializedDomainObject.getName());
}
public void testSubBeanConfiguredOnlyOnce() throws Exception {
SubBean subBean = new SubBean();
assertEquals("Property injected more than once", 1, subBean.setterCount);
}
public void testSubBeanConfiguredOnlyOnceForPreConstruction() throws Exception {
SubBeanPreConstruction subBean = new SubBeanPreConstruction();
assertEquals("Property injected more than once", 1, subBean.setterCount);
}
public void testSubSerializableBeanConfiguredOnlyOnce() throws Exception {
SubSerializableBean subBean = new SubSerializableBean();
assertEquals("Property injected more than once", 1, subBean.setterCount);
subBean.setterCount = 0;
SubSerializableBean deserializedSubBean = serializeAndDeserialize(subBean);
assertEquals("Property injected more than once", 1, deserializedSubBean.setterCount);
}
public void testPreConstructionConfiguredBean() {
PreConstructionConfiguredBean bean = new PreConstructionConfiguredBean();
assertTrue("Injection didn't occur before construction", bean.preConstructionConfigured);
}
public void testPreConstructionConfiguredBeanDeserializationReinjection() throws Exception {
PreConstructionConfiguredBean bean = new PreConstructionConfiguredBean();
PreConstructionConfiguredBean deserialized = serializeAndDeserialize(bean);
assertEquals("Injection didn't occur upon deserialization", "ramnivas", deserialized.getName());
}
public void testPostConstructionConfiguredBean() {
PostConstructionConfiguredBean bean = new PostConstructionConfiguredBean();
assertFalse("Injection occurred before construction", bean.preConstructionConfigured);
}
public void testPostConstructionConfiguredBeanDeserializationReinjection() throws Exception {
PostConstructionConfiguredBean bean = new PostConstructionConfiguredBean();
PostConstructionConfiguredBean deserialized = serializeAndDeserialize(bean);
assertEquals("Injection didn't occur upon deserialization", "ramnivas", deserialized.getName());
}
public void testInterfaceDrivenDependencyInjection() {
MailClientDependencyInjectionAspect.aspectOf().setMailSender(new JavaMailSenderImpl());
Order testOrder = new Order();
assertNotNull("Interface driven injection didn't occur for direct construction", testOrder.mailSender);
}
public void testGenericInterfaceDrivenDependencyInjection() {
PricingStrategy injectedPricingStrategy = new PricingStrategy();
PricingStrategyDependencyInjectionAspect.aspectOf().setPricingStrategy(injectedPricingStrategy);
LineItem testLineItem = new LineItem();
assertSame("Generic interface driven injection didn't occur for direct construction", injectedPricingStrategy, testLineItem.pricingStrategy);
}
public void testInterfaceDrivenDependencyInjectionMultipleInterfaces() {
MailClientDependencyInjectionAspect.aspectOf().setMailSender(new JavaMailSenderImpl());
PaymentProcessorDependencyInjectionAspect.aspectOf().setPaymentProcessor(new PaymentProcessor());
ShoppingCart testCart = new ShoppingCart();
assertNotNull("Interface driven injection didn't occur for direct construction", testCart.mailSender);
assertNotNull("Interface driven injection didn't occur for direct construction", testCart.paymentProcessor);
}
public void testInterfaceDrivenDependencyInjectionUponDeserialization() throws Exception {
MailClientDependencyInjectionAspect.aspectOf().setMailSender(new JavaMailSenderImpl());
Order testOrder = new Order();
Order deserializedOrder = serializeAndDeserialize(testOrder);
assertNotNull(deserializedOrder);
assertNotNull("Interface driven injection didn't occur for deserialization", testOrder.mailSender);
}
public void testFieldAutoWiredAnnotationInjection() {
FieldAutoWiredServiceBean bean = new FieldAutoWiredServiceBean();
assertNotNull(bean.testService);
}
public void testMethodAutoWiredAnnotationInjection() {
MethodAutoWiredServiceBean bean = new MethodAutoWiredServiceBean();
assertNotNull(bean.testService);
}
public void testMultiArgumentMethodAutoWiredAnnotationInjection() {
MultiArgumentMethodAutoWiredServiceBean bean = new MultiArgumentMethodAutoWiredServiceBean();
assertNotNull(bean.testService);
assertNotNull(bean.paymentService);
}
public void testGenericParameterConfigurableBean() {
GenericParameterConfigurableBean bean = new GenericParameterConfigurableBean();
assertNotNull(bean.testService);
}
@SuppressWarnings("unchecked")
private <T> T serializeAndDeserialize(T serializable) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(serializable);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return (T)ois.readObject();
}
@Configurable("beanOne")
@SuppressWarnings("serial")
protected static class ShouldBeConfiguredBySpring implements Serializable {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
@Configurable("beanOne")
@SuppressWarnings("serial")
private static class ShouldBeConfiguredBySpringContainsPublicReadResolve implements Serializable {
private String name;
private int readResolveInvocationCount = 0;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public Object readResolve() throws ObjectStreamException {
readResolveInvocationCount++;
return this;
}
}
// Won't work until we use hasmethod() experimental pointcut in AspectJ.
// @Configurable("beanOne")
// private static class ShouldBeConfiguredBySpringContainsPrivateReadResolve implements Serializable {
//
// private String name;
//
// public void setName(String name) {
// this.name = name;
// }
//
// public String getName() {
// return this.name;
// }
//
// private Object readResolve() throws ObjectStreamException {
// return this;
// }
// }
@SuppressWarnings("unused")
private static class ShouldNotBeConfiguredBySpring {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
@SuppressWarnings("serial")
private static class SerializableThatShouldNotBeConfiguredBySpring implements Serializable {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
@Configurable
@SuppressWarnings("unused")
private static class ShouldBeConfiguredBySpringUsingTypeNameAsBeanName {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
@Configurable(autowire=Autowire.BY_TYPE)
@SuppressWarnings("unused")
private static class ShouldBeConfiguredBySpringUsingAutowireByType {
private TestBean friend = null;
public TestBean getFriend() {
return friend;
}
public void setFriend(TestBean friend) {
this.friend = friend;
}
}
@Configurable(autowire=Autowire.BY_NAME)
@SuppressWarnings("unused")
private static class ValidAutowireByName {
private TestBean friend = null;
public TestBean getRamnivas() {
return friend;
}
public void setRamnivas(TestBean friend) {
this.friend = friend;
}
}
@Configurable(autowire=Autowire.BY_NAME, dependencyCheck=true)
@SuppressWarnings("unused")
private static class InvalidAutowireByName {
private TestBean friend;
public TestBean getFriend() {
return friend;
}
public void setFriend(TestBean friend) {
this.friend = friend;
}
}
@SuppressWarnings("unused")
private static class ArbitraryExistingPojo {
private TestBean friend;
public void setFriend(TestBean f) {
this.friend = f;
}
}
public static class CircularFactoryBean implements FactoryBean{
public CircularFactoryBean() {
ValidAutowireByName autowired = new ValidAutowireByName();
assertNull(autowired.getRamnivas());
}
@Override
public Object getObject() throws Exception {
return new TestBean();
}
@Override
public Class getObjectType() {
return TestBean.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
@Configurable
@SuppressWarnings("unused")
private static class BaseBean {
public int setterCount;
private String name;
public void setName(String name) {
this.name = name;
setterCount++;
}
}
private static class SubBean extends BaseBean {
}
@Configurable(preConstruction=true)
private static class SubBeanPreConstruction extends BaseBean {
}
@Configurable
@SuppressWarnings({"serial", "unused"})
private static class BaseSerializableBean implements Serializable {
public int setterCount;
private String name;
public void setName(String name) {
this.name = name;
setterCount++;
}
}
@SuppressWarnings("serial")
private static class SubSerializableBean extends BaseSerializableBean {
}
@Aspect
@SuppressWarnings("unused")
private static class WireArbitraryExistingPojo extends AbstractBeanConfigurerAspect {
@Pointcut("initialization(ArbitraryExistingPojo.new(..)) && this(beanInstance)")
protected void beanCreation(Object beanInstance){
}
}
@Aspect
@SuppressWarnings("unused")
private static class AspectThatWillNotBeUsed extends AbstractBeanConfigurerAspect {
@Pointcut("initialization(ClassThatWillNotActuallyBeWired.new(..)) && this(beanInstance)")
protected void beanCreation(Object beanInstance){
}
}
private static aspect MailClientDependencyInjectionAspect extends AbstractInterfaceDrivenDependencyInjectionAspect {
private MailSender mailSender;
public pointcut inConfigurableBean() : within(MailSenderClient+);
public void configureBean(Object bean) {
((MailSenderClient)bean).setMailSender(this.mailSender);
}
declare parents: MailSenderClient implements ConfigurableObject;
public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}
}
private static aspect PaymentProcessorDependencyInjectionAspect extends AbstractInterfaceDrivenDependencyInjectionAspect {
private PaymentProcessor paymentProcessor;
public pointcut inConfigurableBean() : within(PaymentProcessorClient+);
public void configureBean(Object bean) {
((PaymentProcessorClient)bean).setPaymentProcessor(this.paymentProcessor);
}
declare parents: PaymentProcessorClient implements ConfigurableObject;
public void setPaymentProcessor(PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
}
private static aspect PricingStrategyDependencyInjectionAspect extends GenericInterfaceDrivenDependencyInjectionAspect<PricingStrategyClient> {
private PricingStrategy pricingStrategy;
public void configure(PricingStrategyClient bean) {
bean.setPricingStrategy(pricingStrategy);
}
public void setPricingStrategy(PricingStrategy pricingStrategy) {
this.pricingStrategy = pricingStrategy;
}
}
public static interface MailSenderClient {
public void setMailSender(MailSender mailSender);
}
public static interface PaymentProcessorClient {
public void setPaymentProcessor(PaymentProcessor paymentProcessor);
}
public static class PaymentProcessor {
}
public static interface PricingStrategyClient {
public void setPricingStrategy(PricingStrategy pricingStrategy);
}
public static class PricingStrategy {
}
public static class LineItem implements PricingStrategyClient {
private PricingStrategy pricingStrategy;
@Override
public void setPricingStrategy(PricingStrategy pricingStrategy) {
this.pricingStrategy = pricingStrategy;
}
}
@SuppressWarnings("serial")
public static class Order implements MailSenderClient, Serializable {
private transient MailSender mailSender;
@Override
public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}
}
public static class ShoppingCart implements MailSenderClient, PaymentProcessorClient {
private transient MailSender mailSender;
private transient PaymentProcessor paymentProcessor;
@Override
public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}
@Override
public void setPaymentProcessor(PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
}
private static class ClassThatWillNotActuallyBeWired {
}
@Configurable
@SuppressWarnings("serial")
private static class PreOrPostConstructionConfiguredBean implements Serializable {
private transient String name;
protected transient boolean preConstructionConfigured;
transient int count;
public PreOrPostConstructionConfiguredBean() {
preConstructionConfigured = (this.name != null);
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
@Configurable(preConstruction=true)
@SuppressWarnings("serial")
public static class PreConstructionConfiguredBean extends PreOrPostConstructionConfiguredBean {
}
@Configurable(preConstruction=false)
@SuppressWarnings("serial")
private static class PostConstructionConfiguredBean extends PreOrPostConstructionConfiguredBean {
}
@Configurable
public static class FieldAutoWiredServiceBean {
@Autowired transient private TestService testService;
}
@Configurable
public static class MethodAutoWiredServiceBean {
transient private TestService testService;
@Autowired
public void setTestService(TestService testService) {
this.testService = testService;
}
}
@Configurable
public static class MultiArgumentMethodAutoWiredServiceBean {
transient private TestService testService;
transient private PaymentService paymentService;
@Autowired
public void setDependencies(TestService testService, PaymentService paymentService) {
this.testService = testService;
this.paymentService = paymentService;
}
}
@Configurable
public static class GenericParameterConfigurableBean {
private TestService testService;
public void setTestService(TestService testService) {
this.testService = testService;
}
}
public static class TestService {
}
public static class PaymentService {
}
}
@@ -0,0 +1,37 @@
/*
* Copyright 2002-2014 the original author 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.aspectj;
import java.io.Serializable;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable("configuredBean")
@SuppressWarnings("serial")
public class ShouldBeConfiguredBySpring implements Serializable {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author 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,23 +16,22 @@
package org.springframework.beans.factory.aspectj;
import org.springframework.context.ConfigurableApplicationContext;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Chris Beams
*/
public class XmlBeanConfigurerTests extends AbstractBeanConfigurerTests {
public class XmlBeanConfigurerTests {
@Override
protected ConfigurableApplicationContext createContext() {
return new ClassPathXmlApplicationContext("org/springframework/beans/factory/aspectj/beanConfigurerTests.xml");
}
public void testInjectionAfterRefresh() {
context.refresh();
@Test
public void testInjection() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"org/springframework/beans/factory/aspectj/beanConfigurerTests.xml");
ShouldBeConfiguredBySpring myObject = new ShouldBeConfiguredBySpring();
assertEquals("Rod", myObject.getName());
Assert.assertEquals("Rod", myObject.getName());
}
}
@@ -1,64 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean class="org.springframework.beans.factory.aspectj.AbstractBeanConfigurerTests$WireArbitraryExistingPojo"
factory-method="aspectOf"/>
<bean id="beanOne" class="org.springframework.beans.factory.aspectj.AbstractBeanConfigurerTests$ShouldBeConfiguredBySpring"
lazy-init="true">
<bean id="configuredBean" class="org.springframework.beans.factory.aspectj.ShouldBeConfiguredBySpring" lazy-init="true">
<property name="name" value="Rod"/>
</bean>
<bean class="org.springframework.beans.factory.aspectj.AbstractBeanConfigurerTests$ShouldBeConfiguredBySpringUsingTypeNameAsBeanName"
lazy-init="true">
<property name="name" value="Rob"/>
</bean>
<bean class="org.springframework.beans.factory.aspectj.AbstractBeanConfigurerTests$SubBean"
lazy-init="true">
<property name="name" value="Ramnivas"/>
</bean>
<bean class="org.springframework.beans.factory.aspectj.AbstractBeanConfigurerTests$SubBeanPreConstruction"
lazy-init="true">
<property name="name" value="Ramnivas"/>
</bean>
<bean class="org.springframework.beans.factory.aspectj.AbstractBeanConfigurerTests$SubSerializableBean"
lazy-init="true">
<property name="name" value="Ramnivas"/>
</bean>
<bean id="circular" class="org.springframework.beans.factory.aspectj.AbstractBeanConfigurerTests$CircularFactoryBean"
autowire-candidate="false"/>
<!-- Used as a target for autowiring by type -->
<bean id="ramnivas" class="org.springframework.tests.sample.beans.TestBean" depends-on="circular">
<property name="name" value="Ramnivas"/>
<property name="spouse" ref="circular"/>
</bean>
<bean class="org.springframework.beans.factory.aspectj.AbstractBeanConfigurerTests$ArbitraryExistingPojo">
<property name="friend" ref="ramnivas"/>
</bean>
<bean class="org.springframework.beans.factory.aspectj.AbstractBeanConfigurerTests$PreConstructionConfiguredBean">
<property name="name" value="ramnivas"/>
</bean>
<bean class="org.springframework.beans.factory.aspectj.AbstractBeanConfigurerTests$PostConstructionConfiguredBean">
<property name="name" value="ramnivas"/>
</bean>
<bean class="org.springframework.beans.factory.aspectj.AbstractBeanConfigurerTests$GenericParameterConfigurableBean" scope="prototype">
<property name="testService" ref="testService"/>
</bean>
<bean id="testService" class="org.springframework.beans.factory.aspectj.AbstractBeanConfigurerTests$TestService"/>
<bean id="paymentService" class="org.springframework.beans.factory.aspectj.AbstractBeanConfigurerTests$PaymentService"/>
</beans>
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,7 +25,7 @@ import org.junit.Test;
import org.springframework.cache.Cache;
import org.springframework.cache.config.AbstractAnnotationTests;
import org.springframework.cache.config.CacheableService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
/**
@@ -35,7 +35,7 @@ public class AspectJAnnotationTests extends AbstractAnnotationTests {
@Override
protected ApplicationContext getApplicationContext() {
protected ConfigurableApplicationContext getApplicationContext() {
return new GenericXmlApplicationContext("/org/springframework/cache/config/annotation-cache-aspectj.xml");
}
@@ -71,4 +71,5 @@ public class AspectJAnnotationTests extends AbstractAnnotationTests {
assertSame(r3, primary.get(o1).get());
assertSame(r4, secondary.get(o1).get());
}
}
@@ -0,0 +1,71 @@
/*
* Copyright 2002-2014 the original author 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.cache.aspectj;
import java.util.Arrays;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.config.AnnotatedJCacheableService;
import org.springframework.cache.jcache.config.AbstractJCacheAnnotationTests;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Stephane Nicoll
*/
public class JCacheAspectJJavaConfigTests extends AbstractJCacheAnnotationTests {
@Override
protected ApplicationContext getApplicationContext() {
return new AnnotationConfigApplicationContext(EnableCachingConfig.class);
}
@Configuration
@EnableCaching(mode = AdviceMode.ASPECTJ)
public static class EnableCachingConfig {
@Bean
public CacheManager cacheManager() {
SimpleCacheManager cm = new SimpleCacheManager();
cm.setCaches(Arrays.asList(
defaultCache(),
new ConcurrentMapCache("primary"),
new ConcurrentMapCache("secondary"),
new ConcurrentMapCache("exception")));
return cm;
}
@Bean
public AnnotatedJCacheableService cacheableService() {
return new AnnotatedJCacheableService(defaultCache());
}
@Bean
public Cache defaultCache() {
return new ConcurrentMapCache("default");
}
}
}
@@ -0,0 +1,34 @@
/*
* Copyright 2002-2014 the original author 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.cache.aspectj;
import org.springframework.cache.jcache.config.AbstractJCacheAnnotationTests;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
/**
* @author Stephane Nicoll
*/
public class JCacheAspectJNamespaceConfigTests extends AbstractJCacheAnnotationTests {
@Override
protected ApplicationContext getApplicationContext() {
return new GenericXmlApplicationContext(
"/org/springframework/cache/config/annotation-jcache-aspectj.xml");
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@ import org.springframework.cache.annotation.Caching;
* @author Costin Leau
* @author Phillip Webb
*/
@Cacheable("default")
@Cacheable("testCache")
public class AnnotatedClassCacheableService implements CacheableService<Object> {
private final AtomicLong counter = new AtomicLong();
@@ -49,70 +49,94 @@ public class AnnotatedClassCacheableService implements CacheableService<Object>
}
@Override
@CacheEvict("default")
@CacheEvict("testCache")
public void invalidate(Object arg1) {
}
@Override
@CacheEvict("default")
@CacheEvict("testCache")
public void evictWithException(Object arg1) {
throw new RuntimeException("exception thrown - evict should NOT occur");
}
@Override
@CacheEvict(value = "default", allEntries = true)
@CacheEvict(value = "testCache", allEntries = true)
public void evictAll(Object arg1) {
}
@Override
@CacheEvict(value = "default", beforeInvocation = true)
@CacheEvict(value = "testCache", beforeInvocation = true)
public void evictEarly(Object arg1) {
throw new RuntimeException("exception thrown - evict should still occur");
}
@Override
@CacheEvict(value = "default", key = "#p0")
@CacheEvict(value = "testCache", key = "#p0")
public void evict(Object arg1, Object arg2) {
}
@Override
@CacheEvict(value = "default", key = "#p0", beforeInvocation = true)
@CacheEvict(value = "testCache", key = "#p0", beforeInvocation = true)
public void invalidateEarly(Object arg1, Object arg2) {
throw new RuntimeException("exception thrown - evict should still occur");
}
@Override
@Cacheable(value = "default", key = "#p0")
@Cacheable(value = "testCache", key = "#p0")
public Object key(Object arg1, Object arg2) {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "default")
@Cacheable(value = "testCache")
public Object varArgsKey(Object... args) {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "default", key = "#root.methodName + #root.caches[0].name")
@Cacheable(value = "testCache", key = "#root.methodName + #root.caches[0].name")
public Object name(Object arg1) {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "default", key = "#root.methodName + #root.method.name + #root.targetClass + #root.target")
@Cacheable(value = "testCache", key = "#root.methodName + #root.method.name + #root.targetClass + #root.target")
public Object rootVars(Object arg1) {
return counter.getAndIncrement();
}
@Override
@CachePut("default")
@Cacheable(value = "testCache", keyGenerator = "customKyeGenerator")
public Object customKeyGenerator(Object arg1) {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "testCache", keyGenerator = "unknownBeanName")
public Object unknownCustomKeyGenerator(Object arg1) {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "testCache", cacheManager = "customCacheManager")
public Object customCacheManager(Object arg1) {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "testCache", cacheManager = "unknownBeanName")
public Object unknownCustomCacheManager(Object arg1) {
return counter.getAndIncrement();
}
@Override
@CachePut("testCache")
public Object update(Object arg1) {
return counter.getAndIncrement();
}
@Override
@CachePut(value = "default", condition = "#arg.equals(3)")
@CachePut(value = "testCache", condition = "#arg.equals(3)")
public Object conditionalUpdate(Object arg) {
return arg;
}
@@ -176,4 +200,5 @@ public class AnnotatedClassCacheableService implements CacheableService<Object>
arg1.setId(Long.MIN_VALUE);
return arg1;
}
}
@@ -0,0 +1,214 @@
/*
* Copyright 2002-2014 the original author 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.cache.config;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import javax.cache.annotation.CacheDefaults;
import javax.cache.annotation.CacheKey;
import javax.cache.annotation.CachePut;
import javax.cache.annotation.CacheRemove;
import javax.cache.annotation.CacheRemoveAll;
import javax.cache.annotation.CacheResult;
import javax.cache.annotation.CacheValue;
import org.springframework.cache.Cache;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.cache.jcache.config.JCacheableService;
import org.springframework.cache.jcache.support.TestableCacheKeyGenerator;
import org.springframework.cache.jcache.support.TestableCacheResolverFactory;
/**
* Repository sample with a @CacheDefaults annotation
*
* <p>Note: copy/pasted from its original compilation because it needs to be
* processed by the AspectJ compiler to wave the required aspects.
*
* @author Stephane Nicoll
*/
@CacheDefaults(cacheName = "default")
public class AnnotatedJCacheableService implements JCacheableService<Long> {
private final AtomicLong counter = new AtomicLong();
private final AtomicLong exceptionCounter = new AtomicLong();
private final Cache defaultCache;
public AnnotatedJCacheableService(Cache defaultCache) {
this.defaultCache = defaultCache;
}
@Override
@CacheResult
public Long cache(String id) {
return counter.getAndIncrement();
}
@Override
@CacheResult(exceptionCacheName = "exception", nonCachedExceptions = NullPointerException.class)
public Long cacheWithException(@CacheKey String id, boolean matchFilter) {
throwException(matchFilter);
return 0L; // Never reached
}
@Override
@CacheResult(exceptionCacheName = "exception", nonCachedExceptions = NullPointerException.class)
public Long cacheWithCheckedException(@CacheKey String id, boolean matchFilter) throws IOException {
throwCheckedException(matchFilter);
return 0L; // Never reached
}
@Override
@CacheResult(skipGet = true)
public Long cacheAlwaysInvoke(String id) {
return counter.getAndIncrement();
}
@Override
@CacheResult
public Long cacheWithPartialKey(@CacheKey String id, boolean notUsed) {
return counter.getAndIncrement();
}
@Override
@CacheResult(cacheResolverFactory = TestableCacheResolverFactory.class)
public Long cacheWithCustomCacheResolver(String id) {
return counter.getAndIncrement();
}
@Override
@CacheResult(cacheKeyGenerator = TestableCacheKeyGenerator.class)
public Long cacheWithCustomKeyGenerator(String id, String anotherId) {
return counter.getAndIncrement();
}
@Override
@CachePut
public void put(String id, @CacheValue Object value) {
}
@Override
@CachePut(cacheFor = UnsupportedOperationException.class)
public void putWithException(@CacheKey String id, @CacheValue Object value, boolean matchFilter) {
throwException(matchFilter);
}
@Override
@CachePut(afterInvocation = false)
public void earlyPut(String id, @CacheValue Object value) {
Object key = SimpleKeyGenerator.generateKey(id);
Cache.ValueWrapper valueWrapper = defaultCache.get(key);
if (valueWrapper == null) {
throw new AssertionError("Excepted value to be put in cache with key " + key);
}
Object actual = valueWrapper.get();
if (value != actual) { // instance check on purpose
throw new AssertionError("Wrong value set in cache with key " + key + ". " +
"Expected=" + value + ", but got=" + actual);
}
}
@Override
@CachePut(afterInvocation = false)
public void earlyPutWithException(@CacheKey String id, @CacheValue Object value, boolean matchFilter) {
throwException(matchFilter);
}
@Override
@CacheRemove
public void remove(String id) {
}
@Override
@CacheRemove(noEvictFor = NullPointerException.class)
public void removeWithException(@CacheKey String id, boolean matchFilter) {
throwException(matchFilter);
}
@Override
@CacheRemove(afterInvocation = false)
public void earlyRemove(String id) {
Object key = SimpleKeyGenerator.generateKey(id);
Cache.ValueWrapper valueWrapper = defaultCache.get(key);
if (valueWrapper != null) {
throw new AssertionError("Value with key " + key + " expected to be already remove from cache");
}
}
@Override
@CacheRemove(afterInvocation = false, evictFor = UnsupportedOperationException.class)
public void earlyRemoveWithException(@CacheKey String id, boolean matchFilter) {
throwException(matchFilter);
}
@Override
@CacheRemoveAll
public void removeAll() {
}
@Override
@CacheRemoveAll(noEvictFor = NullPointerException.class)
public void removeAllWithException(boolean matchFilter) {
throwException(matchFilter);
}
@Override
@CacheRemoveAll(afterInvocation = false)
public void earlyRemoveAll() {
ConcurrentHashMap<?, ?> nativeCache = (ConcurrentHashMap<?, ?>) defaultCache.getNativeCache();
if (!nativeCache.isEmpty()) {
throw new AssertionError("Cache was expected to be empty");
}
}
@Override
@CacheRemoveAll(afterInvocation = false, evictFor = UnsupportedOperationException.class)
public void earlyRemoveAllWithException(boolean matchFilter) {
throwException(matchFilter);
}
@Deprecated
public void noAnnotation() {
}
@Override
public long exceptionInvocations() {
return exceptionCounter.get();
}
private void throwException(boolean matchFilter) {
long count = exceptionCounter.getAndIncrement();
if (matchFilter) {
throw new UnsupportedOperationException("Expected exception (" + count + ")");
}
else {
throw new NullPointerException("Expected exception (" + count + ")");
}
}
private void throwCheckedException(boolean matchFilter) throws IOException {
long count = exceptionCounter.getAndIncrement();
if (matchFilter) {
throw new IOException("Expected exception (" + count + ")");
}
else {
throw new NullPointerException("Expected exception (" + count + ")");
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author 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,6 +58,14 @@ public interface CacheableService<T> {
T rootVars(Object arg1);
T customKeyGenerator(Object arg1);
T unknownCustomKeyGenerator(Object arg1);
T customCacheManager(Object arg1);
T unknownCustomCacheManager(Object arg1);
T throwChecked(Object arg1) throws Exception;
T throwUnchecked(Object arg1);
@@ -74,4 +82,5 @@ public interface CacheableService<T> {
T multiUpdate(Object arg1);
TestEntity putRefersToResult(TestEntity arg1);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author 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,94 +35,118 @@ public class DefaultCacheableService implements CacheableService<Long> {
private final AtomicLong nullInvocations = new AtomicLong();
@Override
@Cacheable("default")
@Cacheable("testCache")
public Long cache(Object arg1) {
return counter.getAndIncrement();
}
@Override
@CacheEvict("default")
@CacheEvict("testCache")
public void invalidate(Object arg1) {
}
@Override
@CacheEvict("default")
@CacheEvict("testCache")
public void evictWithException(Object arg1) {
throw new RuntimeException("exception thrown - evict should NOT occur");
}
@Override
@CacheEvict(value = "default", allEntries = true)
@CacheEvict(value = "testCache", allEntries = true)
public void evictAll(Object arg1) {
}
@Override
@CacheEvict(value = "default", beforeInvocation = true)
@CacheEvict(value = "testCache", beforeInvocation = true)
public void evictEarly(Object arg1) {
throw new RuntimeException("exception thrown - evict should still occur");
}
@Override
@CacheEvict(value = "default", key = "#p0")
@CacheEvict(value = "testCache", key = "#p0")
public void evict(Object arg1, Object arg2) {
}
@Override
@CacheEvict(value = "default", key = "#p0", beforeInvocation = true)
@CacheEvict(value = "testCache", key = "#p0", beforeInvocation = true)
public void invalidateEarly(Object arg1, Object arg2) {
throw new RuntimeException("exception thrown - evict should still occur");
}
@Override
@Cacheable(value = "default", condition = "#classField == 3")
@Cacheable(value = "testCache", condition = "#classField == 3")
public Long conditional(int classField) {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "default", unless = "#result > 10")
@Cacheable(value = "testCache", unless = "#result > 10")
public Long unless(int arg) {
return (long) arg;
}
@Override
@Cacheable(value = "default", key = "#p0")
@Cacheable(value = "testCache", key = "#p0")
public Long key(Object arg1, Object arg2) {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "default")
@Cacheable(value = "testCache")
public Long varArgsKey(Object... args) {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "default", key = "#root.methodName")
@Cacheable(value = "testCache", key = "#root.methodName")
public Long name(Object arg1) {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "default", key = "#root.methodName + #root.method.name + #root.targetClass + #root.target")
@Cacheable(value = "testCache", key = "#root.methodName + #root.method.name + #root.targetClass + #root.target")
public Long rootVars(Object arg1) {
return counter.getAndIncrement();
}
@Override
@CachePut("default")
@Cacheable(value = "testCache", keyGenerator = "customKeyGenerator")
public Long customKeyGenerator(Object arg1) {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "testCache", keyGenerator = "unknownBeanName")
public Long unknownCustomKeyGenerator(Object arg1) {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "testCache", cacheManager = "customCacheManager")
public Long customCacheManager(Object arg1) {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "testCache", cacheManager = "unknownBeanName")
public Long unknownCustomCacheManager(Object arg1) {
return counter.getAndIncrement();
}
@Override
@CachePut("testCache")
public Long update(Object arg1) {
return counter.getAndIncrement();
}
@Override
@CachePut(value = "default", condition = "#arg.equals(3)")
@CachePut(value = "testCache", condition = "#arg.equals(3)")
public Long conditionalUpdate(Object arg) {
return Long.valueOf(arg.toString());
}
@Override
@Cacheable("default")
@Cacheable("testCache")
public Long nullValue(Object arg1) {
nullInvocations.incrementAndGet();
return null;
@@ -134,13 +158,13 @@ public class DefaultCacheableService implements CacheableService<Long> {
}
@Override
@Cacheable("default")
@Cacheable("testCache")
public Long throwChecked(Object arg1) throws Exception {
throw new Exception(arg1.toString());
}
@Override
@Cacheable("default")
@Cacheable("testCache")
public Long throwUnchecked(Object arg1) {
throw new UnsupportedOperationException(arg1.toString());
}
@@ -184,4 +208,5 @@ public class DefaultCacheableService implements CacheableService<Long> {
arg1.setId(Long.MIN_VALUE);
return arg1;
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2002-2014 the original author 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.cache.config;
import org.springframework.cache.interceptor.KeyGenerator;
import java.lang.reflect.Method;
/**
* A custom {@link KeyGenerator} that exposes the algorithm used to compute the key
* for convenience in tests scenario.
*
* @author Stephane Nicoll
*/
final class SomeCustomKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object target, Method method, Object... params) {
return generateKey(method.getName(), params);
}
/**
* @see #generate(Object, java.lang.reflect.Method, Object...)
*/
static Object generateKey(String methodName, Object... params) {
final StringBuilder sb = new StringBuilder(methodName);
for (Object param : params) {
sb.append(param);
}
return sb.toString();
}
}
@@ -21,7 +21,7 @@ import org.springframework.util.ObjectUtils;
/**
* Simple test entity for use with caching tests.
*
* @author Michael Plšd
* @author Michael Plod
*/
public class TestEntity {
@@ -53,4 +53,5 @@ public class TestEntity {
}
return false;
}
}
@@ -1,13 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<!--
@@ -31,7 +28,7 @@
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
<property name="name" value="default"/>
<property name="name" value="testCache"/>
</bean>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
<property name="name" value="primary"/>
@@ -43,6 +40,18 @@
</property>
</bean>
<bean id="customKeyGenerator" class="org.springframework.cache.config.SomeCustomKeyGenerator" />
<bean id="customCacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
<property name="name" value="testCache"/>
</bean>
</set>
</property>
</bean>
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/>
<bean id="service" class="org.springframework.cache.config.DefaultCacheableService"/>
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven mode="aspectj"/>
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<ref bean="defaultCache"/>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
<property name="name" value="primary"/>
</bean>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
<property name="name" value="secondary"/>
</bean>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
<property name="name" value="exception"/>
</bean>
</set>
</property>
</bean>
<bean id="defaultCache"
class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
<property name="name" value="default"/>
</bean>
<bean id="cacheableService" class="org.springframework.cache.config.AnnotatedJCacheableService">
<constructor-arg ref="defaultCache"/>
</bean>
</beans>
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author 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,31 +16,36 @@
package org.springframework.context.annotation.aspectj;
import org.springframework.beans.factory.aspectj.AbstractBeanConfigurerTests;
import org.springframework.context.ConfigurableApplicationContext;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.aspectj.ShouldBeConfiguredBySpring;
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 org.springframework.beans.factory.aspectj.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
*/
public class AnnotationBeanConfigurerTests extends AbstractBeanConfigurerTests {
public class AnnotationBeanConfigurerTests {
@Override
protected ConfigurableApplicationContext createContext() {
return new AnnotationConfigApplicationContext(Config.class);
@Test
public void testInjection() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
ShouldBeConfiguredBySpring myObject = new ShouldBeConfiguredBySpring();
Assert.assertEquals("Rod", myObject.getName());
}
@Configuration
@ImportResource("org/springframework/beans/factory/aspectj/beanConfigurerTests-beans.xml")
@EnableSpringConfigured
static class Config {
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -53,7 +53,7 @@ import org.springframework.beans.factory.xml.XmlReaderContext;
import org.springframework.core.io.DescriptiveResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.util.StringUtils;
/**
* A Groovy-based reader for Spring bean definitions: like a Groovy builder,
@@ -108,6 +108,9 @@ import org.springframework.core.io.support.ResourcePatternUtils;
* }
* }</pre>
*
* <p><b>This bean definition reader also understands XML bean definition files,
* allowing for seamless mixing and matching with Groovy bean definition files.</b>
*
* <p>Typically applied to a
* {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
* or a {@link org.springframework.context.support.GenericApplicationContext},
@@ -187,7 +190,9 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
// TRADITIONAL BEAN DEFINITION READER METHODS
/**
* Load bean definitions from the specified Groovy script.
* Load bean definitions from the specified Groovy script or XML file.
* <p>Note that ".xml" files will be parsed as XML content; all other kinds
* of resources will be parsed as Groovy scripts.
* @param resource the resource descriptor for the Groovy script
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
@@ -197,14 +202,22 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
}
/**
* Load bean definitions from the specified Groovy script.
* Load bean definitions from the specified Groovy script or XML file.
* <p>Note that ".xml" files will be parsed as XML content; all other kinds
* of resources will be parsed as Groovy scripts.
* @param encodedResource the resource descriptor for the Groovy script,
* allowing to specify an encoding to use for parsing the file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Closure beans = new Closure(this){
// Check for XML files and redirect them to the XmlBeanDefinitionReader
String filename = encodedResource.getResource().getFilename();
if (StringUtils.endsWithIgnoreCase(filename, ".xml")) {
return this.xmlBeanDefinitionReader.loadBeanDefinitions(encodedResource);
}
Closure beans = new Closure(this) {
public Object call(Object[] args) {
invokeBeanDefiningClosure((Closure) args[0]);
return null;
@@ -213,7 +226,7 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
Binding binding = new Binding() {
@Override
public void setVariable(String name, Object value) {
if (currentBeanDefinition !=null) {
if (currentBeanDefinition != null) {
applyPropertyToBeanDefinition(name, value);
}
else {
@@ -321,17 +334,7 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
* @param resourcePattern the resource pattern
*/
public void importBeans(String resourcePattern) throws IOException {
Resource[] resources =
ResourcePatternUtils.getResourcePatternResolver(getResourceLoader()).getResources(resourcePattern);
for (Resource resource : resources) {
String filename = resource.getFilename();
if (filename.endsWith(".groovy")) {
loadBeanDefinitions(resource);
}
else if (filename.endsWith(".xml")) {
this.xmlBeanDefinitionReader.loadBeanDefinitions(resource);
}
}
loadBeanDefinitions(resourcePattern);
}
@@ -410,7 +413,7 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
}
dp.apply();
}
deferredProperties.clear();
this.deferredProperties.clear();
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@ import java.util.Map;
* implementation of actual property access left to subclasses.
*
* @author Juergen Hoeller
* @author Stephane Nicoll
* @since 2.0
* @see #getPropertyValue
* @see #setPropertyValue
@@ -35,6 +36,8 @@ public abstract class AbstractPropertyAccessor extends TypeConverterSupport impl
private boolean extractOldValueForEditor = false;
private boolean autoGrowNestedPaths = false;
@Override
public void setExtractOldValueForEditor(boolean extractOldValueForEditor) {
@@ -46,6 +49,16 @@ public abstract class AbstractPropertyAccessor extends TypeConverterSupport impl
return this.extractOldValueForEditor;
}
@Override
public void setAutoGrowNestedPaths(boolean autoGrowNestedPaths) {
this.autoGrowNestedPaths = autoGrowNestedPaths;
}
@Override
public boolean isAutoGrowNestedPaths() {
return this.autoGrowNestedPaths;
}
@Override
public void setPropertyValue(PropertyValue pv) throws BeansException {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,8 +29,7 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -38,6 +37,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
@@ -57,9 +57,8 @@ public abstract class BeanUtils {
private static final Log logger = LogFactory.getLog(BeanUtils.class);
// Effectively using a WeakHashMap as a Set
private static final Map<Class<?>, Boolean> unknownEditorTypes =
Collections.synchronizedMap(new WeakHashMap<Class<?>, Boolean>());
private static final Set<Class<?>> unknownEditorTypes =
Collections.newSetFromMap(new ConcurrentReferenceHashMap<Class<?>, Boolean>(64));
/**
@@ -404,7 +403,7 @@ public abstract class BeanUtils {
* @return the corresponding editor, or {@code null} if none found
*/
public static PropertyEditor findEditorByConvention(Class<?> targetType) {
if (targetType == null || targetType.isArray() || unknownEditorTypes.containsKey(targetType)) {
if (targetType == null || targetType.isArray() || unknownEditorTypes.contains(targetType)) {
return null;
}
ClassLoader cl = targetType.getClassLoader();
@@ -431,7 +430,7 @@ public abstract class BeanUtils {
logger.warn("Editor class [" + editorName +
"] does not implement [java.beans.PropertyEditor] interface");
}
unknownEditorTypes.put(targetType, Boolean.TRUE);
unknownEditorTypes.add(targetType);
return null;
}
return (PropertyEditor) instantiateClass(editorClass);
@@ -441,7 +440,7 @@ public abstract class BeanUtils {
logger.debug("No property editor [" + editorName + "] found for type " +
targetType.getName() + " according to 'Editor' suffix convention");
}
unknownEditorTypes.put(targetType, Boolean.TRUE);
unknownEditorTypes.add(targetType);
return null;
}
}
@@ -78,22 +78,6 @@ public interface BeanWrapper extends ConfigurablePropertyAccessor {
*/
PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException;
/**
* Set whether this BeanWrapper should attempt to "auto-grow" a
* nested path that contains a {@code null} value.
* <p>If {@code true}, a {@code null} path location will be populated
* with a default object value and traversed instead of resulting in a
* {@link NullValueInNestedPathException}. Turning this flag on also enables
* auto-growth of collection elements when accessing an out-of-bounds index.
* <p>Default is {@code false} on a plain BeanWrapper.
*/
void setAutoGrowNestedPaths(boolean autoGrowNestedPaths);
/**
* Return whether "auto-growing" of nested paths has been activated.
*/
boolean isAutoGrowNestedPaths();
/**
* Specify a limit for array and collection auto-growing.
* <p>Default is unlimited on a plain BeanWrapper.
@@ -115,8 +115,6 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
*/
private Map<String, BeanWrapperImpl> nestedBeanWrappers;
private boolean autoGrowNestedPaths = false;
private int autoGrowCollectionLimit = Integer.MAX_VALUE;
@@ -252,25 +250,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
return (this.rootObject != null ? this.rootObject.getClass() : null);
}
/**
* Set whether this BeanWrapper should attempt to "auto-grow" a nested path that contains a null value.
* <p>If "true", a null path location will be populated with a default object value and traversed
* instead of resulting in a {@link NullValueInNestedPathException}. Turning this flag on also
* enables auto-growth of collection elements when accessing an out-of-bounds index.
* <p>Default is "false" on a plain BeanWrapper.
*/
@Override
public void setAutoGrowNestedPaths(boolean autoGrowNestedPaths) {
this.autoGrowNestedPaths = autoGrowNestedPaths;
}
/**
* Return whether "auto-growing" of nested paths has been activated.
*/
@Override
public boolean isAutoGrowNestedPaths() {
return this.autoGrowNestedPaths;
}
/**
* Specify a limit for array and collection auto-growing.
@@ -398,7 +378,8 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
if (pd.getReadMethod() != null || pd.getWriteMethod() != null) {
return TypeDescriptor.nested(property(pd), tokens.keys.length);
}
} else {
}
else {
if (pd.getReadMethod() != null || pd.getWriteMethod() != null) {
return new TypeDescriptor(property(pd));
}
@@ -570,7 +551,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
String canonicalName = tokens.canonicalName;
Object propertyValue = getPropertyValue(tokens);
if (propertyValue == null) {
if (this.autoGrowNestedPaths) {
if (isAutoGrowNestedPaths()) {
propertyValue = setDefaultValue(tokens);
}
else {
@@ -761,7 +742,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
if (tokens.keys != null) {
if (value == null) {
if (this.autoGrowNestedPaths) {
if (isAutoGrowNestedPaths()) {
value = setDefaultValue(tokens.actualName);
}
else {
@@ -851,7 +832,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
}
private Object growArrayIfNecessary(Object array, int index, String name) {
if (!this.autoGrowNestedPaths) {
if (!isAutoGrowNestedPaths()) {
return array;
}
int length = Array.getLength(array);
@@ -874,7 +855,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
private void growCollectionIfNecessary(Collection<Object> collection, int index, String name,
PropertyDescriptor pd, int nestingLevel) {
if (!this.autoGrowNestedPaths) {
if (!isAutoGrowNestedPaths()) {
return;
}
int size = collection.size();
@@ -951,7 +932,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
String key = tokens.keys[tokens.keys.length - 1];
if (propValue == null) {
// null map value case
if (this.autoGrowNestedPaths) {
if (isAutoGrowNestedPaths()) {
// TODO: cleanup, this is pretty hacky
int lastKeyIndex = tokens.canonicalName.lastIndexOf('[');
getterTokens.canonicalName = tokens.canonicalName.substring(0, lastKeyIndex);
@@ -20,15 +20,12 @@ import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
@@ -38,6 +35,7 @@ import org.springframework.core.SpringProperties;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.StringUtils;
/**
@@ -107,14 +105,22 @@ public class CachedIntrospectionResults {
* Set of ClassLoaders that this CachedIntrospectionResults class will always
* accept classes from, even if the classes do not qualify as cache-safe.
*/
static final Set<ClassLoader> acceptedClassLoaders = new HashSet<ClassLoader>();
static final Set<ClassLoader> acceptedClassLoaders =
Collections.newSetFromMap(new ConcurrentHashMap<ClassLoader, Boolean>(16));
/**
* Map keyed by class containing CachedIntrospectionResults.
* Needs to be a WeakHashMap with WeakReferences as values to allow
* for proper garbage collection in case of multiple class loaders.
* Map keyed by Class containing CachedIntrospectionResults, strongly held.
* This variant is being used for cache-safe bean classes.
*/
static final Map<Class<?>, Object> classCache = new WeakHashMap<Class<?>, Object>();
static final Map<Class<?>, CachedIntrospectionResults> strongClassCache =
new ConcurrentHashMap<Class<?>, CachedIntrospectionResults>(64);
/**
* Map keyed by Class containing CachedIntrospectionResults, softly held.
* This variant is being used for non-cache-safe bean classes.
*/
static final Map<Class<?>, CachedIntrospectionResults> softClassCache =
new ConcurrentReferenceHashMap<Class<?>, CachedIntrospectionResults>(64);
/**
@@ -131,9 +137,7 @@ public class CachedIntrospectionResults {
*/
public static void acceptClassLoader(ClassLoader classLoader) {
if (classLoader != null) {
synchronized (acceptedClassLoaders) {
acceptedClassLoaders.add(classLoader);
}
acceptedClassLoaders.add(classLoader);
}
}
@@ -144,20 +148,22 @@ public class CachedIntrospectionResults {
* @param classLoader the ClassLoader to clear the cache for
*/
public static void clearClassLoader(ClassLoader classLoader) {
synchronized (classCache) {
for (Iterator<Class<?>> it = classCache.keySet().iterator(); it.hasNext();) {
Class<?> beanClass = it.next();
if (isUnderneathClassLoader(beanClass.getClassLoader(), classLoader)) {
it.remove();
}
for (Iterator<ClassLoader> it = acceptedClassLoaders.iterator(); it.hasNext();) {
ClassLoader registeredLoader = it.next();
if (isUnderneathClassLoader(registeredLoader, classLoader)) {
it.remove();
}
}
synchronized (acceptedClassLoaders) {
for (Iterator<ClassLoader> it = acceptedClassLoaders.iterator(); it.hasNext();) {
ClassLoader registeredLoader = it.next();
if (isUnderneathClassLoader(registeredLoader, classLoader)) {
it.remove();
}
for (Iterator<Class<?>> it = strongClassCache.keySet().iterator(); it.hasNext();) {
Class<?> beanClass = it.next();
if (isUnderneathClassLoader(beanClass.getClassLoader(), classLoader)) {
it.remove();
}
}
for (Iterator<Class<?>> it = softClassCache.keySet().iterator(); it.hasNext();) {
Class<?> beanClass = it.next();
if (isUnderneathClassLoader(beanClass.getClassLoader(), classLoader)) {
it.remove();
}
}
}
@@ -170,35 +176,25 @@ public class CachedIntrospectionResults {
*/
@SuppressWarnings("unchecked")
static CachedIntrospectionResults forClass(Class<?> beanClass) throws BeansException {
CachedIntrospectionResults results;
Object value;
synchronized (classCache) {
value = classCache.get(beanClass);
CachedIntrospectionResults results = strongClassCache.get(beanClass);
if (results != null) {
return results;
}
if (value instanceof Reference) {
Reference<CachedIntrospectionResults> ref = (Reference<CachedIntrospectionResults>) value;
results = ref.get();
results = softClassCache.get(beanClass);
if (results != null) {
return results;
}
results = new CachedIntrospectionResults(beanClass);
if (ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) ||
isClassLoaderAccepted(beanClass.getClassLoader())) {
strongClassCache.put(beanClass, results);
}
else {
results = (CachedIntrospectionResults) value;
}
if (results == null) {
if (ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) ||
isClassLoaderAccepted(beanClass.getClassLoader())) {
results = new CachedIntrospectionResults(beanClass);
synchronized (classCache) {
classCache.put(beanClass, results);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Not strongly caching class [" + beanClass.getName() + "] because it is not cache-safe");
}
results = new CachedIntrospectionResults(beanClass);
synchronized (classCache) {
classCache.put(beanClass, new WeakReference<CachedIntrospectionResults>(results));
}
if (logger.isDebugEnabled()) {
logger.debug("Not strongly caching class [" + beanClass.getName() + "] because it is not cache-safe");
}
softClassCache.put(beanClass, results);
}
return results;
}
@@ -211,13 +207,7 @@ public class CachedIntrospectionResults {
* @see #acceptClassLoader
*/
private static boolean isClassLoaderAccepted(ClassLoader classLoader) {
// Iterate over array copy in order to avoid synchronization for the entire
// ClassLoader check (avoiding a synchronized acceptedClassLoaders Iterator).
ClassLoader[] acceptedLoaderArray;
synchronized (acceptedClassLoaders) {
acceptedLoaderArray = acceptedClassLoaders.toArray(new ClassLoader[acceptedClassLoaders.size()]);
}
for (ClassLoader acceptedLoader : acceptedLoaderArray) {
for (ClassLoader acceptedLoader : acceptedClassLoaders) {
if (isUnderneathClassLoader(classLoader, acceptedLoader)) {
return true;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import org.springframework.core.convert.ConversionService;
* <p>Serves as base interface for {@link BeanWrapper}.
*
* @author Juergen Hoeller
* @author Stephane Nicoll
* @since 2.0
* @see BeanWrapper
*/
@@ -54,4 +55,19 @@ public interface ConfigurablePropertyAccessor extends PropertyAccessor, Property
*/
boolean isExtractOldValueForEditor();
/**
* Set whether this instance should attempt to "auto-grow" a
* nested path that contains a {@code null} value.
* <p>If {@code true}, a {@code null} path location will be populated
* with a default object value and traversed instead of resulting in a
* {@link NullValueInNestedPathException}.
* <p>Default is {@code false} on a plain instance.
*/
void setAutoGrowNestedPaths(boolean autoGrowNestedPaths);
/**
* Return whether "auto-growing" of nested paths has been activated.
*/
boolean isAutoGrowNestedPaths();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author 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,28 +16,31 @@
package org.springframework.beans;
import java.beans.PropertyChangeEvent;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import java.beans.PropertyChangeEvent;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* {@link PropertyAccessor} implementation that directly accesses instance fields.
* Allows for direct binding to fields instead of going through JavaBean setters.
*
* <p>This implementation just supports fields in the actual target object.
* It is not able to traverse nested fields.
* <p>As of Spring 4.1, this implementation supports nested field traversal.
*
* <p>A DirectFieldAccessor's default for the "extractOldValueForEditor" setting
* is "true", since a field can always be read without side effects.
*
* @author Juergen Hoeller
* @author Stephane Nicoll
* @since 2.0
* @see #setExtractOldValueForEditor
* @see BeanWrapper
@@ -46,112 +49,264 @@ import java.util.Map;
*/
public class DirectFieldAccessor extends AbstractPropertyAccessor {
private final Object target;
private final Object rootObject;
private final Map<String, Field> fieldMap = new HashMap<String, Field>();
private final Map<String, FieldAccessor> fieldMap = new HashMap<String, FieldAccessor>();
/**
* Create a new DirectFieldAccessor for the given target object.
* @param target the target object to access
* Create a new DirectFieldAccessor for the given root object.
* @param rootObject the root object to access
*/
public DirectFieldAccessor(final Object target) {
Assert.notNull(target, "Target object must not be null");
this.target = target;
ReflectionUtils.doWithFields(this.target.getClass(), new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) {
if (fieldMap.containsKey(field.getName())) {
// ignore superclass declarations of fields already found in a subclass
}
else {
fieldMap.put(field.getName(), field);
}
}
});
this.typeConverterDelegate = new TypeConverterDelegate(this, target);
public DirectFieldAccessor(final Object rootObject) {
Assert.notNull(rootObject, "Root object must not be null");
this.rootObject = rootObject;
this.typeConverterDelegate = new TypeConverterDelegate(this, rootObject);
registerDefaultEditors();
setExtractOldValueForEditor(true);
}
/**
* Return the root object at the top of the path of this instance.
*/
public final Object getRootInstance() {
return this.rootObject;
}
/**
* Return the class of the root object at the top of the path of this instance.
*/
public final Class<?> getRootClass() {
return (this.rootObject != null ? this.rootObject.getClass() : null);
}
@Override
public boolean isReadableProperty(String propertyName) throws BeansException {
return this.fieldMap.containsKey(propertyName);
return hasProperty(propertyName);
}
@Override
public boolean isWritableProperty(String propertyName) throws BeansException {
return this.fieldMap.containsKey(propertyName);
return hasProperty(propertyName);
}
@Override
public Class<?> getPropertyType(String propertyName) throws BeansException {
Field field = this.fieldMap.get(propertyName);
if (field != null) {
return field.getType();
public Class<?> getPropertyType(String propertyPath) throws BeansException {
FieldAccessor fieldAccessor = getFieldAccessor(propertyPath);
if (fieldAccessor != null) {
return fieldAccessor.getField().getType();
}
return null;
}
@Override
public TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException {
Field field = this.fieldMap.get(propertyName);
if (field != null) {
return new TypeDescriptor(field);
FieldAccessor fieldAccessor = getFieldAccessor(propertyName);
if (fieldAccessor != null) {
return new TypeDescriptor(fieldAccessor.getField());
}
return null;
}
@Override
public Object getPropertyValue(String propertyName) throws BeansException {
Field field = this.fieldMap.get(propertyName);
if (field == null) {
FieldAccessor fieldAccessor = getFieldAccessor(propertyName);
if (fieldAccessor == null) {
throw new NotReadablePropertyException(
this.target.getClass(), propertyName, "Field '" + propertyName + "' does not exist");
}
try {
ReflectionUtils.makeAccessible(field);
return field.get(this.target);
}
catch (IllegalAccessException ex) {
throw new InvalidPropertyException(this.target.getClass(), propertyName, "Field is not accessible", ex);
getRootClass(), propertyName, "Field '" + propertyName + "' does not exist");
}
return fieldAccessor.getValue();
}
@Override
public void setPropertyValue(String propertyName, Object newValue) throws BeansException {
Field field = this.fieldMap.get(propertyName);
if (field == null) {
FieldAccessor fieldAccessor = getFieldAccessor(propertyName);
if (fieldAccessor == null) {
throw new NotWritablePropertyException(
this.target.getClass(), propertyName, "Field '" + propertyName + "' does not exist");
getRootClass(), propertyName, "Field '" + propertyName + "' does not exist");
}
Field field = fieldAccessor.getField();
Object oldValue = null;
try {
ReflectionUtils.makeAccessible(field);
oldValue = field.get(this.target);
oldValue = fieldAccessor.getValue();
Object convertedValue = this.typeConverterDelegate.convertIfNecessary(
field.getName(), oldValue, newValue, field.getType(), new TypeDescriptor(field));
field.set(this.target, convertedValue);
fieldAccessor.setValue(convertedValue);
}
catch (ConverterNotFoundException ex) {
PropertyChangeEvent pce = new PropertyChangeEvent(this.target, propertyName, oldValue, newValue);
PropertyChangeEvent pce = new PropertyChangeEvent(getRootInstance(), propertyName, oldValue, newValue);
throw new ConversionNotSupportedException(pce, field.getType(), ex);
}
catch (ConversionException ex) {
PropertyChangeEvent pce = new PropertyChangeEvent(this.target, propertyName, oldValue, newValue);
PropertyChangeEvent pce = new PropertyChangeEvent(getRootInstance(), propertyName, oldValue, newValue);
throw new TypeMismatchException(pce, field.getType(), ex);
}
catch (IllegalStateException ex) {
PropertyChangeEvent pce = new PropertyChangeEvent(this.target, propertyName, oldValue, newValue);
PropertyChangeEvent pce = new PropertyChangeEvent(getRootInstance(), propertyName, oldValue, newValue);
throw new ConversionNotSupportedException(pce, field.getType(), ex);
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce = new PropertyChangeEvent(this.target, propertyName, oldValue, newValue);
PropertyChangeEvent pce = new PropertyChangeEvent(getRootInstance(), propertyName, oldValue, newValue);
throw new TypeMismatchException(pce, field.getType(), ex);
}
catch (IllegalAccessException ex) {
throw new InvalidPropertyException(this.target.getClass(), propertyName, "Field is not accessible", ex);
}
private boolean hasProperty(String propertyPath) {
Assert.notNull(propertyPath, "PropertyPath must not be null");
return getFieldAccessor(propertyPath) != null;
}
private FieldAccessor getFieldAccessor(String propertyPath) {
FieldAccessor fieldAccessor = this.fieldMap.get(propertyPath);
if (fieldAccessor == null) {
fieldAccessor = doGetFieldAccessor(propertyPath, getRootClass());
this.fieldMap.put(propertyPath, fieldAccessor);
}
return fieldAccessor;
}
private FieldAccessor doGetFieldAccessor(String propertyPath, Class<?> targetClass) {
StringTokenizer st = new StringTokenizer(propertyPath, ".");
FieldAccessor accessor = null;
Class<?> parentType = targetClass;
while (st.hasMoreTokens()) {
String localProperty = st.nextToken();
Field field = ReflectionUtils.findField(parentType, localProperty);
if (field == null) {
return null;
}
if (accessor == null) {
accessor = root(propertyPath, localProperty, field);
}
else {
accessor = accessor.child(localProperty, field);
}
parentType = field.getType();
}
return accessor;
}
/**
* Create a root {@link FieldAccessor}.
* @param canonicalName the full expression for the field to access
* @param actualName the name of the local (root) property
* @param field the field accessing the property
*/
private FieldAccessor root(String canonicalName, String actualName, Field field) {
return new FieldAccessor(null, canonicalName, actualName, field);
}
/**
* Provide an easy access to a potentially hierarchical value.
*/
private class FieldAccessor {
private final List<FieldAccessor> parents;
private final String canonicalName;
private final String actualName;
private final Field field;
/**
* Create a new FieldAccessor instance.
* @param parent the parent accessor, if any
* @param canonicalName the full expression for the field to access
* @param actualName the name of the partial expression for this property
* @param field the field accessing the property
*/
public FieldAccessor(FieldAccessor parent, String canonicalName, String actualName, Field field) {
Assert.notNull(canonicalName, "Expression must no be null");
Assert.notNull(field, "Field must no be null");
this.parents = buildParents(parent);
this.canonicalName = canonicalName;
this.actualName = actualName;
this.field = field;
}
/**
* Create a child instance.
* @param actualName the name of the child property
* @param field the field accessing the child property
*/
public FieldAccessor child(String actualName, Field field) {
return new FieldAccessor(this, this.canonicalName, this.actualName + "." + actualName, field);
}
public Field getField() {
return this.field;
}
public Object getValue() {
Object localTarget = getLocalTarget(getRootInstance());
return getParentValue(localTarget);
}
public void setValue(Object value) {
Object localTarget = getLocalTarget(getRootInstance());
try {
this.field.set(localTarget, value);
}
catch (IllegalAccessException ex) {
throw new InvalidPropertyException(localTarget.getClass(), canonicalName, "Field is not accessible", ex);
}
}
private Object getParentValue(Object target) {
try {
ReflectionUtils.makeAccessible(this.field);
return this.field.get(target);
}
catch (IllegalAccessException ex) {
throw new InvalidPropertyException(target.getClass(),
this.canonicalName, "Field is not accessible", ex);
}
}
private Object getLocalTarget(Object rootTarget) {
Object localTarget = rootTarget;
for (FieldAccessor parent : parents) {
localTarget = autoGrowIfNecessary(parent, parent.getParentValue(localTarget));
if (localTarget == null) { // Could not traverse the graph any further
throw new NullValueInNestedPathException(getRootClass(), parent.actualName,
"Cannot access indexed value of property referenced in indexed property path '" +
getField().getName() + "': returned null");
}
}
return localTarget;
}
private Object newValue() {
Class<?> type = getField().getType();
try {
return type.newInstance();
}
catch (Exception ex) {
throw new NullValueInNestedPathException(getRootClass(), this.actualName,
"Could not instantiate property type [" + type.getName() +
"] to auto-grow nested property path: " + ex);
}
}
private Object autoGrowIfNecessary(FieldAccessor accessor, Object value) {
if (value == null && isAutoGrowNestedPaths()) {
Object defaultValue = accessor.newValue();
accessor.setValue(defaultValue);
return defaultValue;
}
return value;
}
private List<FieldAccessor> buildParents(FieldAccessor parent) {
List<FieldAccessor> parents = new ArrayList<FieldAccessor>();
if (parent != null) {
parents.addAll(parent.parents);
parents.add(parent);
}
return parents;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,18 +42,18 @@ class GenericTypeAwarePropertyDescriptor extends PropertyDescriptor {
private final Class<?> beanClass;
private final Class<?> propertyEditorClass;
private final Method readMethod;
private final Method writeMethod;
private final Class<?> propertyEditorClass;
private volatile Set<Method> ambiguousWriteMethods;
private Class<?> propertyType;
private MethodParameter writeMethodParameter;
private Class<?> propertyType;
public GenericTypeAwarePropertyDescriptor(Class<?> beanClass, String propertyName,
Method readMethod, Method writeMethod, Class<?> propertyEditorClass)
@@ -78,27 +78,44 @@ class GenericTypeAwarePropertyDescriptor extends PropertyDescriptor {
this.readMethod = readMethodToUse;
this.writeMethod = writeMethodToUse;
if (this.writeMethod != null && this.readMethod == null) {
// Write method not matched against read method: potentially ambiguous through
// several overloaded variants, in which case an arbitrary winner has been chosen
// by the JDK's JavaBeans Introspector...
Set<Method> ambiguousCandidates = new HashSet<Method>();
for (Method method : beanClass.getMethods()) {
if (method.getName().equals(writeMethodToUse.getName()) &&
!method.equals(writeMethodToUse) && !method.isBridge()) {
ambiguousCandidates.add(method);
if (this.writeMethod != null) {
if (this.readMethod == null) {
// Write method not matched against read method: potentially ambiguous through
// several overloaded variants, in which case an arbitrary winner has been chosen
// by the JDK's JavaBeans Introspector...
Set<Method> ambiguousCandidates = new HashSet<Method>();
for (Method method : beanClass.getMethods()) {
if (method.getName().equals(writeMethodToUse.getName()) &&
!method.equals(writeMethodToUse) && !method.isBridge()) {
ambiguousCandidates.add(method);
}
}
if (!ambiguousCandidates.isEmpty()) {
this.ambiguousWriteMethods = ambiguousCandidates;
}
}
if (!ambiguousCandidates.isEmpty()) {
this.ambiguousWriteMethods = ambiguousCandidates;
}
this.writeMethodParameter = new MethodParameter(this.writeMethod, 0);
GenericTypeResolver.resolveParameterType(this.writeMethodParameter, this.beanClass);
}
if (this.readMethod != null) {
this.propertyType = GenericTypeResolver.resolveReturnType(this.readMethod, this.beanClass);
}
else if (this.writeMethodParameter != null) {
this.propertyType = this.writeMethodParameter.getParameterType();
}
}
public Class<?> getBeanClass() {
return this.beanClass;
}
@Override
public Class<?> getPropertyEditorClass() {
return this.propertyEditorClass;
}
@Override
public Method getReadMethod() {
return this.readMethod;
@@ -120,39 +137,13 @@ class GenericTypeAwarePropertyDescriptor extends PropertyDescriptor {
return this.writeMethod;
}
@Override
public Class<?> getPropertyEditorClass() {
return this.propertyEditorClass;
}
@Override
public synchronized Class<?> getPropertyType() {
if (this.propertyType == null) {
if (this.readMethod != null) {
this.propertyType = GenericTypeResolver.resolveReturnType(this.readMethod, this.beanClass);
}
else {
MethodParameter writeMethodParam = getWriteMethodParameter();
if (writeMethodParam != null) {
this.propertyType = writeMethodParam.getParameterType();
}
else {
this.propertyType = super.getPropertyType();
}
}
}
return this.propertyType;
}
public synchronized MethodParameter getWriteMethodParameter() {
if (this.writeMethod == null) {
return null;
}
if (this.writeMethodParameter == null) {
this.writeMethodParameter = new MethodParameter(this.writeMethod, 0);
GenericTypeResolver.resolveParameterType(this.writeMethodParameter, this.beanClass);
}
public MethodParameter getWriteMethodParameter() {
return this.writeMethodParameter;
}
@Override
public Class<?> getPropertyType() {
return this.propertyType;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -89,7 +89,7 @@ public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
static {
try {
zoneIdClass = PropertyEditorRegistrySupport.class.getClassLoader().loadClass("java.time.ZoneId");
zoneIdClass = ClassUtils.forName("java.time.ZoneId", PropertyEditorRegistrySupport.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
// Java 8 ZoneId class not available
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -172,7 +172,8 @@ final class PropertyMatches {
char t_j = s2.charAt(j - 1);
if (s_i == t_j) {
cost = 0;
} else {
}
else {
cost = 1;
}
d[i][j] = Math.min(Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1),
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -53,6 +53,20 @@ class TypeConverterDelegate {
private static final Log logger = LogFactory.getLog(TypeConverterDelegate.class);
/** Java 8's java.util.Optional.empty() instance */
private static Object javaUtilOptionalEmpty = null;
static {
try {
Class<?> clazz = ClassUtils.forName("java.util.Optional", TypeConverterDelegate.class.getClassLoader());
javaUtilOptionalEmpty = ClassUtils.getMethod(clazz, "empty").invoke(null);
}
catch (Exception ex) {
// Java 8 not available - conversion to Optional not supported then.
}
}
private final PropertyEditorRegistrySupport propertyEditorRegistry;
private final Object targetObject;
@@ -244,6 +258,12 @@ class TypeConverterDelegate {
standardConversion = true;
}
}
else {
// convertedValue == null
if (javaUtilOptionalEmpty != null && requiredType.equals(javaUtilOptionalEmpty.getClass())) {
convertedValue = javaUtilOptionalEmpty;
}
}
if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
if (firstAttemptEx != null) {
@@ -289,19 +309,19 @@ class TypeConverterDelegate {
if (index > - 1) {
String enumType = trimmedValue.substring(0, index);
String fieldName = trimmedValue.substring(index + 1);
ClassLoader loader = this.targetObject.getClass().getClassLoader();
ClassLoader cl = this.targetObject.getClass().getClassLoader();
try {
Class<?> enumValueType = loader.loadClass(enumType);
Class<?> enumValueType = ClassUtils.forName(enumType, cl);
Field enumField = enumValueType.getField(fieldName);
convertedValue = enumField.get(null);
}
catch (ClassNotFoundException ex) {
if(logger.isTraceEnabled()) {
logger.trace("Enum class [" + enumType + "] cannot be loaded from [" + loader + "]", ex);
if (logger.isTraceEnabled()) {
logger.trace("Enum class [" + enumType + "] cannot be loaded", ex);
}
}
catch (Throwable ex) {
if(logger.isTraceEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Field [" + fieldName + "] isn't an enum value for type [" + enumType + "]", ex);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -170,8 +170,7 @@ public interface BeanFactory {
* <p>Allows for specifying explicit constructor arguments / factory method arguments,
* overriding the specified default arguments (if any) in the bean definition.
* @param name the name of the bean to retrieve
* @param args arguments to use if creating a prototype using explicit arguments to a
* static factory method. It is invalid to use a non-null args value in any other case.
* @param args arguments to use if creating a prototype using explicit arguments
* @return an instance of the bean
* @throws NoSuchBeanDefinitionException if there is no such bean definition
* @throws BeanDefinitionStoreException if arguments have been given but
@@ -181,6 +180,27 @@ public interface BeanFactory {
*/
Object getBean(String name, Object... args) throws BeansException;
/**
* Return an instance, which may be shared or independent, of the specified bean.
* <p>Allows for specifying explicit constructor arguments / factory method arguments,
* overriding the specified default arguments (if any) in the bean definition.
* @param requiredType type the bean must match; can be an interface or superclass.
* {@code null} is disallowed.
* <p>This method goes into {@link ListableBeanFactory} by-type lookup territory
* but may also be translated into a conventional by-name lookup based on the name
* of the given type. For more extensive retrieval operations across sets of beans,
* use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}.
* @param args arguments to use if creating a prototype using explicit arguments to a
* static factory method. It is invalid to use a non-null args value in any other case.
* @return an instance of the bean
* @throws NoSuchBeanDefinitionException if there is no such bean definition
* @throws BeanDefinitionStoreException if arguments have been given but
* the affected bean isn't a prototype
* @throws BeansException if the bean could not be created
* @since 4.1
*/
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
/**
* Does this bean factory contain a bean definition or externally registered singleton
* instance with the given name?
@@ -101,7 +101,7 @@ public interface ListableBeanFactory extends BeanFactory {
* by other means than bean definitions.
* <p>This version of {@code getBeanNamesForType} matches all kinds of beans,
* be it singletons, prototypes, or FactoryBeans. In most implementations, the
* result will be the same as for {@code getBeanNamesOfType(type, true, true)}.
* result will be the same as for {@code getBeanNamesForType(type, true, true)}.
* <p>Bean names returned by this method should always return bean names <i>in the
* order of definition</i> in the backend configuration, as far as possible.
* @param type the class or interface to match, or {@code null} for all bean names
@@ -0,0 +1,58 @@
/*
* Copyright 2002-2014 the original author 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;
/**
* Callback interface triggered at the end of the singleton pre-instantiation phase
* during {@link BeanFactory} bootstrap. This interface can be implemented by
* singleton beans in order to perform some initialization after the regular
* singleton instantiation algorithm, avoiding side effects with accidental early
* initialization (e.g. from {@link ListableBeanFactory#getBeansOfType} calls).
* In that sense, it is an alternative to {@link InitializingBean} which gets
* triggered right at the end of a bean's local construction phase.
*
* <p>This callback variant is somewhat similar to
* {@link org.springframework.context.event.ContextRefreshedEvent} but doesn't
* require an implementation of {@link org.springframework.context.ApplicationListener},
* with no need to filter context references across a context hierarchy etc.
* It also implies a more minimal dependency on just the {@code beans} package
* and is being honored by standalone {@link ListableBeanFactory} implementations,
* not just in an {@link org.springframework.context.ApplicationContext} environment.
*
* <p><b>NOTE:</b> If you intend to start/manage asynchronous tasks, preferably
* implement {@link org.springframework.context.Lifecycle} instead which offers
* a richer model for runtime management and allows for phased startup/shutdown.
*
* @author Juergen Hoeller
* @since 4.1
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletons()
*/
public interface SmartInitializingSingleton {
/**
* Invoked right at the end of the singleton pre-instantiation phase,
* with a guarantee that all regular singleton beans have been created
* already. {@link ListableBeanFactory#getBeansOfType} calls within
* this method won't trigger accidental side effects during bootstrap.
* <p><b>NOTE:</b> This callback won't be triggered for singleton beans
* lazily initialized on demand after {@link BeanFactory} bootstrap,
* and not for any other bean scope either. Carefully use it for beans
* with the intended bootstrap semantics only.
*/
void afterSingletonsInstantiated();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
@@ -44,10 +45,12 @@ import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.LookupOverride;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.BridgeMethodResolver;
@@ -96,6 +99,12 @@ import org.springframework.util.StringUtils;
* thus the latter configuration will override the former for properties wired through
* both approaches.
*
* <p>In addition to regular injection points as discussed above, this post-processor
* also handles Spring's {@link Lookup @Lookup} annotation which identifies lookup
* methods to be replaced by the container at runtime. This is essentially a type-safe
* version of {@code getBean(Class, args)} and {@code getBean(String, args)},
* See {@link Lookup @Lookup's javadoc} for details.
*
* @author Juergen Hoeller
* @author Mark Fisher
* @since 2.5
@@ -119,6 +128,9 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
private ConfigurableListableBeanFactory beanFactory;
private final Set<String> lookupMethodsChecked =
Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(64));
private final Map<Class<?>, Constructor<?>[]> candidateConstructorsCache =
new ConcurrentHashMap<Class<?>, Constructor<?>[]>(64);
@@ -135,9 +147,9 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
public AutowiredAnnotationBeanPostProcessor() {
this.autowiredAnnotationTypes.add(Autowired.class);
this.autowiredAnnotationTypes.add(Value.class);
ClassLoader cl = AutowiredAnnotationBeanPostProcessor.class.getClassLoader();
try {
this.autowiredAnnotationTypes.add((Class<? extends Annotation>) cl.loadClass("javax.inject.Inject"));
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
}
catch (ClassNotFoundException ex) {
@@ -224,7 +236,28 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
}
@Override
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName) throws BeansException {
if (!this.lookupMethodsChecked.contains(beanName)) {
ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Lookup lookup = method.getAnnotation(Lookup.class);
if (lookup != null) {
LookupOverride override = new LookupOverride(method, lookup.value());
try {
RootBeanDefinition mbd = (RootBeanDefinition) beanFactory.getMergedBeanDefinition(beanName);
mbd.getMethodOverrides().addOverride(override);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(beanName,
"Cannot apply @Lookup to beans without corresponding bean definition");
}
}
}
});
this.lookupMethodsChecked.add(beanName);
}
// Quick check on the concurrent map first, with minimal locking.
Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
@@ -239,7 +272,8 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
AnnotationAttributes annotation = findAutowiredAnnotation(candidate);
if (annotation != null) {
if (requiredConstructor != null) {
throw new BeanCreationException("Invalid autowire-marked constructor: " + candidate +
throw new BeanCreationException(beanName,
"Invalid autowire-marked constructor: " + candidate +
". Found another constructor with 'required' Autowired annotation: " +
requiredConstructor);
}
@@ -250,10 +284,10 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
boolean required = determineRequiredStatus(annotation);
if (required) {
if (!candidates.isEmpty()) {
throw new BeanCreationException(
throw new BeanCreationException(beanName,
"Invalid autowire-marked constructors: " + candidates +
". Found another constructor with 'required' Autowired annotation: " +
requiredConstructor);
candidate);
}
requiredConstructor = candidate;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@ import org.springframework.util.ObjectUtils;
* Spring's {@link Qualifier @Qualifier} annotation.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1.2
* @see BeanFactoryUtils
*/
@@ -90,9 +91,13 @@ public class BeanFactoryAnnotationUtils {
if (matchingBean != null) {
return matchingBean;
}
else if (bf.containsBean(qualifier)) {
// Fallback: target bean at least found by bean name - probably a manually registered singleton.
return bf.getBean(qualifier, beanType);
}
else {
throw new NoSuchBeanDefinitionException(qualifier, "No matching " + beanType.getSimpleName() +
" bean found for qualifier '" + qualifier + "' - neither qualifier " + "match nor bean name match!");
" bean found for qualifier '" + qualifier + "' - neither qualifier match nor bean name match!");
}
}
@@ -128,7 +133,7 @@ public class BeanFactoryAnnotationUtils {
}
}
catch (NoSuchBeanDefinitionException ex) {
// ignore - can't compare qualifiers for a manually registered singleton object
// Ignore - can't compare qualifiers for a manually registered singleton object
}
}
return false;
@@ -0,0 +1,65 @@
/*
* Copyright 2002-2014 the original author 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.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* An annotation that indicates 'lookup' methods, to be overridden by the container
* to redirect them back to the {@link org.springframework.beans.factory.BeanFactory}
* for a {@code getBean} call. This is essentially an annotation-based version of the
* XML {@code lookup-method} attribute, resulting in the same runtime arrangement.
*
* <p>The resolution of the target bean can either be based on the return type
* ({@code getBean(Class)}) or on a suggested bean name ({@code getBean(String)}),
* in both cases passing the method's arguments to the {@code getBean} call
* for applying them as target factory method arguments or constructor arguments.
*
* <p>Such lookup methods can have default (stub) implementations that will simply
* get replaced by the container, or they can be declared as abstract - for the
* container to fill them in at runtime. In both cases, the container will generate
* runtime subclasses of the method's containing class via CGLIB, which is why such
* lookup methods can only work on beans that the container instantiates through
* regular constructors (i.e. lookup methods cannot get replaced on beans returned
* from factory methods where we can't dynamically provide a subclass for them).
*
* <p>Note: When used with component scanning or any other mechanism that filters
* out abstract beans, provide stub implementations of your lookup methods to be
* able to declare them as concrete classes.
*
* @author Juergen Hoeller
* @since 4.1
* @see org.springframework.beans.factory.BeanFactory#getBean(Class, Object...)
* @see org.springframework.beans.factory.BeanFactory#getBean(String, Object...)
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lookup {
/**
* This annotation attribute may suggest a target bean name to look up.
* If not specified, the target bean will be resolved based on the
* annotated method's return type declaration.
*/
String value() default "";
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -66,9 +66,9 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
@SuppressWarnings("unchecked")
public QualifierAnnotationAutowireCandidateResolver() {
this.qualifierTypes.add(Qualifier.class);
ClassLoader cl = QualifierAnnotationAutowireCandidateResolver.class.getClassLoader();
try {
this.qualifierTypes.add((Class<? extends Annotation>) cl.loadClass("javax.inject.Qualifier"));
this.qualifierTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.inject.Qualifier", QualifierAnnotationAutowireCandidateResolver.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -166,7 +166,7 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
* Set the names of the beans that this bean depends on being initialized.
* The bean factory will guarantee that these beans get initialized first.
*/
void setDependsOn(String[] dependsOn);
void setDependsOn(String... dependsOn);
/**
* Return whether this bean is a candidate for getting autowired into some other bean.
@@ -1,117 +0,0 @@
/*
* 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.config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.SmartFactoryBean;
/**
* FactoryBean that exposes an arbitrary target bean under a different name.
*
* <p>Usually, the target bean will reside in a different bean definition file,
* using this FactoryBean to link it in and expose it under a different name.
* Effectively, this corresponds to an alias for the target bean.
*
* <p><b>NOTE:</b> For XML bean definition files, an {@code &lt;alias&gt;}
* tag is available that effectively achieves the same.
*
* <p>A special capability of this FactoryBean is enabled through its configuration
* as bean definition: The "targetBeanName" can be substituted through a placeholder,
* in combination with Spring's {@link PropertyPlaceholderConfigurer}.
* Thanks to Marcus Bristav for pointing this out!
*
* @author Juergen Hoeller
* @since 1.2
* @see #setTargetBeanName
* @see PropertyPlaceholderConfigurer
* @deprecated as of Spring 3.2, in favor of using regular bean name aliases
* (which support placeholder parsing since Spring 2.5)
*/
@Deprecated
public class BeanReferenceFactoryBean implements SmartFactoryBean<Object>, BeanFactoryAware {
private String targetBeanName;
private BeanFactory beanFactory;
/**
* Set the name of the target bean.
* <p>This property is required. The value for this property can be
* substituted through a placeholder, in combination with Spring's
* PropertyPlaceholderConfigurer.
* @param targetBeanName the name of the target bean
* @see PropertyPlaceholderConfigurer
*/
public void setTargetBeanName(String targetBeanName) {
this.targetBeanName = targetBeanName;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
if (this.targetBeanName == null) {
throw new IllegalArgumentException("'targetBeanName' is required");
}
if (!this.beanFactory.containsBean(this.targetBeanName)) {
throw new NoSuchBeanDefinitionException(this.targetBeanName, this.beanFactory.toString());
}
}
@Override
public Object getObject() throws BeansException {
if (this.beanFactory == null) {
throw new FactoryBeanNotInitializedException();
}
return this.beanFactory.getBean(this.targetBeanName);
}
@Override
public Class<?> getObjectType() {
if (this.beanFactory == null) {
return null;
}
return this.beanFactory.getType(this.targetBeanName);
}
@Override
public boolean isSingleton() {
if (this.beanFactory == null) {
throw new FactoryBeanNotInitializedException();
}
return this.beanFactory.isSingleton(this.targetBeanName);
}
@Override
public boolean isPrototype() {
if (this.beanFactory == null) {
throw new FactoryBeanNotInitializedException();
}
return this.beanFactory.isPrototype(this.targetBeanName);
}
@Override
public boolean isEagerInit() {
return false;
}
}
@@ -1,78 +0,0 @@
/*
* 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.config;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
/**
* Factory bean for
* <a href="http://jakarta.apache.org/commons/logging.html">commons-logging</a>
* {@link org.apache.commons.logging.Log} instances.
*
* <p>Useful for sharing Log instances among multiple beans instead of using
* one Log instance per class name, e.g. for common log topics.
*
* @author Juergen Hoeller
* @since 16.11.2003
* @see org.apache.commons.logging.Log
* @deprecated as of Spring 3.2, in favor of a bean definition that points
* to the bean class "org.apache.commons.logging.LogFactory" and the factory
* method "getLog", passing in the log name as constructor argument
*/
@Deprecated
public class CommonsLogFactoryBean implements FactoryBean<Log>, InitializingBean {
private Log log;
/**
* The name of the log.
* <p>This property is required.
* @param logName the name of the log
*/
public void setLogName(String logName) {
this.log = LogFactory.getLog(logName);
}
@Override
public void afterPropertiesSet() {
if (this.log == null) {
throw new IllegalArgumentException("'logName' is required");
}
}
@Override
public Log getObject() {
return this.log;
}
@Override
public Class<? extends Log> getObjectType() {
return (this.log != null ? this.log.getClass() : Log.class);
}
@Override
public boolean isSingleton() {
return true;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -263,18 +263,20 @@ public class DependencyDescriptor implements Serializable {
if (this.field != null) {
if (this.nestingLevel > 1) {
Type type = this.field.getGenericType();
if (type instanceof ParameterizedType) {
Type[] args = ((ParameterizedType) type).getActualTypeArguments();
Type arg = args[args.length - 1];
for (int i = 2; i <= this.nestingLevel; i++) {
if (type instanceof ParameterizedType) {
Type[] args = ((ParameterizedType) type).getActualTypeArguments();
type = args[args.length - 1];
}
}
if (type instanceof Class) {
return (Class<?>) type;
}
else if (type instanceof ParameterizedType) {
Type arg = ((ParameterizedType) type).getRawType();
if (arg instanceof Class) {
return (Class<?>) arg;
}
else if (arg instanceof ParameterizedType) {
arg = ((ParameterizedType) arg).getRawType();
if (arg instanceof Class) {
return (Class<?>) arg;
}
}
}
return Object.class;
}
@@ -0,0 +1,142 @@
/*
* Copyright 2002-2014 the original author 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.config;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
/**
* Factory for a Map that reads from a YAML source. YAML is a nice human-readable
* format for configuration, and it has some useful hierarchical properties. It's
* more or less a superset of JSON, so it has a lot of similar features. If
* multiple resources are provided the later ones will override entries in the
* earlier ones hierarchically - that is all entries with the same nested key of
* type Map at any depth are merged. For example:
*
* <pre class="code">
* foo:
* bar:
* one: two
* three: four
* </pre>
*
* plus (later in the list)
*
* <pre class="code">
* foo:
* bar:
* one: 2
* five: six
* </pre>
*
* results in an effective input of
*
* <pre class="code">
* foo:
* bar:
* one: 2
* three: four
* five: six
* </pre>
*
* Note that the value of "foo" in the first document is not simply replaced
* with the value in the second, but its nested values are merged.
*
* @author Dave Syer
* @since 4.1
*/
public class YamlMapFactoryBean extends YamlProcessor implements FactoryBean<Map<String, Object>>, InitializingBean {
private boolean singleton = true;
private Map<String, Object> map;
/**
* Set if a singleton should be created, or a new object on each request
* otherwise. Default is {@code true} (a singleton).
*/
public void setSingleton(boolean singleton) {
this.singleton = singleton;
}
@Override
public boolean isSingleton() {
return this.singleton;
}
@Override
public void afterPropertiesSet() {
if (isSingleton()) {
this.map = createMap();
}
}
@Override
public Map<String, Object> getObject() {
return (this.map != null ? this.map : createMap());
}
@Override
public Class<?> getObjectType() {
return Map.class;
}
/**
* Template method that subclasses may override to construct the object
* returned by this factory. The default implementation returns the
* merged Map instance.
* <p>Invoked lazily the first time {@link #getObject()} is invoked in
* case of a shared singleton; else, on each {@link #getObject()} call.
* @return the object returned by this factory
* @see #process(java.util.Map, MatchCallback)
*/
protected Map<String, Object> createMap() {
final Map<String, Object> result = new LinkedHashMap<String, Object>();
process(new MatchCallback() {
@Override
public void process(Properties properties, Map<String, Object> map) {
merge(result, map);
}
});
return result;
}
@SuppressWarnings({"unchecked", "rawtypes"})
private void merge(Map<String, Object> output, Map<String, Object> map) {
for (Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
Object existing = output.get(key);
if (value instanceof Map && existing instanceof Map) {
Map<String, Object> result = new LinkedHashMap<String, Object>((Map) existing);
merge(result, (Map) value);
output.put(key, result);
}
else {
output.put(key, value);
}
}
}
}
@@ -0,0 +1,357 @@
/*
* Copyright 2002-2014 the original author 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.config;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.yaml.snakeyaml.Yaml;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Base class for Yaml factories.
*
* @author Dave Syer
* @since 4.1
*/
public abstract class YamlProcessor {
private final Log logger = LogFactory.getLog(getClass());
private ResolutionMethod resolutionMethod = ResolutionMethod.OVERRIDE;
private Resource[] resources = new Resource[0];
private List<DocumentMatcher> documentMatchers = Collections.emptyList();
private boolean matchDefault = true;
/**
* A map of document matchers allowing callers to selectively use only
* some of the documents in a YAML resource. In YAML documents are
* separated by <code>---<code> lines, and each document is converted
* to properties before the match is made. E.g.
*
* <pre class="code">
* environment: dev
* url: http://dev.bar.com
* name: Developer Setup
* ---
* environment: prod
* url:http://foo.bar.com
* name: My Cool App
* </pre>
*
* when mapped with
* <code>documentMatchers = YamlProcessor.mapMatcher({"environment": "prod"})</code>
* would end up as
*
* <pre class="code">
* environment=prod
* url=http://foo.bar.com
* name=My Cool App
* url=http://dev.bar.com
* </pre>
* @param matchers a map of keys to value patterns (regular expressions)
*/
public void setDocumentMatchers(DocumentMatcher... matchers) {
this.documentMatchers = Collections
.unmodifiableList(new ArrayList<DocumentMatcher>(Arrays.asList(matchers)));
}
/**
* Flag indicating that a document for which all the
* {@link #setDocumentMatchers(DocumentMatcher...) document matchers} abstain will
* nevertheless match.
* @param matchDefault the flag to set (default true)
*/
public void setMatchDefault(boolean matchDefault) {
this.matchDefault = matchDefault;
}
/**
* Method to use for resolving resources. Each resource will be converted to a Map, so
* this property is used to decide which map entries to keep in the final output from
* this factory.
* @param resolutionMethod the resolution method to set (defaults to
* {@link ResolutionMethod#OVERRIDE}).
*/
public void setResolutionMethod(ResolutionMethod resolutionMethod) {
Assert.notNull(resolutionMethod, "ResolutionMethod must not be null");
this.resolutionMethod = resolutionMethod;
}
/**
* Set locations of YAML {@link Resource resources} to be loaded.
* @see ResolutionMethod
*/
public void setResources(Resource... resources) {
this.resources = (resources == null ? null : resources.clone());
}
/**
* Provide an opportunity for subclasses to process the Yaml parsed from the supplied
* resources. Each resource is parsed in turn and the documents inside checked against
* the {@link #setDocumentMatchers(DocumentMatcher...) matchers}. If a document
* matches it is passed into the callback, along with its representation as
* Properties. Depending on the {@link #setResolutionMethod(ResolutionMethod)} not all
* of the documents will be parsed.
* @param callback a callback to delegate to once matching documents are found
*/
protected void process(MatchCallback callback) {
Yaml yaml = new Yaml();
for (Resource resource : this.resources) {
boolean found = process(callback, yaml, resource);
if (this.resolutionMethod == ResolutionMethod.FIRST_FOUND && found) {
return;
}
}
}
private boolean process(MatchCallback callback, Yaml yaml, Resource resource) {
int count = 0;
try {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Loading from YAML: " + resource);
}
for (Object object : yaml.loadAll(resource.getInputStream())) {
if (object != null && process(asMap(object), callback)) {
count++;
if (this.resolutionMethod == ResolutionMethod.FIRST_FOUND) {
break;
}
}
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Loaded " + count + " document"
+ (count > 1 ? "s" : "") + " from YAML resource: " + resource);
}
}
catch (IOException ex) {
handleProcessError(resource, ex);
}
return (count > 0);
}
private void handleProcessError(Resource resource, IOException ex) {
if (this.resolutionMethod != ResolutionMethod.FIRST_FOUND &&
this.resolutionMethod != ResolutionMethod.OVERRIDE_AND_IGNORE) {
throw new IllegalStateException(ex);
}
if (this.logger.isWarnEnabled()) {
this.logger.warn("Could not load map from " + resource + ": " + ex.getMessage());
}
}
@SuppressWarnings("unchecked")
private Map<String, Object> asMap(Object object) {
// YAML can have numbers as keys
Map<String, Object> result = new LinkedHashMap<String, Object>();
if (!(object instanceof Map)) {
// A document can be a text literal
result.put("document", object);
return result;
}
Map<Object, Object> map = (Map<Object, Object>) object;
for (Entry<Object, Object> entry : map.entrySet()) {
Object value = entry.getValue();
if (value instanceof Map) {
value = asMap(value);
}
Object key = entry.getKey();
if (key instanceof CharSequence) {
result.put(key.toString(), value);
}
else {
// It has to be a map key in this case
result.put("[" + key.toString() + "]", value);
}
}
return result;
}
private boolean process(Map<String, Object> map, MatchCallback callback) {
Properties properties = new Properties();
assignProperties(properties, map, null);
if (this.documentMatchers.isEmpty()) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Merging document (no matchers set)" + map);
}
callback.process(properties, map);
return true;
}
MatchStatus result = MatchStatus.ABSTAIN;
for (DocumentMatcher matcher : this.documentMatchers) {
MatchStatus match = matcher.matches(properties);
result = MatchStatus.getMostSpecific(match, result);
if (match == MatchStatus.FOUND) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Matched document with document matcher: "
+ properties);
}
callback.process(properties, map);
return true;
}
}
if (result == MatchStatus.ABSTAIN && this.matchDefault) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Matched document with default matcher: " + map);
}
callback.process(properties, map);
return true;
}
this.logger.debug("Unmatched document");
return false;
}
private void assignProperties(Properties properties, Map<String, Object> input, String path) {
for (Entry<String, Object> entry : input.entrySet()) {
String key = entry.getKey();
if (StringUtils.hasText(path)) {
if (key.startsWith("[")) {
key = path + key;
}
else {
key = path + "." + key;
}
}
Object value = entry.getValue();
if (value instanceof String) {
properties.put(key, value);
}
else if (value instanceof Map) {
// Need a compound key
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) value;
assignProperties(properties, map, key);
}
else if (value instanceof Collection) {
// Need a compound key
@SuppressWarnings("unchecked")
Collection<Object> collection = (Collection<Object>) value;
int count = 0;
for (Object object : collection) {
assignProperties(properties,
Collections.singletonMap("[" + (count++) + "]", object), key);
}
}
else {
properties.put(key, value == null ? "" : value);
}
}
}
/**
* Callback interface used to process properties in a resulting map.
*/
public interface MatchCallback {
/**
* Process the properties.
* @param properties the properties to process
* @param map a mutable result map
*/
void process(Properties properties, Map<String, Object> map);
}
/**
* Strategy interface used to test if properties match.
*/
public interface DocumentMatcher {
/**
* Test if the given properties match.
* @param properties the properties to test
* @return the status of the match.
*/
MatchStatus matches(Properties properties);
}
/**
* Status returned from {@link DocumentMatcher#matches(java.util.Properties)}
*/
public static enum MatchStatus {
/**
* A match was found.
*/
FOUND,
/**
* No match was found.
*/
NOT_FOUND,
/**
* The matcher should not be considered.
*/
ABSTAIN;
/**
* Compare two {@link MatchStatus} items, returning the most specific status.
*/
public static MatchStatus getMostSpecific(MatchStatus a, MatchStatus b) {
return a.ordinal() < b.ordinal() ? a : b;
}
}
/**
* Method to use for resolving resources.
*/
public static enum ResolutionMethod {
/**
* Replace values from earlier in the list.
*/
OVERRIDE,
/**
* Replace values from earlier in the list, ignoring any failures.
*/
OVERRIDE_AND_IGNORE,
/**
* Take the first resource in the list that exists and use just that.
*/
FIRST_FOUND
}
}
@@ -0,0 +1,131 @@
/*
* Copyright 2002-2014 the original author 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.config;
import java.util.Map;
import java.util.Properties;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
/**
* Factory for Java Properties that reads from a YAML source. YAML is a nice
* human-readable format for configuration, and it has some useful hierarchical
* properties. It's more or less a superset of JSON, so it has a lot of similar
* features. The Properties created by this factory have nested paths for
* hierarchical objects, so for instance this YAML
*
* <pre class="code">
* environments:
* dev:
* url: http://dev.bar.com
* name: Developer Setup
* prod:
* url: http://foo.bar.com
* name: My Cool App
* </pre>
*
* is transformed into these Properties:
*
* <pre class="code">
* environments.dev.url=http://dev.bar.com
* environments.dev.name=Developer Setup
* environments.prod.url=http://foo.bar.com
* environments.prod.name=My Cool App
* </pre>
*
* Lists are represented as comma-separated values (useful for simple String
* values) and also as property keys with <code>[]</code> dereferencers, for
* example this YAML:
*
* <pre class="code">
* servers:
* - dev.bar.com
* - foo.bar.com
* </pre>
*
* becomes Java Properties like this:
*
* <pre class="code">
* servers=dev.bar.com,foo.bar.com
* servers[0]=dev.bar.com
* servers[1]=foo.bar.com
* </pre>
*
* @author Dave Syer
* @author Stephane Nicoll
* @since 4.1
*/
public class YamlPropertiesFactoryBean extends YamlProcessor implements FactoryBean<Properties>, InitializingBean {
private boolean singleton = true;
private Properties properties;
/**
* Set if a singleton should be created, or a new object on each request
* otherwise. Default is {@code true} (a singleton).
*/
public void setSingleton(boolean singleton) {
this.singleton = singleton;
}
@Override
public boolean isSingleton() {
return this.singleton;
}
@Override
public void afterPropertiesSet() {
if (isSingleton()) {
this.properties = createProperties();
}
}
@Override
public Properties getObject() {
return (this.properties != null ? this.properties : createProperties());
}
@Override
public Class<?> getObjectType() {
return Properties.class;
}
/**
* Template method that subclasses may override to construct the object
* returned by this factory. The default implementation returns a
* properties with the content of all resources.
* <p>Invoked lazily the first time {@link #getObject()} is invoked in
* case of a shared singleton; else, on each {@link #getObject()} call.
* @return the object returned by this factory
* @see #process(MatchCallback) ()
*/
protected Properties createProperties() {
final Properties result = new Properties();
process(new MatchCallback() {
@Override
public void process(Properties properties, Map<String, Object> map) {
result.putAll(properties);
}
});
return result;
}
}
@@ -286,7 +286,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
// Use prototype bean definition, to avoid registering bean as dependent bean.
RootBeanDefinition bd = new RootBeanDefinition(beanClass);
bd.setScope(SCOPE_PROTOTYPE);
bd.allowCaching = false;
bd.allowCaching = ClassUtils.isCacheSafe(beanClass, getBeanClassLoader());
return (T) createBean(beanClass.getName(), bd, null);
}
@@ -295,7 +295,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
// Use non-singleton bean definition, to avoid registering bean as dependent bean.
RootBeanDefinition bd = new RootBeanDefinition(ClassUtils.getUserClass(existingBean));
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bd.allowCaching = false;
bd.allowCaching = ClassUtils.isCacheSafe(bd.getBeanClass(), getBeanClassLoader());
BeanWrapper bw = new BeanWrapperImpl(existingBean);
initBeanWrapper(bw);
populateBean(bd.getBeanClass().getName(), bd, bw);
@@ -315,7 +315,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
bd = new RootBeanDefinition(mbd);
}
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bd.allowCaching = false;
bd.allowCaching = ClassUtils.isCacheSafe(ClassUtils.getUserClass(existingBean), getBeanClassLoader());
}
BeanWrapper bw = new BeanWrapperImpl(existingBean);
initBeanWrapper(bw);
@@ -759,7 +759,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
}
}
});
if (objectType.value != null) {
if (objectType.value != null && !Object.class.equals(objectType.value)) {
return objectType.value;
}
}
@@ -38,8 +38,8 @@ import org.springframework.util.StringUtils;
/**
* Base class for concrete, full-fledged
* {@link org.springframework.beans.factory.config.BeanDefinition} classes,
* factoring out common properties of {@link RootBeanDefinition} and
* {@link ChildBeanDefinition}.
* factoring out common properties of {@link GenericBeanDefinition},
* {@link RootBeanDefinition} and {@link ChildBeanDefinition}.
*
* <p>The autowire constants match the ones defined in the
* {@link org.springframework.beans.factory.config.AutowireCapableBeanFactory}
@@ -123,11 +123,14 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
public static final int DEPENDENCY_CHECK_ALL = 3;
/**
* Constant that indicates the container should attempt to infer the {@link
* #setDestroyMethodName destroy method name} for a bean as opposed to explicit
* specification of a method name. The value {@value} is specifically designed to
* include characters otherwise illegal in a method name, ensuring no possibility of
* collisions with legitimately named methods having the same name.
* Constant that indicates the container should attempt to infer the
* {@link #setDestroyMethodName destroy method name} for a bean as opposed to
* explicit specification of a method name. The value {@value} is specifically
* designed to include characters otherwise illegal in a method name, ensuring
* no possibility of collisions with legitimately named methods having the same
* name.
* <p>Currently, the method names detected during destroy method inference
* are "close" and "shutdown", if present on the specific bean class.
*/
public static final String INFER_METHOD = "(inferred)";
@@ -553,7 +556,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
* of dependencies like statics (*ugh*) or database preparation on startup.
*/
@Override
public void setDependsOn(String[] dependsOn) {
public void setDependsOn(String... dependsOn) {
this.dependsOn = dependsOn;
}
@@ -644,8 +647,8 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
/**
* Specify whether to allow access to non-public constructors and methods,
* for the case of externalized metadata pointing to those.
* The default is {@code true}; switch this to {@false} for public access only.
* for the case of externalized metadata pointing to those. The default is
* {@code true}; switch this to {@code false} for public access only.
* <p>This applies to constructor resolution, factory method resolution,
* and also init/destroy methods. Bean property accessors have to be public
* in any case and are not affected by this setting.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -124,7 +124,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
/** Resolution strategy for expressions in bean definition values */
private BeanExpressionResolver beanExpressionResolver;
/** Spring 3.0 ConversionService to use instead of PropertyEditors */
/** Spring ConversionService to use instead of PropertyEditors */
private ConversionService conversionService;
/** Custom PropertyEditorRegistrars to apply to the beans of this factory */
@@ -160,9 +160,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
private final Map<String, RootBeanDefinition> mergedBeanDefinitions =
new ConcurrentHashMap<String, RootBeanDefinition>(64);
/**
* Names of beans that have already been created at least once
*/
/** Names of beans that have already been created at least once */
private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(64));
/** Names of beans that are currently in creation */
@@ -287,8 +285,8 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
if (isDependent(beanName, dependsOnBean)) {
throw new BeanCreationException("Circular depends-on relationship between '" +
beanName + "' and '" + dependsOnBean + "'");
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
}
registerDependentBean(dependsOnBean, beanName);
getBean(dependsOnBean);
@@ -1276,7 +1274,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
// Check validity of the usage of the args parameter. This can
// only be used for prototypes constructed via a factory method.
if (args != null && !mbd.isPrototype()) {
throw new BeanDefinitionStoreException(
throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
"Can only specify arguments for the getBean method when referring to a prototype bean definition");
}
}
@@ -1627,8 +1625,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
* instantiation within this class is performed by this method.
* @param beanName the name of the bean
* @param mbd the merged bean definition for the bean
* @param args arguments to use if creating a prototype using explicit arguments to a
* static factory method. This parameter must be {@code null} except in this case.
* @param args arguments to use if creating a prototype using explicit arguments
* @return a new instance of the bean
* @throws BeanCreationException if the bean could not be created
*/
@@ -25,7 +25,6 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.cglib.core.SpringNamingPolicy;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.CallbackFilter;
@@ -34,6 +33,7 @@ import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cglib.proxy.NoOp;
import org.springframework.util.StringUtils;
/**
* Default object instantiation strategy for use in BeanFactories.
@@ -89,14 +89,13 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
*/
private static class CglibSubclassCreator {
private static final Class<?>[] CALLBACK_TYPES = new Class<?>[] { NoOp.class,
LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class };
private static final Class<?>[] CALLBACK_TYPES = new Class<?>[]
{NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class};
private final RootBeanDefinition beanDefinition;
private final BeanFactory owner;
CglibSubclassCreator(RootBeanDefinition beanDefinition, BeanFactory owner) {
this.beanDefinition = beanDefinition;
this.owner = owner;
@@ -113,7 +112,6 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
*/
Object instantiate(Constructor<?> ctor, Object[] args) {
Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
Object instance;
if (ctor == null) {
instance = BeanUtils.instantiate(subclass);
@@ -123,19 +121,17 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
instance = enhancedSubclassConstructor.newInstance(args);
}
catch (Exception e) {
catch (Exception ex) {
throw new BeanInstantiationException(this.beanDefinition.getBeanClass(), String.format(
"Failed to invoke construcor for CGLIB enhanced subclass [%s]", subclass.getName()), e);
"Failed to invoke constructor for CGLIB enhanced subclass [%s]", subclass.getName()), ex);
}
}
// SPR-10785: set callbacks directly on the instance instead of in the
// enhanced class (via the Enhancer) in order to avoid memory leaks.
Factory factory = (Factory) instance;
factory.setCallbacks(new Callback[] { NoOp.INSTANCE,//
new LookupOverrideMethodInterceptor(beanDefinition, owner),//
new ReplaceOverrideMethodInterceptor(beanDefinition, owner) });
factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
return instance;
}
@@ -153,6 +149,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
}
}
/**
* Class providing hashCode and equals methods required by CGLIB to
* ensure that CGLIB doesn't generate a distinct class per bean.
@@ -162,7 +159,6 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
private final RootBeanDefinition beanDefinition;
CglibIdentitySupport(RootBeanDefinition beanDefinition) {
this.beanDefinition = beanDefinition;
}
@@ -173,8 +169,8 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
@Override
public boolean equals(Object other) {
return other.getClass().equals(this.getClass())
&& ((CglibIdentitySupport) other).getBeanDefinition().equals(this.getBeanDefinition());
return (getClass().equals(other.getClass()) &&
this.beanDefinition.equals(((CglibIdentitySupport) other).beanDefinition));
}
@Override
@@ -183,6 +179,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
}
}
/**
* CGLIB callback for filtering method interception behavior.
*/
@@ -190,7 +187,6 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
private static final Log logger = LogFactory.getLog(MethodOverrideCallbackFilter.class);
MethodOverrideCallbackFilter(RootBeanDefinition beanDefinition) {
super(beanDefinition);
}
@@ -210,11 +206,12 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
else if (methodOverride instanceof ReplaceOverride) {
return METHOD_REPLACER;
}
throw new UnsupportedOperationException("Unexpected MethodOverride subclass: "
+ methodOverride.getClass().getName());
throw new UnsupportedOperationException("Unexpected MethodOverride subclass: " +
methodOverride.getClass().getName());
}
}
/**
* CGLIB MethodInterceptor to override methods, replacing them with an
* implementation that returns a bean looked up in the container.
@@ -223,7 +220,6 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
private final BeanFactory owner;
LookupOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) {
super(beanDefinition);
this.owner = owner;
@@ -233,10 +229,17 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
// Cast is safe, as CallbackFilter filters are used selectively.
LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
return this.owner.getBean(lo.getBeanName());
Object[] argsToUse = (args.length > 0 ? args : null); // if no-arg, don't insist on args at all
if (StringUtils.hasText(lo.getBeanName())) {
return this.owner.getBean(lo.getBeanName(), argsToUse);
}
else {
return this.owner.getBean(method.getReturnType(), argsToUse);
}
}
}
/**
* CGLIB MethodInterceptor to override methods, replacing them with a call
* to a generic MethodReplacer.
@@ -245,7 +248,6 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
private final BeanFactory owner;
ReplaceOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) {
super(beanDefinition);
this.owner = owner;
@@ -255,7 +257,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
ReplaceOverride ro = (ReplaceOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
// TODO could cache if a singleton for minor performance optimization
MethodReplacer mr = owner.getBean(ro.getMethodReplacerBeanName(), MethodReplacer.class);
MethodReplacer mr = this.owner.getBean(ro.getMethodReplacerBeanName(), MethodReplacer.class);
return mr.reimplement(obj, method, args);
}
}
@@ -370,7 +370,7 @@ class ConstructorResolver {
factoryBean = this.beanFactory.getBean(factoryBeanName);
if (factoryBean == null) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"factory-bean '" + factoryBeanName + "' returned null");
"factory-bean '" + factoryBeanName + "' (or a BeanPostProcessor involved) returned null");
}
factoryClass = factoryBean.getClass();
isStatic = false;
@@ -0,0 +1,61 @@
/*
* Copyright 2002-2014 the original author 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.support;
import java.util.Comparator;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.DefaultOrderProviderComparator;
/**
* The default {@link Comparator} to use to order dependencies. Extends from
* {@link DefaultOrderProviderComparator} so that the bean factory has the ability
* to provide an {@link org.springframework.core.annotation.OrderProvider} that
* is aware of more bean metadata, if any.
*
* @author Stephane Nicoll
* @since 4.1
* @see org.springframework.core.annotation.OrderProviderComparator
* @see org.springframework.core.annotation.OrderProvider
* @see DefaultListableBeanFactory#setDependencyComparator(java.util.Comparator)
*/
public class DefaultDependencyComparator extends DefaultOrderProviderComparator implements Comparator<Object> {
/**
* Shared default instance of DefaultDependencyComparator.
*/
public static final DefaultDependencyComparator INSTANCE = new DefaultDependencyComparator();
private final Comparator<Object> comparator;
public DefaultDependencyComparator() {
this.comparator = AnnotationAwareOrderComparator.INSTANCE;
}
public DefaultDependencyComparator(Comparator<Object> comparator) {
this.comparator = comparator;
}
@Override
public int compare(Object o1, Object o2) {
return this.comparator.compare(o1, o2);
}
}
@@ -32,9 +32,11 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.inject.Provider;
@@ -54,12 +56,16 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.SmartFactoryBean;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.OrderProviderComparator;
import org.springframework.core.annotation.OrderUtils;
import org.springframework.lang.UsesJava8;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
@@ -93,6 +99,7 @@ import org.springframework.util.StringUtils;
* @author Costin Leau
* @author Chris Beams
* @author Phillip Webb
* @author Stephane Nicoll
* @since 16 April 2001
* @see StaticListableBeanFactory
* @see PropertiesBeanDefinitionReader
@@ -102,12 +109,21 @@ import org.springframework.util.StringUtils;
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
private static Class<?> javaUtilOptionalClass = null;
private static Class<?> javaxInjectProviderClass = null;
static {
ClassLoader cl = DefaultListableBeanFactory.class.getClassLoader();
try {
javaxInjectProviderClass = cl.loadClass("javax.inject.Provider");
javaUtilOptionalClass =
ClassUtils.forName("java.util.Optional", DefaultListableBeanFactory.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
// Java 8 not available - Optional references simply not supported then.
}
try {
javaxInjectProviderClass =
ClassUtils.forName("javax.inject.Provider", DefaultListableBeanFactory.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - Provider interface simply not supported then.
@@ -279,6 +295,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
return getBean(requiredType, (Object[]) null);
}
@Override
public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
Assert.notNull(requiredType, "Required type must not be null");
String[] beanNames = getBeanNamesForType(requiredType);
if (beanNames.length > 1) {
@@ -293,27 +314,25 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
}
if (beanNames.length == 1) {
return getBean(beanNames[0], requiredType);
return getBean(beanNames[0], requiredType, args);
}
else if (beanNames.length > 1) {
T primaryBean = null;
Map<String, Object> candidates = new HashMap<String, Object>();
for (String beanName : beanNames) {
T beanInstance = getBean(beanName, requiredType);
if (isPrimary(beanName, beanInstance)) {
if (primaryBean != null) {
throw new NoUniqueBeanDefinitionException(requiredType, beanNames.length,
"more than one 'primary' bean found of required type: " + Arrays.asList(beanNames));
}
primaryBean = beanInstance;
}
candidates.put(beanName, getBean(beanName, requiredType, args));
}
if (primaryBean != null) {
return primaryBean;
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
if (primaryCandidate != null) {
return getBean(primaryCandidate, requiredType, args);
}
throw new NoUniqueBeanDefinitionException(requiredType, beanNames);
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
if (priorityCandidate != null) {
return getBean(priorityCandidate, requiredType, args);
}
throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());
}
else if (getParentBeanFactory() != null) {
return getParentBeanFactory().getBean(requiredType);
return getParentBeanFactory().getBean(requiredType, args);
}
else {
throw new NoSuchBeanDefinitionException(requiredType);
@@ -671,12 +690,15 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (this.logger.isDebugEnabled()) {
this.logger.debug("Pre-instantiating singletons in " + this);
}
List<String> beanNames;
synchronized (this.beanDefinitionMap) {
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
beanNames = new ArrayList<String>(this.beanDefinitionNames);
}
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
@@ -704,6 +726,26 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
}
}
// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
smartSingleton.afterSingletonsInstantiated();
return null;
}
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
@@ -728,8 +770,10 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
}
BeanDefinition oldBeanDefinition;
synchronized (this.beanDefinitionMap) {
BeanDefinition oldBeanDefinition = this.beanDefinitionMap.get(beanName);
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
@@ -758,7 +802,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
this.beanDefinitionMap.put(beanName, beanDefinition);
}
resetBeanDefinition(beanName);
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
@Override
@@ -794,9 +840,6 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
// (e.g. the default StaticMessageSource in a StaticApplicationContext).
destroySingleton(beanName);
// Remove any assumptions about by-type mappings.
clearByTypeCache();
// Reset all bean definitions that have the given bean as parent (recursively).
for (String bdName : this.beanDefinitionNames) {
if (!beanName.equals(bdName)) {
@@ -846,7 +889,10 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
if (descriptor.getDependencyType().equals(ObjectFactory.class)) {
if (descriptor.getDependencyType().equals(javaUtilOptionalClass)) {
return new OptionalDependencyFactory().createOptionalDependency(descriptor, beanName);
}
else if (descriptor.getDependencyType().equals(ObjectFactory.class)) {
return new DependencyObjectFactory(descriptor, beanName);
}
else if (descriptor.getDependencyType().equals(javaxInjectProviderClass)) {
@@ -895,7 +941,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
Object result = converter.convertIfNecessary(matchingBeans.values(), type);
if (this.dependencyComparator != null && result instanceof Object[]) {
Arrays.sort((Object[]) result, this.dependencyComparator);
sortArray((Object[]) result, matchingBeans);
}
return result;
}
@@ -922,7 +968,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
Object result = converter.convertIfNecessary(matchingBeans.values(), type);
if (this.dependencyComparator != null && result instanceof List) {
Collections.sort((List<?>) result, this.dependencyComparator);
sortList((List<?>) result, matchingBeans);
}
return result;
}
@@ -965,7 +1011,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
return null;
}
if (matchingBeans.size() > 1) {
String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor);
String primaryBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (primaryBeanName == null) {
throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
}
@@ -983,6 +1029,34 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
}
private void sortArray(Object[] items, Map<String, Object> matchingBeans) {
if (this.dependencyComparator instanceof OrderProviderComparator) {
((OrderProviderComparator) this.dependencyComparator)
.sortArray(items, createFactoryAwareOrderProvider(matchingBeans));
}
else {
Arrays.sort(items, this.dependencyComparator);
}
}
private void sortList(List<?> items, Map<String, Object> matchingBeans) {
if (this.dependencyComparator instanceof OrderProviderComparator) {
((OrderProviderComparator) this.dependencyComparator)
.sortList(items, createFactoryAwareOrderProvider(matchingBeans));
}
else {
Collections.sort(items, this.dependencyComparator);
}
}
private FactoryAwareOrderProvider createFactoryAwareOrderProvider(Map<String, Object> beans) {
IdentityHashMap<Object, String> instancesToBeanNames = new IdentityHashMap<Object, String>();
for (Map.Entry<String, Object> entry : beans.entrySet()) {
instancesToBeanNames.put(entry.getValue(), entry.getKey());
}
return new FactoryAwareOrderProvider(instancesToBeanNames, this);
}
/**
* Find bean instances that match the required type.
* Called during autowiring for the specified bean.
@@ -1013,7 +1087,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
}
for (String candidateName : candidateNames) {
if (!candidateName.equals(beanName) && isAutowireCandidate(candidateName, descriptor)) {
if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) {
result.put(candidateName, getBean(candidateName));
}
}
@@ -1029,15 +1103,45 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
/**
* Determine the primary autowire candidate in the given set of beans.
* Determine the autowire candidate in the given set of beans.
* <p>Looks for {@code @Primary} and {@code @Priority} (in that order).
* @param candidateBeans a Map of candidate names and candidate instances
* that match the required type, as returned by {@link #findAutowireCandidates}
* @param descriptor the target dependency to match against
* @return the name of the primary candidate, or {@code null} if none found
* @return the name of the autowire candidate, or {@code null} if none found
*/
protected String determinePrimaryCandidate(Map<String, Object> candidateBeans, DependencyDescriptor descriptor) {
protected String determineAutowireCandidate(Map<String, Object> candidateBeans, DependencyDescriptor descriptor) {
Class<?> requiredType = descriptor.getDependencyType();
String primaryCandidate = determinePrimaryCandidate(candidateBeans, requiredType);
if (primaryCandidate != null) {
return primaryCandidate;
}
String priorityCandidate = determineHighestPriorityCandidate(candidateBeans, requiredType);
if (priorityCandidate != null) {
return priorityCandidate;
}
// Fallback
for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) {
String candidateBeanName = entry.getKey();
Object beanInstance = entry.getValue();
if (this.resolvableDependencies.values().contains(beanInstance) ||
matchesBeanName(candidateBeanName, descriptor.getDependencyName())) {
return candidateBeanName;
}
}
return null;
}
/**
* Determine the primary candidate in the given set of beans.
* @param candidateBeans a Map of candidate names and candidate instances
* that match the required type
* @param requiredType the target dependency type to match against
* @return the name of the primary candidate, or {@code null} if none found
* @see #isPrimary(String, Object)
*/
protected String determinePrimaryCandidate(Map<String, Object> candidateBeans, Class<?> requiredType) {
String primaryBeanName = null;
String fallbackBeanName = null;
for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) {
String candidateBeanName = entry.getKey();
Object beanInstance = entry.getValue();
@@ -1045,11 +1149,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (primaryBeanName != null) {
boolean candidateLocal = containsBeanDefinition(candidateBeanName);
boolean primaryLocal = containsBeanDefinition(primaryBeanName);
if (candidateLocal == primaryLocal) {
throw new NoUniqueBeanDefinitionException(descriptor.getDependencyType(), candidateBeans.size(),
if (candidateLocal && primaryLocal) {
throw new NoUniqueBeanDefinitionException(requiredType, candidateBeans.size(),
"more than one 'primary' bean found among candidates: " + candidateBeans.keySet());
}
else if (candidateLocal && !primaryLocal) {
else if (candidateLocal) {
primaryBeanName = candidateBeanName;
}
}
@@ -1057,20 +1161,54 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
primaryBeanName = candidateBeanName;
}
}
if (primaryBeanName == null &&
(this.resolvableDependencies.values().contains(beanInstance) ||
matchesBeanName(candidateBeanName, descriptor.getDependencyName()))) {
fallbackBeanName = candidateBeanName;
}
return primaryBeanName;
}
/**
* Determine the candidate with the highest priority in the given set of beans. As
* defined by the {@link org.springframework.core.Ordered} interface, the lowest
* value has the highest priority.
* @param candidateBeans a Map of candidate names and candidate instances
* that match the required type
* @param requiredType the target dependency type to match against
* @return the name of the candidate with the highest priority,
* or {@code null} if none found
* @see #getPriority(Object)
*/
protected String determineHighestPriorityCandidate(Map<String, Object> candidateBeans, Class<?> requiredType) {
String highestPriorityBeanName = null;
Integer highestPriority = null;
for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) {
String candidateBeanName = entry.getKey();
Object beanInstance = entry.getValue();
Integer candidatePriority = getPriority(beanInstance);
if (candidatePriority != null) {
if (highestPriorityBeanName != null) {
if (candidatePriority.equals(highestPriority)) {
throw new NoUniqueBeanDefinitionException(requiredType, candidateBeans.size(),
"Multiple beans found with the same priority ('" + highestPriority + "') " +
"among candidates: " + candidateBeans.keySet());
}
else if (candidatePriority < highestPriority) {
highestPriorityBeanName = candidateBeanName;
highestPriority = candidatePriority;
}
}
else {
highestPriorityBeanName = candidateBeanName;
highestPriority = candidatePriority;
}
}
}
return (primaryBeanName != null ? primaryBeanName : fallbackBeanName);
return highestPriorityBeanName;
}
/**
* Return whether the bean definition for the given bean name has been
* marked as a primary bean.
* @param beanName the name of the bean
* @param beanInstance the corresponding bean instance
* @param beanInstance the corresponding bean instance (can be null)
* @return whether the given bean qualifies as primary
*/
protected boolean isPrimary(String beanName, Object beanInstance) {
@@ -1082,6 +1220,20 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
((DefaultListableBeanFactory) parentFactory).isPrimary(beanName, beanInstance));
}
/**
* Return the priority assigned for the given bean instance by
* the {@code javax.annotation.Priority} annotation.
* <p>If the annotation is not present, returns {@code null}.
* @param beanInstance the bean instance to check (can be null)
* @return the priority assigned to that bean or {@code null} if none is set
*/
protected Integer getPriority(Object beanInstance) {
if (beanInstance != null) {
return OrderUtils.getPriorityValue(beanInstance.getClass());
}
return null;
}
/**
* Determine whether the given candidate name matches the bean name or the aliases
* stored in this bean definition.
@@ -1091,6 +1243,17 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
(candidateName.equals(beanName) || ObjectUtils.containsElement(getAliases(beanName), candidateName)));
}
/**
* Determine whether the given beanName/candidateName pair indicates a self reference,
* i.e. whether the candidate points back to the original bean or to a factory method
* on the original bean.
*/
private boolean isSelfReference(String beanName, String candidateName) {
return (beanName != null && candidateName != null &&
(beanName.equals(candidateName) || (containsBeanDefinition(candidateName) &&
beanName.equals(getMergedLocalBeanDefinition(candidateName).getFactoryBeanName()))));
}
/**
* Raise a NoSuchBeanDefinitionException for an unresolvable dependency.
*/
@@ -1168,6 +1331,25 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
/**
* Separate inner class for avoiding a hard dependency on the {@code javax.inject} API.
*/
@UsesJava8
private class OptionalDependencyFactory {
public Object createOptionalDependency(DependencyDescriptor descriptor, String beanName) {
DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) {
@Override
public boolean isRequired() {
return false;
}
};
descriptorToUse.increaseNestingLevel();
return Optional.ofNullable(doResolveDependency(descriptorToUse, beanName, null, null));
}
}
/**
* Serializable ObjectFactory for lazy resolution of a dependency.
*/
@@ -1175,17 +1357,25 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
private final DependencyDescriptor descriptor;
private final boolean optional;
private final String beanName;
public DependencyObjectFactory(DependencyDescriptor descriptor, String beanName) {
this.descriptor = new DependencyDescriptor(descriptor);
this.descriptor.increaseNestingLevel();
this.optional = this.descriptor.getDependencyType().equals(javaUtilOptionalClass);
this.beanName = beanName;
}
@Override
public Object getObject() throws BeansException {
return doResolveDependency(this.descriptor, this.beanName, null, null);
if (this.optional) {
return new OptionalDependencyFactory().createOptionalDependency(this.descriptor, this.beanName);
}
else {
return doResolveDependency(this.descriptor, this.beanName, null, null);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -277,7 +277,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
@Override
public boolean containsSingleton(String beanName) {
return (this.singletonObjects.containsKey(beanName));
return this.singletonObjects.containsKey(beanName);
}
@Override
@@ -330,8 +330,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
* @see #isSingletonCurrentlyInCreation
*/
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) &&
!this.singletonsCurrentlyInCreation.add(beanName)) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
@@ -343,8 +342,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
* @see #isSingletonCurrentlyInCreation
*/
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) &&
!this.singletonsCurrentlyInCreation.remove(beanName)) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}

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