Compare commits

..

248 Commits

Author SHA1 Message Date
Spring Buildmaster f440f92719 Release version 3.2.6.RELEASE 2013-12-12 01:06:43 -08:00
Phillip Webb c2d98de6a6 Update changelog 2013-12-12 00:28:01 -08:00
Juergen Hoeller bf31f7178d Polishing
(cherry picked from commit a3a4344)
2013-12-11 23:58:16 +01:00
Juergen Hoeller 249d8379ae Polishing
(cherry picked from commit aa6e6c1)
2013-12-11 23:12:34 +01:00
Juergen Hoeller bfcfbe41a1 Made BeanUtils.copyProperties defensive about property type mismatches
Issue: SPR-11209
(cherry picked from commit bc5affa)
2013-12-11 23:12:33 +01:00
Phillip Webb f17ae5a7a8 Replace references to VMware with Pivotal
Replace references to VMware in license, notices and maven generation
with Pivotal, Inc.

Issue: SPR-11073
(cherry picked from commit b085ec9)
2013-12-11 14:10:28 -08:00
Juergen Hoeller 779d5716e5 Moved RequiredAnnotationBeanPostProcessorTests to correct package beans.factory.annotation 2013-12-11 18:45:23 +01:00
Juergen Hoeller 2a6c642408 Added tests for custom qualifier annotations on scoped @Bean methods
Issue: SPR-11116
(cherry picked from commit c5d7977)
2013-12-11 18:44:13 +01:00
Juergen Hoeller 126116e759 DispatcherPortlet never uses a resource forward on Liferay
Issue: SPR-10791
(cherry picked from commit 9c157ea)
2013-12-11 18:43:58 +01:00
Juergen Hoeller 1ea218cee2 MBean registration happens in a fully synchronized fashion for consistent results
Issue: SPR-11002
(cherry picked from commit 8d6d6be)
2013-12-11 18:43:33 +01:00
Juergen Hoeller ec78165072 Component scanning ignores attributes and meta-annotations on non-public annotations
Issue: SPR-11091
(cherry picked from commit 161819f)
2013-12-11 18:42:48 +01:00
Juergen Hoeller e4fb72f6e3 Fixed CGLIB class validation to not raising warning for static final methods
Issue: SPR-11107
(cherry picked from commit 9fbb57c)
2013-12-11 17:46:23 +01:00
Juergen Hoeller 6fb10981b0 Fixed extractOutputParameters to properly extract ResultSet in all cases
Issue: SPR-11076
(cherry picked from commit 3ff3805)
2013-12-11 17:45:51 +01:00
Juergen Hoeller d48b9b1ef7 Actually accept String expressions for "prefetch" and "receive-timeout" in jms namespace
Issue: SPR-10986
(cherry picked from commit ccafccb)
2013-12-11 17:45:15 +01:00
Juergen Hoeller c3907cdae5 Added 546 to Sybase data integrity violation codes
Issue: SPR-11097
(cherry picked from commit 2d2226b)
2013-12-11 17:44:47 +01:00
Juergen Hoeller 7f895222e1 Revised exclusion of java.lang.Class properties
Issue: SPR-11098
(cherry picked from commit 62ea627)
2013-12-11 17:44:16 +01:00
Phillip Webb 22ece06b39 Add a 'bill of materials' project for Maven users
Add 'spring-framework-bom' meta-project that contains the other projects
in a dependencyManagement section.

Issue: SPR-11138
2013-12-11 10:01:23 -06:00
Sam Brannen b20b7415d8 Make methods of TestContext non-final
This commit makes several methods of TestContext non-final in order to
allow TestContext to be mocked — for example with Mockito.

Note, however, that TestContext has been converted to an interface in
Spring Framework 4.0.

Issue: SPR-11144
2013-12-11 01:23:03 +01:00
Sam Brannen 38ad39f467 Do not repopulate RequestContextHolder in ServTEL
This commit fixes a bug introduced in the last commit.

ServletTestExecutionListener (STEL) now tracks whether it has already
populated the RequestContextHolder.

Issue: SPR-11144

Backport-Commit: 800018a817
2013-12-11 01:02:05 +01:00
Sam Brannen 6ded287144 Honor presence of @WebAppConfiguration in ServTEL
The previous commit for issue SPR-11144 revealed a bug in
ServletTestExecutionListener (STEL). Specifically, STEL acted on the
fact that the ApplicationContext for a given TestContext was an
instance of WebApplicationContext. This behavior could potentially
break test code from previous releases of the Spring Framework that
relied on a custom setup of the RequestAttributes in the
RequestContextHolder with a custom WebApplicationContext ContextLoader.

This commit addresses this issue by ensuring that STEL only comes into
play if the test class is annotated with @WebAppConfiguration (for
prepareTestInstance() and beforeTestMethod()) or if the TestContext
attribute named RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE is set to
Boolean.TRUE (for afterTestMethod()).

Issue: SPR-11144

Backport-Commit: 099b10d23b
2013-12-10 23:54:05 +01:00
Juergen Hoeller 6e275b7663 Polishing 2013-12-10 22:48:42 +01:00
Juergen Hoeller 882726377f Fixed ExtendedBeanInfo and its tests to accept JDK 8 b117+ introspection results
Specifically, read and write methods are allowed to express property types with superclass/subclass relationships in both directions now.

Issue: SPR-11139
(cherry picked from commit cb624e2)
2013-12-10 22:48:35 +01:00
Juergen Hoeller 2e15f94cf2 Optimized use of JDBC 3.0 ParameterMetaData.getParameterType, caching information about drivers which do not support that feature
Issue: SPR-11100
(cherry picked from commit 4c8a789)
2013-12-10 13:14:13 +01:00
Juergen Hoeller d0fc38ec76 Fixed inner bean name determination for multi-level nesting scenario, calculating a unique bean name as early as possible now (and for any kind of bean scope)
Our per-bean caching in AutowiredAnnotationBeanPostProcessor and co relies on unique bean names, so this change fixes potential cache mismatch problems occuring there.

Issue: SPR-11131
(cherry picked from commit 242ecdc)
2013-12-10 13:12:41 +01:00
Juergen Hoeller 8e52e650f4 Fixed type resolution for uninitialized factory-method declaration
Issue: SPR-11112
(cherry picked from commit 5dcd287)
2013-12-10 13:12:32 +01:00
Juergen Hoeller 71650c0a44 Fixed evaluation of "!" operator in case of multiple profile expressions
Issue: SPR-11093
(cherry picked from commit 6078c27)
2013-12-10 12:16:22 +01:00
Sam Brannen c89985c1db Ensure ServTEL doesn't reset ReqAttrs by accident
Prior to this commit, the ServletTestExecutionListener did not
overwrite RequestAttributes in the RequestContextHolder if the
ApplicationContext associated with the given TestContext was not a
WebApplicationContext; however, the ServletTestExecutionListener would
clear the RequestAttributes after every test method execution,
regardless of whether the context was a WebApplicationContext or not.
This behavior breaks backwards compatibility with integration tests
that managed the RequestAttributes in RequestContextHolder themselves.

This commit addresses this issue by introducing a TestContext attribute
named RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE in
ServletTestExecutionListener. This attribute is used internally within
ServletTestExecutionListener to ensure that the RequestContextHolder is
only cleared (i.e., reset) if the ServletTestExecutionListener actually
populated the RequestContextHolder.

Issue: SPR-11144

Backport-Commit: a3b022aa48
2013-12-09 15:43:32 +01:00
Juergen Hoeller d4b0751626 Polishing 2013-12-09 00:12:03 +01:00
Juergen Hoeller 6bed18004e Fixed detection of qualifier annotations on scoped-proxy factory methods
Issue: SPR-11116
2013-12-09 00:11:41 +01:00
Phillip Webb dfed8afb26 Only consider "is" methods with boolean returns
Fix regression introduced in b25e91a5 where ReflectivePropertyAccessor
does not consider the return type for "is" getters.

Issue: SPR-11142
(cherry picked from commit 85b0bfff)
2013-12-03 12:06:26 -08:00
Rossen Stoyanchev 236981aaa9 Support multi-value x-forwarded-host header
Issue: SPR-11140
2013-12-03 09:55:47 -05:00
Juergen Hoeller 6ce2eb9622 EhCacheFactoryBean calls CacheManager.addCache before setStatisticsEnabled
Issue: SPR-11080
Issue: SPR-11092
2013-12-03 01:30:22 +01:00
Juergen Hoeller 6224ddd0cb Polishing (including removal of javadoc imports that show as package cycles in IntelliJ)
(cherry picked from commit 2a52dec)
2013-12-03 01:28:45 +01:00
Juergen Hoeller 25c7e3645b Polishing (including removal of javadoc imports that show as package cycles in IntelliJ)
(cherry picked from commit 2a52dec)
2013-12-03 00:00:31 +01:00
Juergen Hoeller 9c9cc7fcb4 Use AnnotationTransactionAttributeSource class name to avoid hard dependency on transaction.annotation package
(cherry picked from commit 4133272)
2013-12-02 23:59:36 +01:00
Juergen Hoeller 5639eb66b6 Polishing 2013-12-02 16:32:02 +01:00
Juergen Hoeller 1dc79f7f9c Synchronized cache creation on CacheManager
Issue: SPR-11132
(cherry picked from commit de890fd)
2013-12-02 16:10:44 +01:00
Juergen Hoeller c1b772cd9c Optimized ImportAware processing to avoid re-reading class files
(cherry picked from commit 519f78c)
2013-12-02 16:10:34 +01:00
Phillip Webb 73a8a1b966 Detect cache hit with multiple @Cachables
Fix CacheAspectSupport to consider a cache hit from any of the multiple
@Cachables that may have been specified using the @Caching annotation.

Prior to this commit the following scenario would never produce a hit:

    @Caching(cacheable = {
            @Cacheable(value = "c1", unless = "#result.size() < 4"),
            @Cacheable(value = "c2", unless = "#result.size() > 3")
     })

Issue: SPR-11124
2013-11-26 17:13:50 -08:00
Spring Buildmaster 0f26a7795d Next development version 2013-11-06 11:51:48 -08:00
Phillip Webb 47ae15b7ee Update changelog 2013-11-06 11:15:37 -08:00
Juergen Hoeller df7b661702 Added test for placeholder in alias definition
Issue: SPR-11018
(cherry picked from commit e9a24da)
2013-11-06 00:36:56 +01:00
Juergen Hoeller fe46a01a57 JndiObjectFactoryBean converts a "defaultObject" value to the expected type if necessary
Issue: SPR-11039
(cherry picked from commit 0aedd81)
2013-11-06 00:36:48 +01:00
Rossen Stoyanchev 2f13e05b4f Return 415 on bad request body content-type
Issue: SPR-10982
2013-11-05 15:38:04 -05:00
Rossen Stoyanchev 4b38dc8972 Fix exception mock request builder
Issue: SPR-11043
2013-11-05 11:00:02 -05:00
Rossen Stoyanchev 7387cb990e Disable ext entities in SourceHttpMessageConverter
This change disables the processing of external entities in
SourceHttpMessageConverter by default and provides an option to enable
it if required.
2013-11-05 09:49:13 -05:00
Juergen Hoeller 863570abb7 Perform setParent's Environment merging for ClassPathXmlApplicationContext etc constructors with parent argument as well
Issue: SPR-11068
(cherry picked from commit c5fcf19)
2013-11-05 14:18:14 +01:00
Juergen Hoeller 70907fd326 Only retrieve the FlashMapManager if a non-empty output FlashMap has been found
Issue: SPR-10937
(cherry picked from commit 4ac6801)
2013-11-05 14:18:08 +01:00
Juergen Hoeller 494cc22bcd Polishing (along with SPR-10992) 2013-11-05 00:52:49 +01:00
Juergen Hoeller 044d68b336 @Bean method metadata is always being picked from the most concrete subclass
As a side effect, @Bean overrides and overloads work with 'allowBeanDefinitionOverriding'=false as well now.

Issue: SPR-10992
2013-11-05 00:52:28 +01:00
Juergen Hoeller a95eb1d044 Polishing (selectively backported from recent 4.0 commits) 2013-11-04 16:22:46 +01:00
Juergen Hoeller b00c31a620 Use non-lenient constructor resolution mode for @Bean methods
Since @Bean methods are never used with externally specified constructor argument values but rather just with autowiring, the non-lenient constructor resolution mode is appropriate in case of an overloaded @Bean method, not performing any type difference weight checks. This change includes a refinement of Spring's existing non-lenient constructor resolution (which needs to be explicitly turned on and is therefore not well tested), narrowing the conditions for the ambiguity check (only in case of the same number of arguments and not for overridden methods).

Issue: SPR-10988
(cherry picked from commit b093b84)
2013-11-04 16:11:04 +01:00
Juergen Hoeller 23cc44f16e Javadoc polishing 2013-11-01 23:04:07 +01:00
Juergen Hoeller e1a065906e Avoid unnecessary char[] allocation in NamedParameterUtils
Issue: SPR-11042
(cherry picked from commit 1dc7ff8)
2013-11-01 23:02:56 +01:00
Juergen Hoeller 3bfddc501d Backported further GenericTypeResolver tests
Issue: SPR-11052
2013-10-31 17:39:39 +01:00
Rossen Stoyanchev f9081bedb4 Add timeout async request handling to OSIV components
This change adds async web request timeout handling to OSIV filters
and interceptors to ensure the session or entity manager is released.

Issue: SPR-10874
2013-10-30 23:12:09 -04:00
Juergen Hoeller 1ac8e48ebf Polishing (backported from several master changes) 2013-10-30 22:47:16 +01:00
Rossen Stoyanchev d4f4225e53 Avoid filling up AntPathMatcher cache
This change avoid use of AntPathMatcher to extract URI template
variables when the lookup path is used as the best matching pattern.

Issue: SPR-10803
(cherry picked from commit a7af950)
2013-10-30 22:18:11 +01:00
Juergen Hoeller c53184cb6d Refined logging for advisors which are currently in creation
Issue: SPR-10430
(cherry picked from commit 62157fe)
2013-10-30 22:12:24 +01:00
Phillip Webb f9f106936c Relax JavaBean rules for SpEL property access
Relax the method search algorithm used by `ReflectivePropertyAccessor`
to include methods of the form `getXY()` for properties of the form
`xy`.

Although the JavaBean specification indicates that a property `xy`
should use the accessors `getxY()` and `setxY()`, in practice many
developers choose to have an uppercase first character. The
`ReflectivePropertyAccessor` will now consider these style methods if
the traditional conventions fail to find a match.

Issue: SPR-10716
(cherry picked from commit b25e91a5)
2013-10-30 13:02:07 -07:00
Phillip Webb bcf7aecf0f Skip convert to Object from TypeConverterDelegate
Update TypeConverterDelegate to bypass conversion when the target type
is Object.class. Prior to this commit converting a single element array
to an Object would result in the element being returned, rather than
the array itself.

Issue: SPR-10996
(cherry picked from commit c9aace4d)
2013-10-30 13:02:07 -07:00
Phillip Webb 5854d519a1 Add targetIsClass to SpEL property cache key
Update the `CacheKey` class used by `ReflectivePropertyAccessor` to
include if the target object is class. The prevents an incorrect cache
hit from being returned when a property with the same name is read on
both an object and its class. For example:

	#{class.name}
	#{name}

Issue: SPR-10486
(cherry picked from commit 6d882b14)
2013-10-30 13:02:07 -07:00
Phillip Webb 1ea7f741fe Multicaster support for events with null source
Update `AbstractApplicationEventMulticaster` to support `EventObjects`
with a null source (which can happen if they have been serialized).

Issue: SPR-10945
(cherry picked from commit b0ff834e)
2013-10-30 13:02:07 -07:00
Phillip Webb 8c6fe2cb42 Consistent ordering for @PropertySource locations
Ensure that property source locations are processed in the same order
regardless if the 'name' attribute is set or not.

Prior to this commit multiple locations from a `@PropertySource` with
a name were added to a `CompositePropertySource` in such a way that
the first location would take precedence. This has now been reversed
for consistence with unnamed `@PropertySource`s

Issue: SPR-10820
(cherry picked from commit e3d3d8cd)
2013-10-30 13:02:06 -07:00
Rossen Stoyanchev 7a5a6893e3 Avoid filling up AntPathMatcher cache
This change avoid use of AntPathMatcher to extract URI template
variables when the lookup path is used as the best matching pattern.

Issue: SPR-10803
2013-10-30 14:34:13 -04:00
Juergen Hoeller c8e8d6cd49 Defensive handling of exceptions during factory method type checking
Also using ClassUtils.forName in AutowireUtils now in order to accept all common class name formats.

Issue: SPR-11034
(cherry picked from commit 8c1767e)
2013-10-28 13:47:11 +01:00
Juergen Hoeller 094ff8883d Polishing 2013-10-27 22:52:36 +01:00
Juergen Hoeller 44f79f95a7 Fixed resolveReturnTypeForFactoryMethod to unwrap TypedStringValue
XML-defined arguments values are initially turned into TypedStringValue wrappers. If we encounter an unresolved argument, we need to unwrap such a TypedStringValue and then try to treat its content as a class name.

Issue: SPR-11034
2013-10-27 22:48:57 +01:00
Juergen Hoeller 3eaec64ee8 Fixed source location of setEncoding method 2013-10-27 00:57:37 +02:00
Juergen Hoeller ff9c695599 Minor optimizations in factory method handling
Optimized getTypeForFactoryMethod's implementation for non-generic factory methods, and reduced calls to getResolvedFactoryMethod in order to avoid repeated synchronization. Derived from work around SPR-11034.
2013-10-27 00:55:02 +02:00
Juergen Hoeller 6021822ce4 Polishing 2013-10-26 17:31:43 +02:00
Juergen Hoeller 045f78e6c5 Fixed detection of setter in case of getter with covariant return type narrowing
Issue: SPR-10995
2013-10-26 17:31:24 +02:00
Juergen Hoeller 42568afb37 Factory method type resolution works with indexed and named arguments as well
Issue: SPR-11019
(cherry picked from commit 109faac)
2013-10-26 15:57:54 +02:00
Juergen Hoeller ce001c23f7 Cache InjectionMetadata per bean name instead of per Class, if possible
Issue: SPR-11027
(cherry picked from commit 4675bc4)
2013-10-26 15:56:57 +02:00
Juergen Hoeller 93405fb289 Made processCommonDefinitionAnnotations(AnnotatedBeanDefinition) public
Issue: SPR-11032
(cherry picked from commit 8bd4804)
2013-10-26 15:55:48 +02:00
Juergen Hoeller db056ae0e0 ExpressionState.getConfiguration() should never return null
Issue: SPR-11031
(cherry picked from commit 4aab315)
2013-10-26 15:55:13 +02:00
Juergen Hoeller 35d53af2ca Removed accidentally added temporary files 2013-10-26 15:54:46 +02:00
Sam Brannen 63de2fab4c Assert context uniqueness against merged config
Prior to this commit, the uniqueness check for @ContextConfiguration
attributes within a @ContextHierarchy was performed at a single test
class level instead of against the merged configuration for all test
class levels in the test class hierarchy.

This commit addresses this issue by moving the uniqueness check
algorithm from resolveContextHierarchyAttributes() to
buildContextHierarchyMap() within ContextLoaderUtils.

Issue: SPR-11011
Backport-Commit: 7658d856ca
2013-10-21 20:51:36 +02:00
Phillip Webb 5d8aa635c6 Propagate EvaluationContext from LiteralExpression
Update getValue(EvaluationContext context, Object rootObject,
Class<T> desiredResultType) to propagate the EvaluationContext to
ExpressionUtils.

Issue: SPR-10953
(cherry picked from 686cd79)
2013-10-18 17:57:46 -07:00
Juergen Hoeller ad9049e68a Polishing 2013-10-17 22:19:17 +02:00
Juergen Hoeller b9135788b9 Fixed type prediction for generic factory methods
We're consistently resolving class names now, and the entire algorithm moved from GenericTypeResolver to the internal AutowireUtils helper in the bean factory package.

Issue: SPR-10411
(cherry picked from commit 8b1927f)
2013-10-17 21:46:59 +02:00
Juergen Hoeller b62652aca1 Reduced "getParameterType call not supported" log message to single line
Issue: SPR-10185
(cherry picked from commit 41f041e)
2013-10-17 21:09:11 +02:00
Juergen Hoeller fce02d94b8 Changed JSR-330 detection logging from info to debug
Issue: SPR-10974
(cherry picked from commit 7a9d583)
2013-10-17 21:07:44 +02:00
Phillip Webb 2dca5b6815 Filter source @Configuration with @ComponentScan
Exclude self @Component when parsing @ComponentScan annotations.

Issue: SPR-10531
(cherry picked from d91ffb)
2013-10-10 17:03:36 -07:00
Juergen Hoeller a302c4ac45 Polishing
(cherry picked from commit 52cca48)
2013-10-04 23:31:18 +02:00
Rossen Stoyanchev 507cf8ad14 Make method in RequestMappingHandlerMapping protected
Issue: SPR-10950
2013-10-02 17:50:16 -04:00
Phillip Webb 7500caea52 Fix ArrayStoreException reading subclassed enums
Fix ASM AnnotationAttributesReadingVisitor to correctly deal with
subclasses enums.

Issue: SPR-10914
(cherry picked from 8abe949)
2013-09-27 11:39:11 -07:00
Juergen Hoeller 7994b63c13 Polishing 2013-09-26 21:38:54 +02:00
Juergen Hoeller 26e697b60c Silently swallow IllegalStateException on commit if the JMS transaction is based on a JDBC transaction
This fine-tuned change restores the original Spring 3.0 behavior for Oracle AQ, and also allows for other ConnectionFactory types to comply with the "getDataSource()" pattern.

Issue: SPR-10829
(cherry picked from commit 11d20e3)
2013-09-26 21:25:48 +02:00
Juergen Hoeller ef66708aa6 Fixed BadSqlGrammarException usage in transaction test suite
Issue: SPR-10902
2013-09-25 13:47:15 +02:00
Juergen Hoeller 30b3e24a32 Removed S0 from BadSqlGrammarException codes
S0 doesn't seem to be universally used for SQL grammar problems: see MS SQL Server's RAISERROR. This was reported years back; not sure why the fix didn't actually appear in the codebase back then.

Issue: SPR-10902
(cherry picked from commit d9b6203)
2013-09-25 13:05:06 +02:00
Juergen Hoeller 0a31892475 Fixed handling of @Resource's shareable value
Issue: SPR-10931
(cherry picked from commit 7bc3e3b)
2013-09-25 13:04:50 +02:00
Fedor Bobin 6fb2c72a55 Allow cursor returns from SQL functions
Update CallMetaDataContext to use the metaDataProvider
createDefaultOutParameter method when creating a SqlOutParameter as
a result of a SQL function call.

This change allows Spring to support Oracle cursor types when returned
from functions.

Issue: SPR-10606
(cherry picked from c16b21)
2013-09-24 16:09:09 -07:00
Phillip Webb c82d701a4e Fix typo in MVC reference documentation
Fix ContentNeogitatingViewResolver typo in MVC reference documentation.

Issue: SPR-10927
(cherry picked from 371d25)
2013-09-23 16:05:50 -07:00
Clément Plantier d66cf0c32f Fix "Problem locating method" SpEL error message
Replace "Problem locating method {0} cannot on type {1}" with
"Problem locating method {0} on type {1}".

Issue: SPR-10928
(cherry picked from 824ac5)

Conflicts:
	spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java
2013-09-20 11:50:30 -07:00
Juergen Hoeller 562c323989 EhCacheFactoryBean calls addCache after listener registration
Issue: SPR-10904
(cherry picked from commit a3df311)
2013-09-14 15:41:01 +02:00
Juergen Hoeller 13a68c6060 AbstractBeanFactory removes alreadyCreated entry after bean creation failure
Issue: SPR-10896
(cherry picked from commit e213561)
2013-09-14 15:37:41 +02:00
Juergen Hoeller 6110919842 Polished exception message 2013-09-04 22:11:11 +02:00
Juergen Hoeller c5aa0d12b0 Polishing
Issue: SPR-9495
(cherry picked from commit baa698e)
2013-09-04 22:05:47 +02:00
Juergen Hoeller 6de67cc2df Only cache resolved method when coming from ReflectiveMethodResolver
Issue: SPR-9495
(cherry picked from commit b7ff26a)
2013-09-04 22:00:32 +02:00
Juergen Hoeller b9d726fb84 Fixed EL resolution against static method with changing target class
Issue: SPR-10452
(cherry picked from commit 5e6044c)
2013-09-04 22:00:21 +02:00
Juergen Hoeller 2b57020c18 Polishing
(cherry picked from commit b25876f)
2013-09-04 21:59:37 +02:00
Juergen Hoeller 77298a19c9 Javadoc fixes
(cherry picked from commit facf31f)
2013-09-04 21:52:41 +02:00
Juergen Hoeller 55961544a7 Fixed method count in AbstractMethodMockingControl's IllegalStateException message
Issue: SPR-10885
2013-09-02 17:09:29 +02:00
Juergen Hoeller 5639aa7064 Properly handle getMethodDescriptors() null value on IBM JVM
Also internally renaming SimpleNonIndexedPropertyDescriptor to SimplePropertyDescriptor and preferring direct field access wherever possible.

Issue: SPR-10862
2013-09-02 16:42:06 +02:00
Juergen Hoeller 0c30618ae8 Fixed getCachedExecutor race condition in MethodReference
This commit includes a full backport of the changes in SPR-10657, instead of just the initial commit which the original backport request referred to.

Issue: SPR-10884
Issue: SPR-10657
2013-09-02 15:33:15 +02:00
Juergen Hoeller 9317d8f43e Polishing
(cherry picked from commit caa1218)
2013-08-29 19:21:24 +02:00
Juergen Hoeller 692908b722 Javadoc fixes
(cherry picked from commit 465e629)
2013-08-29 19:20:54 +02:00
Grzegorz Rożniecki c890d1e69d Fix malformed code in documentation
Fixed code snippets in "Handling complex types for stored procedure
calls" paragraph.

Issue: SPR-10798
(cherry picked from commit 00c744b)
2013-08-28 14:28:21 -07:00
clemp6r 4c7cb858d2 Fix elvis operator mistake in reference docs
In the given example the expression using the Elvis operator will
return "Nikola Tesla" and not "Mike Tesla".

Issue: SPR-10850
(cherry picked from commit 84242f1)
2013-08-28 14:17:34 -07:00
Juergen Hoeller 5a49d657d8 AnnotationConfigWebApplicationContext allows for repeated register/scan calls
Restoring consistency with AnnotationConfigApplicationContext.

Issue: SPR-10852
(cherry picked from commit 01b8d93)
2013-08-28 00:24:22 +02:00
Juergen Hoeller 8a1ae53166 Consistent use of Class<?>
(cherry picked from commit 091712d)
2013-08-28 00:23:58 +02:00
Juergen Hoeller 0765a05434 Marked @Profile as @Documented
(cherry picked from commit e812b6c)
2013-08-28 00:20:52 +02:00
Rossen Stoyanchev 072e5e8471 Revise use of FilterChain in MockMvc
MockFilterChain should not be re-used across requests. This is not an
issue unless tests are executed concurrently. This change ensures the
MockFilterChain is re-created for each request in MockMvc.

Issue: SPR-10838
2013-08-27 11:45:46 -04:00
Gabriel Axel a17912d19e Fix NPE in ResourceHttpMessageConverter
ResourceHttpMessageConverter tries to use the filename to determine the
media type, but for Resource implementations such as ByteArrayResource
it is null, which causes NullPointerException. The fix checks whether
getFilename returns null before attempting to determine the media type
by it.

Issue: SPR-10848
2013-08-22 16:14:03 -04:00
Juergen Hoeller 610e7796d9 GenericTypeResolver properly handles bound parameterized type
Issue: SPR-10819
(cherry picked from commit ea6525f)
2013-08-20 19:41:00 +02:00
Arjen Poutsma 8efac21c2b Support 'empty' StreamSource in Jaxb2Marshaller
Added support for StreamSources that do not have a InputStream or
Reader, but do have a System ID.

Issue: 10828
2013-08-13 18:16:42 -04:00
Lonre Wang 40c7303702 Fix typo in reference docs
(cherry picked from 5549bd90)
2013-08-12 14:26:08 -07:00
Arjen Poutsma c00782a3e8 Added reference to CatchAllConverter
Added reference to CatchAllConverter in both javadoc and reference docs.

Issue: SPR-10821
2013-08-12 12:32:23 +02:00
Arjen Poutsma 96f5314df6 Added XStream CatchAllConverter
Added XStream CatchAllConverter that supports all classes, but throws
exceptions for (un)marshalling.

Main purpose of this class is to register this converter as a catchall
last converter with a normal or higher priority in addition to
converters that explicitly  support the domain classes that should be
supported. As a result, default XStream  converters with lower
priorities and possible security vulnerabilities do not get invoked.

Issue: SPR-10821
2013-08-09 09:38:51 -04:00
Rossen Stoyanchev 2b628a1a0d Fix bug that ignored custom json prefix
Issue: SPR-10817
2013-08-08 09:39:08 -04:00
Spring Buildmaster 349143864c Increment version to 3.2.5.BUILD-SNAPSHOT 2013-08-06 16:15:25 -07:00
Spring Buildmaster fc73f6bb2c Release version 3.2.4.RELEASE 2013-08-06 16:00:04 -07:00
Phillip Webb 119364f326 Further 3.2.4 changelog entries 2013-08-06 15:11:43 -07:00
Arjen Poutsma 434735fbf6 Fix potential security risk when using Spring OXM
Disable by default external entity resolution when using Spring OXM
with jaxb. This prevents a XML entity from being able to resolve a
local file on the host system.

See:
https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing

Issue: SPR-10806
(cherry picked from commit 7576274)
2013-08-06 15:10:05 -07:00
Grzegorz Rożniecki 8f4221deb3 Fix SimpleJdbcCall and SimpleJdbcInsert Javadoc
Change "fluid" interface to "fluent" and "string" to "chain", plus fix
few typos in Javadoc.

Issue: SPR-10792
(cherry picked from commit 2344412)
2013-08-06 14:44:01 -07:00
Juergen Hoeller 2d646ca79e Polishing 2013-08-06 01:58:46 +02:00
Juergen Hoeller 1245919331 Polishing 2013-08-06 01:15:17 +02:00
Juergen Hoeller bcbd338fe2 Further 3.2.4 changelog entries 2013-08-06 00:32:59 +02:00
Juergen Hoeller 945463b826 Added javadoc note on the use of CachingConnectionFactory
Issue: SPR-10581
(cherry picked from commit 2626a35)
2013-08-06 00:31:18 +02:00
Juergen Hoeller 3ba3dd61d1 Consistent use of varargs; deprecated setAnnotatedClass method
Issue: SPR-10421
2013-08-06 00:31:06 +02:00
Juergen Hoeller cd523c7051 Restored overridable getXStream() method in Spring Framework 3.2.x
Also setting the bean class loader on the XStream instance itself now.

Issue: SPR-10421
2013-08-05 23:05:15 +02:00
Juergen Hoeller 20ddd3254b DelegatingFilterProxy avoids synchronization for pre-resolved delegate
Issue: SPR-10413
(cherry picked from commit c26272c)
2013-08-05 22:24:58 +02:00
Juergen Hoeller e30b842316 Fixed ClassFilterAwareUnionMethodMatcher equals implementation
Issue: SPR-10604
(cherry picked from commit f329140)
2013-08-05 22:23:10 +02:00
Rossen Stoyanchev b0675c031e Make date methods in HttpHeaders public
Issue: SPR-10713
2013-08-05 12:46:33 -04:00
Rossen Stoyanchev 0f71da5be6 Return -1 after parse error for Expires header
Issue: SPR-10648
2013-08-05 12:38:53 -04:00
Juergen Hoeller 9035a97e18 Refined javadoc for WebLogic/WebSphere transaction setup
Issue: SPR-10777
2013-08-01 17:24:24 +02:00
Juergen Hoeller d32a77a5f0 Optimized TomcatInstrumentableClassLoader implementation
Issue: SPR-10788
2013-08-01 15:49:58 +02:00
Juergen Hoeller d8e3ef7640 Polishing
Issue: SPR-10752
Issue: SPR-10627
2013-08-01 00:35:51 +02:00
Juergen Hoeller ccb02a2355 Updated changelog for 3.2.4 2013-07-31 23:42:36 +02:00
Juergen Hoeller d0948f1f03 Fixed misunderstanding with respect to excludeUnlistedClasses default in JPA 2.0
Issue: SPR-10767
2013-07-31 23:40:59 +02:00
Juergen Hoeller ae0f23e942 Polishing
Issue: SPR-10752
Issue: SPR-10627
2013-07-31 22:42:27 +02:00
Juergen Hoeller b40263e06b PropertySourcesPlaceholderConfigurer's "ignoreUnresolvablePlaceholders" setting reliably applies to nested placeholders as well
Issue: SPR-10549
(cherry picked from commit 127b91f)
2013-07-31 18:38:31 +02:00
Juergen Hoeller 2f8dfb3e52 Updated Quartz version range in javadoc
Issue: SPR-10775
2013-07-30 23:27:34 +02:00
Juergen Hoeller d5c78c953d Remove the JobDetail from the job data map after discovering it
Issue: SPR-10775
2013-07-30 23:27:33 +02:00
Juergen Hoeller dfac1d0510 Added "requestsRecovery" bean property to JobDetailFactoryBean
Issue: SPR-10775
2013-07-30 23:27:32 +02:00
Juergen Hoeller 14b83c3fcc Polishing
Issue: SPR-10609
2013-07-30 23:27:30 +02:00
Phillip Webb ae6e84bc7d Fix TestNG build settings 2013-07-30 14:21:28 -07:00
Phillip Webb 4213a31a7d Fix failing test due to CUSTOM_COMPILATION Group 2013-07-30 13:54:42 -07:00
Phillip Webb 22454c8edf Upgradle to gradle 1.6 2013-07-30 13:29:05 -07:00
Deline Neo 98da5a7b2b Add attributeDoesNotExist ModelResultMatcher
Issue: SPR-10509
2013-07-30 15:41:55 -04:00
Phillip Webb 0a37552beb Add CUSTOM_COMPILATION TestGroup 2013-07-30 11:54:22 -07:00
Phillip Webb 1b702fa6a2 Revert "Add test source dependency in attempt to fix CI"
This reverts commit a2e438b2be.
2013-07-30 11:53:34 -07:00
Phillip Webb a2e438b2be Add test source dependency in attempt to fix CI 2013-07-30 11:33:30 -07:00
Phillip Webb 9ff9d45283 Minor polish to gradle build 2013-07-30 10:33:52 -07:00
Phillip Webb 1fee4c0c0b Upgradle to gradle 1.6 2013-07-30 10:33:03 -07:00
Rossen Stoyanchev dc87a2211c Make HTTP scheme parsing case-insensitive
Issue: SPR-10779
2013-07-30 12:33:22 -04:00
Phillip Webb 77b7344530 Upgradle to gradle 1.6 2013-07-30 09:29:44 -07:00
Phillip Webb 7cde4f1503 Update gradle plugin versions 2013-07-30 09:22:08 -07:00
Rossen Stoyanchev 6c11fd3b5e Fix issue with parsing x-forwarded-host header
Issue: SPR-10701
2013-07-30 11:48:06 -04:00
Biju Kunjummen eec4a37d1c Fix Jaxb2TypeScanner to scan for @XmlRegistry
Update ClassPathJaxb2TypeScanner to scan for @XmlRegistry classes.

Prior to this commit explicitly configured @XmlRegistry annotated classes
were not registered with the JAXBContext when using the 'packagesToScan'
property of the Jaxb2Unmarshaller.

Issue: SPR-10714
(cherry picked from 4f871d4)
2013-07-29 11:26:52 -07:00
Phillip Webb 1e3dc4aeae Include argument types in MethodReference cache
Update the cached MethodExecutor in MethodReference to include the
method argument types. Prevents the incorrect use of the cache when the
SpEL expression refers to a class that has overloaded methods.

Issue: SPR-10657
(cherry picked from commit f9b12b8)
2013-07-25 11:28:03 +02:00
Phillip Webb d4b54d834e Skip abstract beans in getBeansWithAnnotation()
Update BeanFactory.getBeansWithAnnotation() to skip any abstract
bean definitions.

Issue: SPR-10663
(cherry picked from commit 8ed8ee2)
2013-07-25 11:27:57 +02:00
Juergen Hoeller 3562cd759b Moved executor null check to AsyncExecutionInterceptor, allowing AbstractAsyncExecutionAspect to fall back to sync execution (as in 3.2.1)
Issue: SPR-10636
(cherry picked from commit b6c54c3)
2013-07-25 10:45:59 +02:00
Juergen Hoeller c1dc4e2c75 Resource-based PlatformTransactionManager implementations defensively catch Throwable in doBegin in order to reliably close resource in case of OutOfMemoryError
Issue: SPR-10755
(cherry picked from commit 2e4eb9f)
2013-07-25 10:45:58 +02:00
Phillip Webb 8feeec4a60 Fix ResourceHttpRequestHandler empty location log
Fix ResourceHttpRequestHandler to only log warning when locations
is empty.

Issue: SPR-10780
(cherry picked from bc52469)
2013-07-24 16:52:47 -07:00
Phillip Webb 440a63a3d0 Make TypeDescriptor serializable
Issue: SPR-10631
(cherry picked from 9eb596a)
2013-07-23 14:19:15 -07:00
Phillip Webb 274ed1fe67 Remove unnecessary spring-instrument dependencies
Remove unnecessary spring-core dependency from spring-instrument.

Issue: SPR-10732
(cherry picked from 9939c48)
2013-07-23 13:37:43 -07:00
Phillip Webb 1b47f6c0b0 Add Java 7 instrumentation manifest attributes
Update META-INF/MANIFEST.MF for spring-instrument to include
necessary attributes for running under Java 7:

    Can-Redefine-Classes : true
    Can-Retransform-Classes: true
    Can-Set-Native-Method-Prefix : false

(see http://docs.oracle.com/javase/7/docs/api/java/lang/instrument/
package-summary.html)

Prior to this commit `InstrumentationSavingAgent.getInstrumentation().
addTransformer(t, true);` would fail under Java 7.

Issue: SPR-10731
(Cherry picked from 50333ca)
2013-07-23 13:37:09 -07:00
Phillip Webb 66e4c9b2bb Fix case of rollbackForClassName in ref docs
Update @Transactional reference documentation to fix the case of the
rollbackForClassName and noRollbackForClassName attributes.

Issue: SPR-10754
(cherry picked from 1204d2a)
2013-07-22 15:48:27 -07:00
Nick Williams e608055021 Parse JPA <exclude-unlisted-classes> correctly
Fix PersistenceUnitReader to correctly read <exclude-unlisted-classes>
in both JPA 1.0 and 2.0 persistence.xml files.

Prior to this commit PersistenceUnitReader would set the value of
excludeUnlistedClasses to true when a <exclude-unlisted-classes> element
was present, regardless of its value.

The following rules are now used when parsing:

- If the <exclude-unlisted-classes> element is missing the appropriate
  default value is set (based on the JPA version).

- If an empty <exclude-unlisted-classes/> element is found the
  excludeUnlistedClasses property is set to true.

- Otherwise the value of the <exclude-unlisted-classes> element is used
  to set the excludeUnlistedClasses property.

Issue: SPR-10767
(cherry picked from 7ad540d)

Conflicts:
	spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/PersistenceUnitReader.java
2013-07-22 12:38:46 -07:00
Phillip Webb 199be0ad3c Add spring-core cglib dependency 2013-07-19 14:52:54 -07:00
Phillip Webb af7753eac6 Add MemorySafeUndeclaredThrowableStrategy
Port the latest unreleased UndeclaredThrowableStrategy implementation
from cglib to fix a memory-leak present in v3.

Issue: SPR-10709
(cherry picked from 587877c)
2013-07-19 14:39:24 -07:00
Rossen Stoyanchev cce74b8ba2 Make the JSON prefix used in converters configurable
Issue: SPR-10627
2013-07-19 17:23:33 -04:00
Rossen Stoyanchev dd7508d606 Replace assert w/ warning in ResourceHttpRequestHandl.
Issue: SPR-9186
2013-07-19 17:23:33 -04:00
Rossen Stoyanchev 3bf56dda56 Fix issue in MappingJacksonJsonView
Issue: SPR-10752
2013-07-19 17:23:33 -04:00
Phillip Webb a72209aad3 Make ConfClassPostPro Ordered.HIGHEST_PRECEDENCE
Make ConfigurationClassPostProcessor Ordered.HIGHEST_PRECEDENCE in
case several BeanDefinitionRegistryPostProcessors are registered.

Issue: SPR-10645
(cherry picked from 350cb9b)
2013-07-19 14:16:39 -07:00
Phillip Webb 67f41b15eb Resolve ${} placeholders in @ImportResource
Update ConfigurationClassParser to resolve any ${} placeholders from
@ImportResource values.

Issue: SPR-10686
(cherry picked from commit 700a08e)
2013-07-19 13:14:05 -07:00
Arjen Poutsma 3515acfa3d Added 'processExternalEntities' to JAXB2Marshaller
Added 'processExternalEntities' property to the JAXB2Marshaller, which
indicates whether external XML entities are processed when
unmarshalling.

Default is false, meaning that external entities are not resolved.
Processing of external entities will only be enabled/disabled when the
Source} passed to #unmarshal(Source) is a SAXSource or StreamSource. It
has no effect for DOMSource or StAXSource instances.
2013-07-19 11:07:07 -04:00
Rossen Stoyanchev 7fdd0c22b2 Fix issue in ContentNeogitatingViewResolver
The following commit in 3.2.3 had a side effect on CNVR:
https://github.com/SpringSource/spring-framework/commit/aaded7e30b06cc70a354856a2288acd04045b673

It appears that CNVR doesn't treat a request for "*/*" differently
from a request that does not request content types. Both should be
interpreted as "any content type is acceptable". This fix ensures
that CNVR treats these two cases the same way.

Issue: SPR-10683
2013-07-18 22:03:13 -04:00
Rossen Stoyanchev a0f8a894f2 Cache constructor instance in WebAsyncUtils
Issue: SPR-10673
2013-07-18 17:48:18 -04:00
Phillip Webb 55f02db18e 'Rename' HttpHeaders.getIfNotModifiedSince()
Effectively rename HttpHeaders.getIfNotModifiedSince() to
getIfModifiedSince() by adding a new method and deprecating the
old one.

Issue: SPR-10600
2013-06-26 10:45:33 -07:00
Phillip Webb 71e88cbf30 Fix SQL syntax error in jdbcTemplate update docs
Update the reference guide jdbcTemplate update example to include the
column name.

Issue: SPR-10625
2013-06-25 22:22:36 -07:00
Phillip Webb 514a2e9568 Package RmiInvocationWrapperRTD.xml in jar
Ensure RmiInvocationWrapperRTD.xml is packaged inside spring-context.jar

Issue: SPR-10649

Conflicts:
	build.gradle
2013-06-23 23:52:05 -07:00
Rossen Stoyanchev abfb439bfe Reverse SPR-10402 change that caused 3.2.3 regression
SPR-10402 in Spring Framework 3.2.3 treated empty request parameter
values as missing values, if the empty value was turned into a null
by a PropertyEditor or Converter. This caused the regression.

Issue: SPR-10578, SPR-10402, SPR-10584
2013-06-20 16:25:30 -04:00
Phillip Webb 4ba43b4737 Support meta @Component with non-string value
Update AnnotationBeanNameGenerator to only use the value attribute of
a @Component meta-annotated annotation when it is a String.

Issue: SPR-10580
2013-06-04 10:24:14 -07:00
Spring Buildmaster 2655657a51 2013-05-20 10:18:53 -07:00
Rob Winch 404671416a Update BuildTests to verify JDK 1.6
Issue: SPR-10569
2013-05-20 10:37:33 -05:00
Phillip Webb 260eafc819 Enforce JDK version on CI server
Add a test to ensure that the CI server does not accidentally build
with the wrong JDK version.

Issue: SPR-10569
2013-05-16 17:14:03 -07:00
Juergen Hoeller ef8b5763e6 Final addition to 3.2.3 changelog 2013-05-16 19:31:57 +02:00
Juergen Hoeller 6b89af824d Consistent detection of Order annotation in superclasses and interfaces
Issue: SPR-10514
(cherry picked from commit 814d24e)
2013-05-16 18:07:00 +02:00
Juergen Hoeller 86b4b2ef23 Consistent use of Class<?> in Assert
(cherry picked from commit 16548d2)
2013-05-16 16:11:30 +02:00
Juergen Hoeller faa8430fd7 Avoid NPE in ContextLoader when dealing with an untyped ApplicationContextInitializer
Issue: SPR-10449
(cherry picked from commit a19c976)
2013-05-16 16:11:25 +02:00
Juergen Hoeller 76f870caca Final preparations for 3.2.3 release 2013-05-16 14:50:03 +02:00
Juergen Hoeller 615315728b Fixed BeanPropertyRowMapper to only prefix actual upper-case letters with underscores
Issue: SPR-10547
(cherry picked from commit c8b071c)
2013-05-16 14:34:44 +02:00
Juergen Hoeller 115442242f Replaced Map synchronization with ConcurrentHashMap to avoid session access deadlocks
Issue: SPR-10436
(cherry picked from commit cd3d0c3)
2013-05-16 14:34:38 +02:00
Juergen Hoeller 10c0e8af01 Final changelog entries for 3.2.3 2013-05-16 00:17:00 +02:00
Juergen Hoeller b6e20a4cc9 Introduced "jsonPrefix" bean property
This change involves a modification of the "writeContent" template method to include the "jsonPrefix" String instead of the "prefixJson" boolean flag. Since said template method has only been introduced in 3.2.2, this change should hopefully not be a problem.

Issue: SPR-10567
(cherry picked from commit 28164b4)
2013-05-16 00:02:31 +02:00
Juergen Hoeller a8adec7673 Avoid unnecessary creation of default StandardEnvironment instances
Issue: SPR-10568
(cherry picked from commit 7e01578)
2013-05-15 23:59:29 +02:00
Juergen Hoeller 2ae61c503e Further changelog entries for 3.2.3 2013-05-15 17:04:34 +02:00
Juergen Hoeller c2b2f30669 Polishing 2013-05-15 16:57:57 +02:00
Juergen Hoeller 239a7cd85f Fixed type detection to avoid reuse of parent bean's targetType on child definition merge
Issue: SPR-10374
(cherry picked from commit 070103b)
2013-05-15 16:42:28 +02:00
Juergen Hoeller 6e4317ebfb Properly handle knownSuperclasses in case of an overridden ConfigurationClass
Issue: SPR-10546
2013-05-15 13:47:03 +02:00
Juergen Hoeller 325d8834e3 Polishing 2013-05-15 11:34:58 +02:00
Juergen Hoeller 0c76cb7e80 Clarified that CronTrigger never schedules overlapping executions
Issue: SPR-10556
(cherry picked from commit 5dbbd8f)
2013-05-15 11:34:57 +02:00
Rob Winch 940011e233 Ensure Parent ConfigurationClass loaded on overrides
Previously ConfigurationClassParser could override a nested
@Configuration without consideration of @Bean's defined in parent
classes.

This commit ensures that if the original ConfigurationClass contains
additional bean definitions it is processed again.

Issue: SPR-10546
2013-05-15 11:34:56 +02:00
Rossen Stoyanchev 92bbd8103b Make removal of jsessionid case insensitive
Issue: SPR-10398
2013-05-14 22:07:52 -04:00
Rossen Stoyanchev 9c194699c7 Reinstate removal of semicolon content
Commit 5b1165 was an attempt to leave semicolon content in the URL path
while ignoring it for request mapping purposes. However, it becomes
quite difficult to manage and semicolon content should not always be
ignored (sometimes a semicolon is used as a separator of multiple items
in a path segment, rather than for matrix variables).

This change effectively reverts back to the original approach in 3.2
where a flag on AbstractHandlerMapping can be used to have semicolon
content removed or kept. If kept, path segments with matrix variables
must be represented with a path segment.

The main difference is that by default it is removed everywhere
including the MVC namespace and Java config.

Issue: SPR-10427, SPR-10234
2013-05-14 21:30:59 -04:00
Rossen Stoyanchev 25701ef984 Fix deadlock issue in DeferredResult
Previously DeferredResult locked around the setting of the result
including handling up to the part of submitting a dispatch. This
can cause a deadlock if a timeout occurs at the same time since
the Tomcat timeout thread has its own locking that permits only
one thread to do timeout or dispatch processing.

The fix reduces the locking to cover only the attempt to set the
DeferredResult but not the dispatching.

Issue: SPR-10485
2013-05-14 17:06:00 -04:00
Juergen Hoeller 87ef99df5e Updated changelog for 3.2.3 release 2013-05-14 18:50:39 +02:00
Juergen Hoeller 13ca2752ae Consistent use of Class<?> in core container method signatures
(cherry picked from commit 1461744)
2013-05-14 18:06:12 +02:00
Juergen Hoeller ef363b0f7a Detect a FactoryBean type match even if predictBeanType returned a non-FactoryBean type
This change should be the final piece in the puzzle to let SmartInstantiationAwareBeanPostProcessor's predictBeanType predict a FactoryBean-produced type, effectively as a semantic alternative to its postProcessBeforeInstantiation-related role.

Issue: SPR-10517
(cherry picked from commit bc3e585)
2013-05-14 18:02:49 +02:00
Juergen Hoeller f9db59e730 GenericTypeResolver defensively calls Class.getGenericSuperclass() and consistently uses Class<?>
Issue: SPR-10559
(cherry picked from commit 25e29b8)
2013-05-14 18:01:41 +02:00
Rob Winch 0dcca2304f Remove xdoclint:none for < JDK 8 2013-05-13 11:54:58 -05:00
Rossen Stoyanchev ad272a0c87 Close InputStream in ResourceHttpMessageConverter
Spring 3.2.2 introduced a change to avoid closing the response stream
in HttpMessageConverters (SPR-10095). However, the InputStream of
resources being written, for example as part of a multi-part request
should be closed. This change ensures that.

Issue: SPR-10460
2013-05-10 17:31:48 -04:00
Juergen Hoeller be224e1f30 ServletContextResourcePatternResolver uses encoded jar file location for UrlResource
Adding overloaded constructors for URI specification to UrlResource, as a convenience.

Issue: SPR-10471
(cherry picked from commit 1f0f46f)
2013-05-10 22:13:17 +02:00
Rossen Stoyanchev 4bb0916202 Delay check if pattern ends with slash
This is a minor fix with no actual impact.

Issue: SPR-10504
2013-05-10 12:51:48 -04:00
Rossen Stoyanchev d3eda09c01 Allow treating empty @RequestParam as missing value
If type conversion turns an empty request parameter value (i.e. "") to
null, we should treat it as a missing value. By default the
ConversionService doesn't change empty strings and therefore one must
explicitly convert them to null for example by registering a
StringTrimmerEditor with emptyAsNull=true.

Issue: SPR-10402
2013-05-10 12:51:31 -04:00
Rossen Stoyanchev 399f887128 Add servletRelativeAction form tag attribute
A recent change in FormTag to prepend the context and servlet paths if
not present, causes issues when used in portlet applications.
This change introduces a servletRelativeAction form tag attribute that
must be used for the context and servlet paths to be prepended.

Issue: SPR-10382
2013-05-10 12:51:20 -04:00
Rossen Stoyanchev 4d005b6987 Improve default content type selection
Previously ContentNegotiationManager continued with the next
ContentNegotiationStrategy only if the current one returned an empty
list. Now it also does that if the current ContentNegotiationStrategy
returns "*/*". Since the absence of an Accept header and "*/*" have
the same meaning, this allows a default content type to be used in
either case.

Issue: SPR-10513
2013-05-10 12:51:08 -04:00
Rossen Stoyanchev 532a7a3a02 Invoke global, then local @InitBinder/@ModelAttribute
@InitBinder and @ModelAttribute methods in @ControllerAdvice classes
are now invoked first, allowing any such methods in the @Controller
class to override them.

Issue: SPR-10419
2013-05-10 12:50:24 -04:00
Juergen Hoeller b2d416fd5f Revised ReflectiveMethodResolver to properly handle any kind of List returned from a MethodFilter
Issue: SPR-10392
(cherry picked from commit ad88650)
2013-05-07 22:07:29 +02:00
Juergen Hoeller 7298c3d669 Fixed MockMvc example code
Issue: SPR-10389
(cherry picked from commit 35847ad)
2013-05-07 22:06:51 +02:00
Juergen Hoeller 7bbd38b9e0 Fixed javadoc warnings
Issue: SPR-10373
(cherry picked from commit 1ca943c)
2013-05-07 22:06:33 +02:00
Juergen Hoeller 64d83a550e MappingJackson(2)MessageConverter needs to use bean ClassLoader 2013-05-07 18:22:48 +02:00
Juergen Hoeller 0df1630ab9 Jaxb2Marshaller doesn't need to depend on ResourceLoaderAware
Issue: SPR-10512
(cherry picked from commit 255eab5)
2013-05-03 17:48:12 +02:00
Juergen Hoeller 7cbc115da6 Added "postProcessConfiguration" template method to LocalValidatorFactoryBean; clarified limited compatibility with Hibernate Validator 5.0 2013-05-03 15:19:28 +02:00
Juergen Hoeller b962239e11 Polishing 2013-05-03 13:47:54 +02:00
Juergen Hoeller 872cf6b076 Removed unnecessary HttpUrlTemplate subclass of UriTemplate 2013-05-03 13:38:31 +02:00
Juergen Hoeller b2bd319d47 Polished UriComponents implementation
(cherry picked from commit c3b624d)
2013-05-03 13:23:48 +02:00
Juergen Hoeller e3fa49063e Added note on non-thread-safety of Jackson's DateFormat support 2013-05-02 16:06:51 +02:00
Juergen Hoeller c302efdbbf Further build file updates for alignment with 4.0 build 2013-05-01 22:38:43 +02:00
Juergen Hoeller 12699696f4 Added note on OpenJDK 8 compatibility 2013-05-01 21:45:50 +02:00
Juergen Hoeller c4f2566070 Allow for building and testing the 3.2.x line against OpenJDK 8 2013-05-01 21:39:51 +02:00
Juergen Hoeller 6458643f15 Updated 3.2.3 changelog 2013-04-30 23:57:15 +02:00
Juergen Hoeller 2145545e69 Ignore failing TopLink tests (not worth messing with since TopLink is deprecated anyway) 2013-04-30 23:29:07 +02:00
Juergen Hoeller f6545cde38 Polishing 2013-04-30 23:29:06 +02:00
Juergen Hoeller 601c64a65c Removed broken applyQueryTimeout implementation (following a fix in 4.0) 2013-04-30 23:29:06 +02:00
Juergen Hoeller 964f1a6213 Refined HibernateJpaDialect for Hibernate 4.2 support
HibernateJpaDialect's HibernateConnectionHandle does not call close() for Hibernate 4.x anymore, since on 4.2, the exposed Connection handle isn't a "borrowed connection" wrapper but rather the actual underlying Connection - with a close() call immediately returning the Connection to the pool and making the local handle invalid for further use within the transaction.

Issue: SPR-10395
2013-04-30 23:29:05 +02:00
Juergen Hoeller 886cc2cce8 Fixed general test suite problems found during OpenJDK 8 upgrade
Issue: SPR-9639
(cherry picked from commit 19f783b)
2013-04-30 23:29:04 +02:00
Juergen Hoeller 5bdd2d245c Consistent Map/Set ordering
Use LinkedHashMaps/Sets wherever exposed to users, and code tests defensively in terms of expected Map/Set ordering. Otherwise, there'll be runtime order differences between JDK 7 and JDK 8 due to internal HashMap/Set implementation differences.

Issue: SPR-9639
(cherry picked from commit 9c09a0a)
2013-04-30 23:29:03 +02:00
Juergen Hoeller d0513f7521 Revised Joda-Time support (as a side effect of JSR-310 support in Spring 4.0)
Issue: SPR-9641
(cherry picked from commit b5d44e1)
2013-04-30 23:29:02 +02:00
Juergen Hoeller 265c0c1505 Minimized ASM usage
In particular, avoid accidental usage of ASM for core JDK types - which will fail in case of a new bytecode version in the JDK, even if the application itself has been compiled with an earlier bytecode target.

Issue: SPR-10292
(cherry picked from commit d3a4068)
2013-04-30 23:29:02 +02:00
Juergen Hoeller 886cf825cb Added "getName()" accessor to MethodReference
Issue: SPR-10422
(cherry picked from commit 5ff2653)
2013-04-30 23:29:01 +02:00
Juergen Hoeller 03033f6c06 Fixed typo in code example
Issue: SPR-10394
(cherry picked from commit f374b7a)
2013-04-30 23:29:00 +02:00
Juergen Hoeller 5ff2dfbe13 Removed references to deprecated queryForInt method from documentation
Issue: SPR-10257
(cherry picked from commit 6be954e)
2013-04-30 23:28:59 +02:00
Juergen Hoeller fa44ab6336 Removed unnecessary CGLIB 2.2 dependency from spring-test-mvc module
(cherry picked from commit 657bd80)
2013-04-30 23:28:58 +02:00
Juergen Hoeller eed1a58b81 Fixed annotation attribute processing for enum arrays
Based on https://github.com/SpringSource/spring-framework/pull/263
(cherry picked from commit 06fdfb0)
2013-04-30 23:28:57 +02:00
11106 changed files with 476593 additions and 1014148 deletions
-10
View File
@@ -1,10 +0,0 @@
root = true
[*.{adoc,bat,groovy,html,java,js,jsp,kt,kts,md,properties,py,rb,sh,sql,svg,txt,xml,xsd}]
charset = utf-8
[*.{groovy,java,kt,kts,xml,xsd}]
indent_style = tab
indent_size = 4
continuation_indent_size = 8
end_of_line = lf
-16
View File
@@ -1,16 +0,0 @@
# Normalize line endings to LF.
* text eol=lf
# Ensure that line endings for multipart files in spring-web are not modified.
*.multipart -text
# Ensure that line endings for DOS batch files are not modified.
*.bat -text
# Ensure the following are treated as binary.
*.gif binary
*.jar binary
*.jpeg binary
*.jpg binary
*.png binary
*.vsd binary
-17
View File
@@ -1,17 +0,0 @@
<!--
!!! For Security Vulnerabilities, please go to https://spring.io/security-policy !!!
-->
**Affects:** \<Spring Framework version>
---
<!--
Thanks for taking the time to create an issue. Please read the following:
- Questions should be asked on Stack Overflow.
- For bugs, specify affected versions and explain what you are trying to do.
- For enhancements, provide context and describe the problem.
Issue or Pull Request? Create only one, not both. GitHub treats them as the same.
If unsure, start with an issue, and if you submit a pull request later, the
issue will be closed as superseded.
-->
-32
View File
@@ -1,32 +0,0 @@
name: Backport Bot
on:
issues:
types: [labeled]
pull_request:
types: [labeled]
push:
branches:
- '*.x'
permissions:
contents: read
jobs:
build:
permissions:
contents: read
issues: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Download BackportBot
run: wget https://github.com/spring-io/backport-bot/releases/download/latest/backport-bot-0.0.1-SNAPSHOT.jar
- name: Backport
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_EVENT: ${{ toJSON(github.event) }}
run: java -jar backport-bot-0.0.1-SNAPSHOT.jar --github.accessToken="$GITHUB_TOKEN" --github.event_name "$GITHUB_EVENT_NAME" --github.event "$GITHUB_EVENT"
@@ -1,13 +0,0 @@
name: "Validate Gradle Wrapper"
on: [push, pull_request]
permissions:
contents: read
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1
+7 -24
View File
@@ -1,52 +1,35 @@
# Miscellaneous
*.java.hsp
*.sonarj
*.sw*
.DS_Store
.settings
.springBeans
bin
build.sh
integration-repo
ivy-cache
argfile*
activemq-data/
classes/
# Log files
jxl.log
jmx.log
derby.log
# Gradle artifacts
spring-test/test-output/
.gradle
.gradletasknamecache
argfile*
pom.xml
/build
buildSrc/build
/spring-*/build
/framework-bom/build
/framework-docs/build
/integration-tests/build
/src/asciidoc/build
spring-test/test-output/
# Maven artifacts
pom.xml
target/
# Eclipse artifacts, including WTP generated manifests
bin
.classpath
.project
.settings
.springBeans
spring-*/src/main/java/META-INF/MANIFEST.MF
# IDEA artifacts and output dirs
*.iml
*.ipr
*.iws
.idea
out
test-output
atlassian-ide-plugin.xml
# VS Code
.vscode/
-34
View File
@@ -1,34 +0,0 @@
Juergen Hoeller <jhoeller@vmware.com>
Juergen Hoeller <jhoeller@vmware.com> <jhoeller@pivotal.io>
Juergen Hoeller <jhoeller@vmware.com> <jhoeller@gopivotal.com>
Rossen Stoyanchev <rstoyanchev@vmware.com>
Rossen Stoyanchev <rstoyanchev@vmware.com> <rstoyanchev@pivotal.io>
Rossen Stoyanchev <rstoyanchev@vmware.com> <rstoyanchev@gopivotal.com>
Phillip Webb <pwebb@vmware.com>
Phillip Webb <pwebb@vmware.com> <pwebb@pivotal.io>
Phillip Webb <pwebb@vmware.com> <pwebb@gopivotal.com>
Chris Beams <cbeams@vmware.com>
Chris Beams <cbeams@vmware.com> <cbeams@pivotal.io>
Chris Beams <cbeams@vmware.com> <cbeams@gopivotal.com>
Arjen Poutsma <poutsmaa@vmware.com>
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@pivotal.io>
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@gopivotal.com>
Arjen Poutsma <poutsmaa@vmware.com> <poutsma@mac.com>
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@vmware.com>
Oliver Drotbohm <odrotbohm@vmware.com>
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@vmware.com>
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@pivotal.io>
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@gopivotal.com>
Dave Syer <dsyer@vmware.com>
Dave Syer <dsyer@vmware.com> <dsyer@pivotal.io>
Dave Syer <dsyer@vmware.com> <dsyer@gopivotal.com>
Dave Syer <dsyer@vmware.com> <david_syer@hotmail.com>
Andy Clement <aclement@vmware.com>
Andy Clement <aclement@vmware.com> <aclement@pivotal.io>
Andy Clement <aclement@vmware.com> <aclement@gopivotal.com>
Andy Clement <aclement@vmware.com> <andrew.clement@gmail.com>
Sam Brannen <sbrannen@vmware.com>
Sam Brannen <sbrannen@vmware.com> <sbrannen@pivotal.io>
Sam Brannen <sbrannen@vmware.com> <sam@sambrannen.com>
<dmitry.katsubo@gmail.com> <dmitry.katsubo@gmai.com>
Nick Williams <nicholas@nicholaswilliams.net>
-3
View File
@@ -1,3 +0,0 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=17.0.5-librca
@@ -0,0 +1,9 @@
#com.springsource.sts.gradle.core.preferences.GradleImportPreferences
#Thu Aug 9 11:34:43 CEST 2012
enableAfterTasks=true
afterTasks=afterEclipseImport;
enableDependendencyManagement=false
enableBeforeTasks=true
projects=;spring-aop;spring-aspects;spring-beans;spring-context;spring-context-support;spring-core;spring-expression;spring-instrument;spring-instrument-tomcat;spring-jdbc;spring-jms;spring-orm;spring-oxm;spring-struts;spring-test;spring-tx;spring-web;spring-webmvc;spring-webmvc-portlet;
enableDSLD=false
beforeTasks=cleanEclipse;eclipse;\:spring-oxm\:compileTestJava;
@@ -0,0 +1,5 @@
#com.springsource.sts.gradle.core.preferences.GradleProjectPreferences
#Tue Feb 21 14:38:31 CET 2012
com.springsource.sts.gradle.classpath.enableSorting=false
com.springsource.sts.gradle.rootprojectloc=
com.springsource.sts.gradle.linkedresources=
@@ -0,0 +1,9 @@
#com.springsource.sts.gradle.core.actions.GradleRefreshPreferences
#Thu Aug 9 11:34:43 CEST 2012
enableAfterTasks=true
afterTasks=afterEclipseImport;
useHierarchicalNames=false
enableBeforeTasks=true
addResourceFilters=false
enableDSLD=false
beforeTasks=cleanEclipse;eclipse;\:spring-oxm\:compileTestJava;
-44
View File
@@ -1,44 +0,0 @@
= Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of fostering an open
and welcoming community, we pledge to respect all people who contribute through reporting
issues, posting feature requests, updating documentation, submitting pull requests or
patches, and other activities.
We are committed to making participation in this project a harassment-free experience for
everyone, regardless of level of experience, gender, gender identity and expression,
sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic addresses,
without explicit permission
* Other unethical or unprofessional conduct
Project maintainers have the right and responsibility to remove, edit, or reject comments,
commits, code, wiki edits, issues, and other contributions that are not aligned to this
Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors
that they deem inappropriate, threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to fairly and
consistently applying these principles to every aspect of managing this project. Project
maintainers who do not follow or enforce the Code of Conduct may be permanently removed
from the project team.
This Code of Conduct applies both within project spaces and in public spaces when an
individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
contacting a project maintainer at spring-code-of-conduct@pivotal.io . All complaints will
be reviewed and investigated and will result in a response that is deemed necessary and
appropriate to the circumstances. Maintainers are obligated to maintain confidentiality
with regard to the reporter of an incident.
This Code of Conduct is adapted from the
https://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at
https://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/]
+271 -103
View File
@@ -1,135 +1,303 @@
# Contributing to the Spring Framework
_Have something you'd like to contribute to the framework? We welcome pull
requests, but ask that you carefully read this document first to understand how
best to submit them; what kind of changes are likely to be accepted; and what
to expect from the Spring team when evaluating your submission._
First off, thank you for taking the time to contribute! :+1: :tada:
_Please refer back to this document as a checklist before issuing any pull
request; this will save time for everyone!_
### Table of Contents
## Understand the basics
* [Code of Conduct](#code-of-conduct)
* [How to Contribute](#how-to-contribute)
* [Ask questions](#ask-questions)
* [Create an Issue](#create-an-issue)
* [Issue Lifecycle](#issue-lifecycle)
* [Submit a Pull Request](#submit-a-pull-request)
* [Build from Source](#build-from-source)
* [Source Code Style](#source-code-style)
* [Reference Docs](#reference-docs)
### Code of Conduct
This project is governed by the [Spring Code of Conduct](CODE_OF_CONDUCT.adoc).
By participating you are expected to uphold this code.
Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
### How to Contribute
#### Ask questions
If you have a question, check Stack Overflow using
[this list of tags](https://stackoverflow.com/questions/tagged/spring+or+spring-mvc+or+spring-aop+or+spring-jdbc+or+spring-transactions+or+spring-annotations+or+spring-jms+or+spring-el+or+spring-test+or+spring+or+spring-remoting+or+spring-orm+or+spring-jmx+or+spring-cache+or+spring-webflux?tab=Newest). Find an existing discussion, or start a new one if necessary.
If you believe there is an issue, search through
[existing issues](https://github.com/spring-projects/spring-framework/issues) trying a
few different ways to find discussions, past or current, that are related to the issue.
Reading those discussions helps you to learn about the issue, and helps us to make a
decision.
Not sure what a pull request is, or how to submit one? Take a look at GitHub's
excellent [help documentation][] first.
#### Create an Issue
## Search JIRA first; create an issue if necessary
Reporting an issue or making a feature request is a great way to contribute. Your feedback
and the conversations that result from it provide a continuous flow of ideas. However,
before creating a ticket, please take the time to [ask and research](#ask-questions) first.
Is there already an issue that addresses your concern? Do a bit of searching
in our [JIRA issue tracker][] to see if you can find something similar. If not,
please create a new issue before submitting a pull request unless the change is
truly trivial, e.g. typo fixes, removing compiler warnings, etc.
If you create an issue after a discussion on Stack Overflow, please provide a description
in the issue instead of simply referring to Stack Overflow. The issue tracker is an
important place of record for design discussions and should be self-sufficient.
## Discuss non-trivial contribution ideas with committers
Once you're ready, create an issue on [GitHub](https://github.com/spring-projects/spring-framework/issues).
If you're considering anything more than correcting a typo or fixing a minor
bug, please discuss it on the [spring-framework-contrib][] mailing list before
submitting a pull request. We're happy to provide guidance, but please spend an
hour or two researching the subject on your own including searching the mailing
list for prior discussions.
Many issues are caused by subtle behavior, typos, and unintended configuration.
Creating a [Minimal Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example)
(starting with https://start.spring.io for example) of the problem helps the team
quickly triage your issue and get to the core of the problem.
## Sign the Contributor License Agreement
#### Issue Lifecycle
If you have not previously done so, please fill out and submit the
[SpringSource CLA form][]. You'll receive a token when this process is complete.
Keep track of this; you may be asked for it later!
When an issue is first created, it is flagged `waiting-for-triage` waiting for a team
member to triage it. Once the issue has been reviewed, the team may ask for further
information if needed, and based on the findings, the issue is either assigned a target
milestone or is closed with a specific status.
Note that emailing/postal mailing a signed copy is _not_ necessary. Submission
of the web form is all that is required.
When a fix is ready, the issue is closed and may still be re-opened until the fix is
released. After that the issue will typically no longer be reopened. In rare cases if the
issue was not at all fixed, the issue may be re-opened. In most cases however any
follow-up reports will need to be created as new issues with a fresh description.
Once you've completed the web form, simply add the following in a comment on
your pull request:
#### Submit a Pull Request
I have signed and agree to the terms of the SpringSource Individual
Contributor License Agreement.
1. If you have not previously done so, please sign the
[Contributor License Agreement](https://cla.spring.io/sign/spring). You will be reminded
automatically when you submit the PR.
You do not need to include your token/id. Please add the statement above to all
future pull requests as well, simply so that the Spring Framework team knows
immediately that this process is complete.
1. Should you create an issue first? No, just create the pull request and use the
description to provide context and motivation, as you would for an issue. If you want
to start a discussion first or have already created an issue, once a pull request is
created, we will close the issue as superseded by the pull request, and the discussion
about the issue will continue under the pull request.
1. Always check out the `main` branch and submit pull requests against it
(for target version see [settings.gradle](settings.gradle)).
Backports to prior versions will be considered on a case-by-case basis and reflected as
the fix version in the issue tracker.
## Create your branch from `3.2.x`
1. Choose the granularity of your commits consciously and squash commits that represent
multiple edits or corrections of the same logical change. See
[Rewriting History section of Pro Git](https://git-scm.com/book/en/Git-Tools-Rewriting-History)
for an overview of streamlining the commit history.
If your pull request addresses a bug or improvement, please create your branch
from Spring Framework's `3.2.x` branch. `master` is reserved for work on new features
for the next major version of the framework. Rest assured that if your pull
request is accepted and merged into `3.2.x`, these changes will also eventually
be merged into `master`.
1. Format commit messages using 55 characters for the subject line, 72 characters per line
for the description, followed by the issue fixed, e.g. `Closes gh-22276`. See the
[Commit Guidelines section of Pro Git](https://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines)
for best practices around commit messages, and use `git log` to see some examples.
1. If there is a prior issue, reference the GitHub issue number in the description of the
pull request.
## Use short branch names
If accepted, your contribution may be heavily modified as needed prior to merging.
You will likely retain author attribution for your Git commits granted that the bulk of
your changes remain intact. You may also be asked to rework the submission.
Branches used when submitting pull requests should preferably be named
according to JIRA issues, e.g. 'SPR-1234'. Otherwise, use succinct, lower-case,
dash (-) delimited names, such as 'fix-warnings', 'fix-typo', etc. In
[fork-and-edit][] cases, the GitHub default 'patch-1' is fine as well. This is
important, because branch names show up in the merge commits that result from
accepting pull requests, and should be as expressive and concise as possible.
If asked to make corrections, simply push the changes against the same branch, and your
pull request will be updated. In other words, you do not need to create a new pull request
when asked to make changes.
#### Participate in Reviews
## Mind the whitespace
Helping to review pull requests is another great way to contribute. Your feedback
can help to shape the implementation of new features. When reviewing pull requests,
however, please refrain from approving or rejecting a PR unless you are a core
committer for the Spring Framework.
Please carefully follow the whitespace and formatting conventions already
present in the framework.
### Build from Source
1. Tabs, not spaces
1. Unix (LF), not DOS (CRLF) line endings
1. Eliminate all trailing whitespace
1. Wrap Javadoc at 90 characters
1. Aim to wrap code at 90 characters, but favor readability over wrapping
1. Preserve existing formatting; i.e. do not reformat code for its own sake
1. Search the codebase using `git grep` and other tools to discover common
naming conventions, etc.
1. Latin-1 (ISO-8859-1) encoding for Java sources; use `native2ascii` to convert
if necessary
See the [Build from Source](https://github.com/spring-projects/spring-framework/wiki/Build-from-Source)
wiki page for instructions on how to check out, build, and import the Spring Framework
source code into your IDE.
### Source Code Style
## Add Apache license header to all new classes
The wiki pages
[Code Style](https://github.com/spring-projects/spring-framework/wiki/Code-Style) and
[IntelliJ IDEA Editor Settings](https://github.com/spring-projects/spring-framework/wiki/IntelliJ-IDEA-Editor-Settings)
define the source file coding standards we use along with some IDEA editor settings we customize.
```java
/*
* 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.
*/
### Reference Docs
package ...;
```
The reference documentation is in the [framework-docs/src/docs/asciidoc](framework-docs/src/docs/asciidoc) directory, in
[Asciidoctor](https://asciidoctor.org/) format. For trivial changes, you may be able to browse,
edit source files, and submit directly from GitHub.
## Update Apache license header to modified files as necessary
When making changes locally, execute `./gradlew :framework-docs:asciidoctor` and then browse the result under
`framework-docs/build/docs/ref-docs/html5/index.html`.
Always check the date range in the license header. For example, if you've
modified a file in 2013 whose header still reads
Asciidoctor also supports live editing. For more details see
[AsciiDoc Tooling](https://docs.asciidoctor.org/asciidoctor/latest/tooling/).
```java
* Copyright 2002-2011 the original author or authors.
```
then be sure to update it to 2013 appropriately
```java
* Copyright 2002-2013 the original author or authors.
```
## Use @since tags for newly-added public API types and methods
e.g.
```java
/**
* ...
*
* @author First Last
* @since 3.2
* @see ...
*/
```
## Submit JUnit test cases for all behavior changes
Search the codebase to find related unit tests and add additional @Test methods
within. It is also acceptable to submit test cases on a per JIRA issue basis,
e.g.
```java
package org.springframework.beans.factory.support;
/**
* Unit tests for SPR-8954, in which a custom {@link InstantiationAwareBeanPostProcessor}
* forces the predicted type of a FactoryBean, effectively preventing retrieval of the
* bean from calls to #getBeansOfType(FactoryBean.class). The implementation of
* {@link AbstractBeanFactory#isFactoryBean(String, RootBeanDefinition)} now ensures
* that not only the predicted bean type is considered, but also the original bean
* definition's beanClass.
*
* @author Chris Beams
*/
public class Spr8954Tests {
@Test
public void cornerSpr8954() {
// ...
}
}
```
## Squash commits
Use `git rebase --interactive`, `git add --patch` and other tools to "squash"
multiple commits into atomic changes. In addition to the man pages for git,
there are many resources online to help you understand how these tools work.
Here is one: http://book.git-scm.com/4_interactive_rebasing.html.
## Use real name in git commits
Please configure git to use your real first and last name for any commits you
intend to submit as pull requests. For example, this is not acceptable:
Author: Nickname <user@mail.com>
Rather, please include your first and last name, properly capitalized, as
submitted against the SpringSource contributor license agreement:
Author: First Last <user@mail.com>
This helps ensure traceability against the CLA, and also goes a long way to
ensuring useful output from tools like `git shortlog` and others.
You can configure this globally via the account admin area GitHub (useful for
fork-and-edit cases); globally with
git config --global user.name "First Last"
git config --global user.email user@mail.com
or locally for the spring-framework repository only by omitting the '--global'
flag:
cd spring-framework
git config user.name "First Last"
git config user.email user@mail.com
## Format commit messages
Please read and follow the [commit guidelines section of Pro Git][].
Most importantly, please format your commit messages in the following way
(adapted from the commit template in the link above):
Short (50 chars or less) summary of changes
More detailed explanatory text, if necessary. Wrap it to about 72
characters or so. In some contexts, the first line is treated as the
subject of an email and the rest of the text as the body. The blank
line separating the summary from the body is critical (unless you omit
the body entirely); tools like rebase can get confused if you run the
two together.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Typically a hyphen or asterisk is used for the bullet, preceded by a
single space, with blank lines in between, but conventions vary here
Issue: SPR-1234, SPR-1235
1. Use imperative statements in the subject line, e.g. "Fix broken Javadoc link"
1. Begin the subject line sentence with a capitalized verb, e.g. "Add, Prune,
Fix, Introduce, Avoid, etc."
1. Do not end the subject line with a period
1. Keep the subject line to 50 characters or less if possible
1. Wrap lines in the body at 72 characters or less
1. Mention associated JIRA issue(s) at the end of the commit comment, prefixed
with "Issue: " as above
1. In the body of the commit message, explain how things worked before this
commit, what has changed, and how things work now
For examples of this style, issue a `git log --author=cbeams` in the
spring-framework git repository. For convenience, here are several such commits:
https://github.com/SpringSource/spring-framework/commit/08e2669b84ec0faa2f7904441fe39ac70b65b078
https://github.com/SpringSource/spring-framework/commit/1d9d3e6ff79ce9f0eca03b02cd1df705925575da
https://github.com/SpringSource/spring-framework/commit/8e0b1c3a5f957af3049cfa0438317177e16d6de6
https://github.com/SpringSource/spring-framework/commit/b787a68f2050df179f7036b209aa741230a02477
## Run all tests prior to submission
See the [building from source][] section of the README for instructions. Make
sure that all tests pass prior to submitting your pull request.
## Submit your pull request
Subject line:
Follow the same conventions for pull request subject lines as mentioned above
for commit message subject lines.
In the body:
1. Explain your use case. What led you to submit this change? Why were existing
mechanisms in the framework insufficient? Make a case that this is a
general-purpose problem and that yours is a general-purpose solution, etc.
1. Add any additional information and ask questions; start a conversation, or
continue one from JIRA
1. Mention the JIRA issue ID
1. Also mention that you have submitted the CLA as described above
Note that for pull requests containing a single commit, GitHub will default the
subject line and body of the pull request to match the subject line and body of
the commit message. This is fine, but please also include the items above in the
body of the request.
## Mention your pull request on the associated JIRA issue
Add a comment to the associated JIRA issue(s) linking to your new pull request.
## Expect discussion and rework
The Spring team takes a very conservative approach to accepting contributions to
the framework. This is to keep code quality and stability as high as possible,
and to keep complexity at a minimum. Your changes, if accepted, may be heavily
modified prior to merging. You will retain "Author:" attribution for your Git
commits granted that the bulk of your changes remain intact. You may be asked to
rework the submission for style (as explained above) and/or substance. Again, we
strongly recommend discussing any serious submissions with the Spring Framework
team _prior_ to engaging in serious development work.
Note that you can always force push (`git push -f`) reworked / rebased commits
against the branch used to submit your pull request. i.e. you do not need to
issue a new pull request when asked to make changes.
[help documentation]: http://help.github.com/send-pull-requests
[JIRA issue tracker]: https://jira.springsource.org/browse/SPR
[spring-framework-contrib]: https://groups.google.com/forum/#!forum/spring-framework-contrib
[SpringSource CLA form]: https://support.springsource.com/spring_committer_signup
[fork-and-edit]: https://github.com/blog/844-forking-with-the-edit-button
[commit guidelines section of Pro Git]: http://progit.org/book/ch5-2.html#commit_guidelines
[building from source]: https://github.com/SpringSource/spring-framework#building-from-source
-202
View File
@@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
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
https://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.
+79 -21
View File
@@ -1,38 +1,96 @@
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://ci.spring.io/api/v1/teams/spring-framework/pipelines/spring-framework-5.3.x/jobs/build/badge)](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.3.x?groups=Build") [![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring)
## Spring Framework
The Spring Framework provides a comprehensive programming and configuration
model for modern Java-based enterprise applications - on any kind of deployment
platform. A key element of Spring is infrastructural support at the application
level: Spring focuses on the "plumbing" of enterprise applications so that teams
can focus on application-level business logic, without unnecessary ties to
specific deployment environments.
This is the home of the Spring Framework: the foundation for all [Spring projects](https://spring.io/projects). Collectively the Spring Framework and the family of Spring projects are often referred to simply as "Spring".
The framework also serves as the foundation for [Spring Integration][], [Spring
Batch][] and the rest of the Spring [family of projects][]. Browse the
repositories under the [SpringSource organization][] on GitHub for a full list.
Spring provides everything required beyond the Java programming language for creating enterprise applications for a wide range of scenarios and architectures. Please read the [Overview](https://docs.spring.io/spring/docs/current/spring-framework-reference/overview.html#spring-introduction) section as reference for a more complete introduction.
[.NET][] and [Python][] variants are available as well.
## Code of Conduct
This project is governed by the [Spring Code of Conduct](CODE_OF_CONDUCT.adoc). By participating, you are expected to uphold this code of conduct. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
## Access to Binaries
For access to artifacts or a distribution zip, see the [Spring Framework Artifacts](https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Artifacts) wiki page.
## Downloading artifacts
See [downloading Spring artifacts][] for Maven repository information. Unable to
use Maven or other transitive dependency management tools? See [building a
distribution with dependencies][].
## Documentation
See the current [Javadoc][] and [reference docs][].
The Spring Framework maintains reference documentation ([published](https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/) and [source](framework-docs/src/docs/asciidoc)), GitHub [wiki pages](https://github.com/spring-projects/spring-framework/wiki), and an
[API reference](https://docs.spring.io/spring-framework/docs/current/javadoc-api/). There are also [guides and tutorials](https://spring.io/guides) across Spring projects.
## Getting support
Check out the [Spring forums][] and the [spring][spring tag] and
[spring-mvc][spring-mvc tag] tags on [Stack Overflow][]. [Commercial support][]
is available too.
## Micro-Benchmarks
## Issue Tracking
Report issues via the [Spring Framework JIRA]. Understand our issue management
process by reading about [the lifecycle of an issue][]. Think you've found a
bug? Please consider submitting a reproduction project via the
[spring-framework-issues][] GitHub repository. The [readme][] there provides
simple step-by-step instructions.
See the [Micro-Benchmarks](https://github.com/spring-projects/spring-framework/wiki/Micro-Benchmarks) wiki page.
## Building from source
The Spring Framework uses a [Gradle][]-based build system. In the instructions
below, [`./gradlew`][] is invoked from the root of the source tree and serves as
a cross-platform, self-contained bootstrap mechanism for the build. The only
prerequisites are [Git][] and JDK 1.7+.
## Build from Source
### check out sources
`git clone git://github.com/SpringSource/spring-framework.git`
See the [Build from Source](https://github.com/spring-projects/spring-framework/wiki/Build-from-Source) wiki page and the [CONTRIBUTING.md](CONTRIBUTING.md) file.
### compile and test, build all jars, distribution zips and docs
`./gradlew build`
## Continuous Integration Builds
### install all spring-\* jars into your local Maven cache
`./gradlew install`
Information regarding CI builds can be found in the [Spring Framework Concourse pipeline](ci/README.adoc) documentation.
### import sources into your IDE
Run `./import-into-eclipse.sh` or read `import-into-idea.md` as appropriate.
## Stay in Touch
... and discover more commands with `./gradlew tasks`. See also the [Gradle
build and release FAQ][].
Follow [@SpringCentral](https://twitter.com/springcentral), [@SpringFramework](https://twitter.com/springframework), and its [team members](https://twitter.com/springframework/lists/team/members) on Twitter. In-depth articles can be found at [The Spring Blog](https://spring.io/blog/), and releases are announced via our [news feed](https://spring.io/blog/category/news).
## Contributing
[Pull requests][] are welcome; see the [contributor guidelines][] for details.
## Staying in touch
Follow [@springframework][] and its [team members][] on Twitter. In-depth
articles can be found at the SpringSource [team blog][], and releases are
announced via our [news feed][].
## License
The Spring Framework is released under version 2.0 of the [Apache License][].
The Spring Framework is released under version 2.0 of the [Apache License](https://www.apache.org/licenses/LICENSE-2.0).
[Spring Integration]: https://github.com/SpringSource/spring-integration
[Spring Batch]: https://github.com/SpringSource/spring-batch
[family of projects]: http://springsource.org/projects
[SpringSource organization]: https://github.com/SpringSource
[.NET]: https://github.com/SpringSource/spring-net
[Python]: https://github.com/SpringSource/spring-python
[downloading Spring artifacts]: https://github.com/SpringSource/spring-framework/wiki/Downloading-Spring-artifacts
[building a distribution with dependencies]: https://github.com/SpringSource/spring-framework/wiki/Building-a-distribution-with-dependencies
[Javadoc]: http://static.springsource.org/spring-framework/docs/current/javadoc-api
[reference docs]: http://static.springsource.org/spring-framework/docs/current/spring-framework-reference
[Spring forums]: http://forum.springsource.org
[spring tag]: http://stackoverflow.com/questions/tagged/spring
[spring-mvc tag]: http://stackoverflow.com/questions/tagged/spring-mvc
[Stack Overflow]: http://stackoverflow.com/faq
[Commercial support]: http://springsource.com/support/springsupport
[Spring Framework JIRA]: http://jira.springsource.org/browse/SPR
[the lifecycle of an issue]: https://github.com/cbeams/spring-framework/wiki/The-Lifecycle-of-an-Issue
[spring-framework-issues]: https://github.com/SpringSource/spring-framework-issues#readme
[readme]: https://github.com/SpringSource/spring-framework-issues#readme
[Gradle]: http://gradle.org
[`./gradlew`]: http://vimeo.com/34436402
[Git]: http://help.github.com/set-up-git-redirect
[Gradle build and release FAQ]: https://github.com/SpringSource/spring-framework/wiki/Gradle-build-and-release-FAQ
[Pull requests]: http://help.github.com/send-pull-requests
[contributor guidelines]: https://github.com/SpringSource/spring-framework/blob/master/CONTRIBUTING.md
[@springframework]: http://twitter.com/springframework
[team members]: http://twitter.com/springframework/team/members
[team blog]: http://blog.springsource.org
[news feed]: http://www.springsource.org/news-events
[Apache License]: http://www.apache.org/licenses/LICENSE-2.0
-11
View File
@@ -1,11 +0,0 @@
# Security Policy
## Supported Versions
Please see the
[Spring Framework Versions](https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions)
wiki page.
## Reporting a Vulnerability
Please see https://spring.io/security-policy.
+1031 -142
View File
File diff suppressed because it is too large Load Diff
-75
View File
@@ -1,75 +0,0 @@
# Spring Framework Build
This folder contains the custom plugins and conventions for the Spring Framework build.
They are declared in the `build.gradle` file in this folder.
## Build Conventions
The `org.springframework.build.conventions` plugin applies all conventions to the Framework build:
* Configuring the Java compiler, see `JavaConventions`
* Configuring the Kotlin compiler, see `KotlinConventions`
* Configuring testing in the build with `TestConventions`
## Build Plugins
### Optional dependencies
The `org.springframework.build.optional-dependencies` plugin creates a new `optional`
Gradle configuration - it adds the dependencies to the project's compile and runtime classpath
but doesn't affect the classpath of dependent projects.
This plugin does not provide a `provided` configuration, as the native `compileOnly` and `testCompileOnly`
configurations are preferred.
### API Diff
This plugin uses the [Gradle JApiCmp](https://github.com/melix/japicmp-gradle-plugin) plugin
to generate API Diff reports for each Spring Framework module. This plugin is applied once on the root
project and creates tasks in each framework module. Unlike previous versions of this part of the build,
there is no need for checking out a specific tag. The plugin will fetch the JARs we want to compare the
current working version with. You can generate the reports for all modules or a single module:
```
./gradlew apiDiff -PbaselineVersion=5.1.0.RELEASE
./gradlew :spring-core:apiDiff -PbaselineVersion=5.1.0.RELEASE
```
The reports are located under `build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/`.
### RuntimeHints Java Agent
The `spring-core-test` project module contributes the `RuntimeHintsAgent` Java agent.
The `RuntimeHintsAgentPlugin` Gradle plugin creates a dedicated `"runtimeHintsTest"` test task for each project.
This task will detect and execute [tests tagged](https://junit.org/junit5/docs/current/user-guide/#running-tests-build-gradle)
with the `"RuntimeHintsTests"` [JUnit tag](https://junit.org/junit5/docs/current/user-guide/#running-tests-tags).
In the Spring Framework test suite, those are usually annotated with the `@EnabledIfRuntimeHintsAgent` annotation.
By default, the agent will instrument all classes located in the `"org.springframework"` package, as they are loaded.
The `RuntimeHintsAgentExtension` allows to customize this using a DSL:
```groovy
// this applies the `RuntimeHintsAgentPlugin` to the project
plugins {
id 'org.springframework.build.runtimehints-agent'
}
// You can configure the agent to include and exclude packages from the instrumentation process.
runtimeHintsAgent {
includedPackages = ["org.springframework", "io.spring"]
excludedPackages = ["org.example"]
}
dependencies {
// to use the test infrastructure, the project should also depend on the "spring-core-test" module
testImplementation(project(":spring-core-test"))
}
```
With this configuration, `./gradlew runtimeHintsTest` will run all tests instrumented by this java agent.
The global `./gradlew check` task depends on `runtimeHintsTest`.
NOTE: the "spring-core-test" module doesn't shade "spring-core" by design, so the agent should never instrument
code that doesn't have "spring-core" on its classpath.
-45
View File
@@ -1,45 +0,0 @@
plugins {
id 'java-gradle-plugin'
}
repositories {
mavenCentral()
gradlePluginPortal()
}
ext {
def propertiesFile = new File(new File("$projectDir").parentFile, "gradle.properties")
propertiesFile.withInputStream {
def properties = new Properties()
properties.load(it)
set("kotlinVersion", properties["kotlinVersion"])
}
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
implementation("org.jetbrains.kotlin:kotlin-compiler-embeddable:${kotlinVersion}")
implementation "me.champeau.gradle:japicmp-gradle-plugin:0.3.0"
implementation "org.gradle:test-retry-gradle-plugin:1.4.1"
}
gradlePlugin {
plugins {
apiDiffPlugin {
id = "org.springframework.build.api-diff"
implementationClass = "org.springframework.build.api.ApiDiffPlugin"
}
conventionsPlugin {
id = "org.springframework.build.conventions"
implementationClass = "org.springframework.build.ConventionsPlugin"
}
optionalDependenciesPlugin {
id = "org.springframework.build.optional-dependencies"
implementationClass = "org.springframework.build.optional.OptionalDependenciesPlugin"
}
runtimeHintsAgentPlugin {
id = "org.springframework.build.runtimehints-agent"
implementationClass = "org.springframework.build.hint.RuntimeHintsAgentPlugin"
}
}
}
-1
View File
@@ -1 +0,0 @@
org.gradle.caching=true
@@ -0,0 +1,158 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.build.gradle
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
/**
* Gradle plugin that detects identically named, non-empty packages split across multiple
* subprojects, e.g. "org.springframework.context.annotation" existing in both spring-core
* and spring-aspects. Adds a 'detectSplitPackages' task to the current project's task
* collection. If the project already contains a 'check' task (i.e. is a typical Gradle
* project with the "java" plugin applied), the 'check' task will be updated to depend on
* the execution of 'detectSplitPackages'.
*
* By default, all subprojects will be scanned. Use the 'projectsToScan' task property to
* modify this value. Example usage:
*
* apply plugin: 'detect-split-packages // typically applied to root project
*
* detectSplitPackages {
* packagesToScan -= project(":spring-xyz") // scan every project but spring-xyz
* }
*
* @author Rob Winch
* @author Glyn Normington
* @author Chris Beams
*/
public class DetectSplitPackagesPlugin implements Plugin<Project> {
public void apply(Project project) {
def tasks = project.tasks
Task detectSplitPackages = tasks.create("detectSplitPackages", DetectSplitPackagesTask.class)
if (tasks.asMap.containsKey("check")) {
tasks.getByName("check").dependsOn detectSplitPackages
}
}
}
public class DetectSplitPackagesTask extends DefaultTask {
private static final String JAVA_FILE_SUFFIX = ".java"
private static final String PACKAGE_SEPARATOR = "."
private static final String HIDDEN_DIRECTORY_PREFIX = "."
@Input
Set<Project> projectsToScan = project.subprojects
public DetectSplitPackagesTask() {
this.group = "Verification"
this.description = "Detects packages split across two or more subprojects."
}
@TaskAction
public void detectSplitPackages() {
def splitPackages = doDetectSplitPackages()
if (!splitPackages.isEmpty()) {
def message = "The following split package(s) have been detected:\n"
splitPackages.each { pkg, mod ->
message += " - ${pkg} (split across ${mod[0].name} and ${mod[1].name})\n"
}
throw new GradleException(message)
}
}
private Map<String, List<Project>> doDetectSplitPackages() {
def splitPackages = [:]
def mergedProjects = findMergedProjects()
def packagesByProject = mapPackagesByProject()
def projects = packagesByProject.keySet().toArray()
def nProjects = projects.length
for (int i = 0; i < nProjects - 1; i++) {
for (int j = i + 1; j < nProjects - 1; j++) {
def prj_i = projects[i]
def prj_j = projects[j]
def pkgs_i = new HashSet(packagesByProject.get(prj_i))
def pkgs_j = packagesByProject.get(prj_j)
pkgs_i.retainAll(pkgs_j)
if (!pkgs_i.isEmpty()
&& mergedProjects.get(prj_i) != prj_j
&& mergedProjects.get(prj_j) != prj_i) {
pkgs_i.each { pkg ->
def readablePkg = pkg.substring(1).replaceAll(File.separator, PACKAGE_SEPARATOR)
splitPackages[readablePkg] = [prj_i, prj_j]
}
}
}
}
return splitPackages;
}
private Map<Project, Set<String>> mapPackagesByProject() {
def packagesByProject = [:]
this.projectsToScan.each { Project p ->
def packages = new HashSet<String>()
p.sourceSets.main.java.srcDirs.each { File dir ->
findPackages(packages, dir, "")
}
if (!packages.isEmpty()) {
packagesByProject.put(p, packages)
}
}
return packagesByProject;
}
private Map<Project, Project> findMergedProjects() {
def mergedProjects = [:]
this.projectsToScan.findAll { p ->
p.plugins.findPlugin(MergePlugin)
}.findAll { p ->
p.merge.into
}.each { p ->
mergedProjects.put(p, p.merge.into)
}
return mergedProjects
}
private static void findPackages(Set<String> packages, File dir, String packagePath) {
def scanDir = new File(dir, packagePath)
def File[] javaFiles = scanDir.listFiles({ file ->
!file.isDirectory() && file.name.endsWith(JAVA_FILE_SUFFIX)
} as FileFilter)
if (javaFiles != null && javaFiles.length != 0) {
packages.add(packagePath)
}
scanDir.listFiles({ File file ->
file.isDirectory() && !file.name.startsWith(HIDDEN_DIRECTORY_PREFIX)
} as FileFilter).each { File subDir ->
findPackages(packages, dir, packagePath + File.separator + subDir.name)
}
}
}
@@ -0,0 +1,160 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.build.gradle
import org.gradle.api.*
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 multiple incompatible versions of third-party
* dependencies (for example Hibernate v3 and v4).
* <p>
* The 'merge' extension should be used to define how projects are merged, for example:
* <pre>
* configure(subprojects) {
* apply plugin: MergePlugin
* }
*
* project("myproject") {
* }
*
* project("myproject-extra") {
* merge.into = project("myproject")
* }
* </pre>
* <p>
* This plugin adds two new configurations:
* <ul>
* <li>merging - Contains the projects being merged into this project<li>
* <li>runtimeMerge - Contains all dependencies that are merge projects. These are used
* to allow an IDE to reference merge projects.</li>
* <ul>
*
* @author Rob Winch
* @author Phillip Webb
*/
class MergePlugin implements Plugin<Project> {
private static boolean attachedProjectsEvaluated;
public void apply(Project project) {
project.plugins.apply(MavenPlugin)
project.plugins.apply(EclipsePlugin)
project.plugins.apply(IdeaPlugin)
MergeModel model = project.extensions.create("merge", MergeModel)
project.configurations.create("merging")
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
// Hook to perform the actual merge logic
project.afterEvaluate{
if(it.merge.into != null) {
setup(it)
}
}
// Hook to build runtimeMerge dependencies
if(!attachedProjectsEvaluated) {
project.gradle.projectsEvaluated{
postProcessProjects(it)
}
attachedProjectsEvaluated = true;
}
}
private void setup(Project project) {
project.merge.into.dependencies.add("merging", project)
project.dependencies.add("provided", project.merge.into.sourceSets.main.output)
project.dependencies.add("runtimeMerge", project.merge.into)
setupTaskDependencies(project)
setupMaven(project)
}
private void setupTaskDependencies(Project 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) {
task.enabled = false
task.dependsOn(project.merge.into.tasks.findByPath(it))
}
}
// update 'into' project artifacts to contain the source artifact contents
project.merge.into.sourcesJar.from(project.sourcesJar.source)
project.merge.into.jar.from(project.sourceSets.main.output)
project.merge.into.javadoc {
source += project.javadoc.source
classpath += project.javadoc.classpath
}
}
private void setupMaven(Project project) {
project.configurations.each { configuration ->
Conf2ScopeMapping mapping = project.conf2ScopeMappings.getMapping([configuration])
if(mapping.scope) {
Configuration intoConfiguration = project.merge.into.configurations.create(
project.name + "-" + configuration.name)
configuration.excludeRules.each {
configuration.exclude([
(ExcludeRule.GROUP_KEY) : it.group,
(ExcludeRule.MODULE_KEY) : it.module])
}
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)) {
intoConfiguration.dependencies.add(it)
}
}
project.merge.into.install.repositories.mavenInstaller.pom.scopeMappings.addMapping(
mapping.priority + 100, intoConfiguration, mapping.scope)
}
}
}
private postProcessProjects(Gradle gradle) {
gradle.allprojects(new Action<Project>() {
public void execute(Project project) {
project.configurations.getByName("runtime").allDependencies.withType(ProjectDependency).each{
Configuration dependsOnMergedFrom = it.dependencyProject.configurations.getByName("merging");
dependsOnMergedFrom.dependencies.each{ dep ->
project.dependencies.add("runtimeMerge", dep.dependencyProject)
}
}
}
});
}
}
class MergeModel {
Project into;
}
@@ -0,0 +1,57 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.build.gradle
import org.gradle.api.Plugin
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.
*
* @author Phillip Webb
*/
class TestSourceSetDependenciesPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.afterEvaluate {
Set<ProjectDependency> projectDependencies = new LinkedHashSet<ProjectDependency>()
collectProjectDependencies(projectDependencies, project)
projectDependencies.each {
project.dependencies.add("testCompile", it.dependencyProject.sourceSets.test.output)
}
}
}
private void collectProjectDependencies(Set<ProjectDependency> projectDependencies,
Project project) {
for(def configurationName in ["compile", "optional", "provided", "testCompile"]) {
Configuration configuration = project.getConfigurations().findByName(configurationName)
if(configuration) {
configuration.dependencies.findAll { it instanceof ProjectDependency }.each {
projectDependencies.add(it)
collectProjectDependencies(projectDependencies, it.dependencyProject)
}
}
}
}
}
@@ -1,45 +0,0 @@
/*
* Copyright 2002-2022 the original author 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.build;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaBasePlugin;
import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin;
/**
* Plugin to apply conventions to projects that are part of Spring Framework's build.
* Conventions are applied in response to various plugins being applied.
*
* When the {@link JavaBasePlugin} is applied, the conventions in {@link TestConventions}
* are applied.
* When the {@link JavaBasePlugin} is applied, the conventions in {@link JavaConventions}
* are applied.
* When the {@link KotlinBasePlugin} is applied, the conventions in {@link KotlinConventions}
* are applied.
*
* @author Brian Clozel
*/
public class ConventionsPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
new JavaConventions().apply(project);
new KotlinConventions().apply(project);
new TestConventions().apply(project);
}
}
@@ -1,86 +0,0 @@
/*
* Copyright 2002-2022 the original author 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.build;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.tasks.compile.JavaCompile;
/**
* {@link Plugin} that applies conventions for compiling Java sources in Spring Framework.
*
* @author Brian Clozel
* @author Sam Brannen
* @author Sebastien Deleuze
*/
public class JavaConventions {
private static final List<String> COMPILER_ARGS;
private static final List<String> TEST_COMPILER_ARGS;
static {
List<String> commonCompilerArgs = Arrays.asList(
"-Xlint:serial", "-Xlint:cast", "-Xlint:classfile", "-Xlint:dep-ann",
"-Xlint:divzero", "-Xlint:empty", "-Xlint:finally", "-Xlint:overrides",
"-Xlint:path", "-Xlint:processing", "-Xlint:static", "-Xlint:try", "-Xlint:-options",
"-parameters"
);
COMPILER_ARGS = new ArrayList<>();
COMPILER_ARGS.addAll(commonCompilerArgs);
COMPILER_ARGS.addAll(Arrays.asList(
"-Xlint:varargs", "-Xlint:fallthrough", "-Xlint:rawtypes", "-Xlint:deprecation",
"-Xlint:unchecked", "-Werror"
));
TEST_COMPILER_ARGS = new ArrayList<>();
TEST_COMPILER_ARGS.addAll(commonCompilerArgs);
TEST_COMPILER_ARGS.addAll(Arrays.asList("-Xlint:-varargs", "-Xlint:-fallthrough", "-Xlint:-rawtypes",
"-Xlint:-deprecation", "-Xlint:-unchecked"));
}
public void apply(Project project) {
project.getPlugins().withType(JavaBasePlugin.class, javaPlugin -> applyJavaCompileConventions(project));
}
/**
* Applies the common Java compiler options for main sources, test fixture sources, and
* test sources.
* @param project the current project
*/
private void applyJavaCompileConventions(Project project) {
project.getTasks().withType(JavaCompile.class)
.matching(compileTask -> compileTask.getName().equals(JavaPlugin.COMPILE_JAVA_TASK_NAME))
.forEach(compileTask -> {
compileTask.getOptions().setCompilerArgs(COMPILER_ARGS);
compileTask.getOptions().setEncoding("UTF-8");
});
project.getTasks().withType(JavaCompile.class)
.matching(compileTask -> compileTask.getName().equals(JavaPlugin.COMPILE_TEST_JAVA_TASK_NAME)
|| compileTask.getName().equals("compileTestFixturesJava"))
.forEach(compileTask -> {
compileTask.getOptions().setCompilerArgs(TEST_COMPILER_ARGS);
compileTask.getOptions().setEncoding("UTF-8");
});
}
}
@@ -1,48 +0,0 @@
/*
* Copyright 2002-2022 the original author 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.build;
import java.util.ArrayList;
import java.util.List;
import org.gradle.api.Project;
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions;
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile;
/**
* @author Brian Clozel
*/
public class KotlinConventions {
void apply(Project project) {
project.getPlugins().withId("org.jetbrains.kotlin.jvm",
(plugin) -> project.getTasks().withType(KotlinCompile.class, this::configure));
}
private void configure(KotlinCompile compile) {
KotlinJvmOptions kotlinOptions = compile.getKotlinOptions();
kotlinOptions.setApiVersion("1.7");
kotlinOptions.setLanguageVersion("1.7");
kotlinOptions.setJvmTarget("17");
kotlinOptions.setJavaParameters(true);
kotlinOptions.setAllWarningsAsErrors(true);
List<String> freeCompilerArgs = new ArrayList<>(compile.getKotlinOptions().getFreeCompilerArgs());
freeCompilerArgs.addAll(List.of("-Xsuppress-version-warnings", "-Xjsr305=strict", "-opt-in=kotlin.RequiresOptIn"));
compile.getKotlinOptions().setFreeCompilerArgs(freeCompilerArgs);
}
}
@@ -1,56 +0,0 @@
/*
* Copyright 2002-2022 the original author 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.build;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.tasks.testing.Test;
import org.gradle.testretry.TestRetryPlugin;
import org.gradle.testretry.TestRetryTaskExtension;
/**
* Conventions that are applied in the presence of the {@link JavaBasePlugin}. When the
* plugin is applied:
* <ul>
* <li>The {@link TestRetryPlugin Test Retry} plugins is applied so that flaky tests
* are retried 3 times when running on the CI.
* </ul>
*
* @author Brian Clozel
* @author Andy Wilkinson
*/
class TestConventions {
void apply(Project project) {
project.getPlugins().withType(JavaBasePlugin.class, (java) -> configureTestConventions(project));
}
private void configureTestConventions(Project project) {
project.getPlugins().apply(TestRetryPlugin.class);
project.getTasks().withType(Test.class,
(test) -> project.getPlugins().withType(TestRetryPlugin.class, (testRetryPlugin) -> {
TestRetryTaskExtension testRetry = test.getExtensions().getByType(TestRetryTaskExtension.class);
testRetry.getFailOnPassedAfterRetry().set(true);
testRetry.getMaxRetries().set(isCi() ? 3 : 0);
}));
}
private boolean isCi() {
return Boolean.parseBoolean(System.getenv("CI"));
}
}
@@ -1,140 +0,0 @@
/*
* Copyright 2002-2019 the original author 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.build.api;
import java.io.File;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import me.champeau.gradle.japicmp.JapicmpPlugin;
import me.champeau.gradle.japicmp.JapicmpTask;
import org.gradle.api.GradleException;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.jvm.tasks.Jar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link Plugin} that applies the {@code "japicmp-gradle-plugin"}
* and create tasks for all subprojects named {@code "spring-*"}, diffing the public API one by one
* and creating the reports in {@code "build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/"}.
* <p>{@code "./gradlew apiDiff -PbaselineVersion=5.1.0.RELEASE"} will output the
* reports for the API diff between the baseline version and the current one for all modules.
* You can limit the report to a single module with
* {@code "./gradlew :spring-core:apiDiff -PbaselineVersion=5.1.0.RELEASE"}.
*
* @author Brian Clozel
*/
public class ApiDiffPlugin implements Plugin<Project> {
private static final Logger logger = LoggerFactory.getLogger(ApiDiffPlugin.class);
public static final String TASK_NAME = "apiDiff";
private static final String BASELINE_VERSION_PROPERTY = "baselineVersion";
private static final List<String> PACKAGE_INCLUDES = Collections.singletonList("org.springframework.*");
private static final URI SPRING_MILESTONE_REPOSITORY = URI.create("https://repo.spring.io/milestone");
@Override
public void apply(Project project) {
if (project.hasProperty(BASELINE_VERSION_PROPERTY) && project.equals(project.getRootProject())) {
project.getPluginManager().apply(JapicmpPlugin.class);
project.getPlugins().withType(JapicmpPlugin.class,
plugin -> applyApiDiffConventions(project));
}
}
private void applyApiDiffConventions(Project project) {
String baselineVersion = project.property(BASELINE_VERSION_PROPERTY).toString();
project.subprojects(subProject -> {
if (subProject.getName().startsWith("spring-")) {
createApiDiffTask(baselineVersion, subProject);
}
});
}
private void createApiDiffTask(String baselineVersion, Project project) {
if (isProjectEligible(project)) {
// Add Spring Milestone repository for generating diffs against previous milestones
project.getRootProject()
.getRepositories()
.maven(mavenArtifactRepository -> mavenArtifactRepository.setUrl(SPRING_MILESTONE_REPOSITORY));
JapicmpTask apiDiff = project.getTasks().create(TASK_NAME, JapicmpTask.class);
apiDiff.setDescription("Generates an API diff report with japicmp");
apiDiff.setGroup(JavaBasePlugin.DOCUMENTATION_GROUP);
apiDiff.setOldClasspath(createBaselineConfiguration(baselineVersion, project));
TaskProvider<Jar> jar = project.getTasks().withType(Jar.class).named("jar");
apiDiff.setNewArchives(project.getLayout().files(jar.get().getArchiveFile().get().getAsFile()));
apiDiff.setNewClasspath(getRuntimeClassPath(project));
apiDiff.setPackageIncludes(PACKAGE_INCLUDES);
apiDiff.setOnlyModified(true);
apiDiff.setIgnoreMissingClasses(true);
// Ignore Kotlin metadata annotations since they contain
// illegal HTML characters and fail the report generation
apiDiff.setAnnotationExcludes(Collections.singletonList("@kotlin.Metadata"));
apiDiff.setHtmlOutputFile(getOutputFile(baselineVersion, project));
apiDiff.dependsOn(project.getTasks().getByName("jar"));
}
}
private boolean isProjectEligible(Project project) {
return project.getPlugins().hasPlugin(JavaPlugin.class)
&& project.getPlugins().hasPlugin(MavenPublishPlugin.class);
}
private Configuration createBaselineConfiguration(String baselineVersion, Project project) {
String baseline = String.join(":",
project.getGroup().toString(), project.getName(), baselineVersion);
Dependency baselineDependency = project.getDependencies().create(baseline + "@jar");
Configuration baselineConfiguration = project.getRootProject().getConfigurations().detachedConfiguration(baselineDependency);
try {
// eagerly resolve the baseline configuration to check whether this is a new Spring module
baselineConfiguration.resolve();
return baselineConfiguration;
}
catch (GradleException exception) {
logger.warn("Could not resolve {} - assuming this is a new Spring module.", baseline);
}
return project.getRootProject().getConfigurations().detachedConfiguration();
}
private Configuration getRuntimeClassPath(Project project) {
return project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
}
private File getOutputFile(String baseLineVersion, Project project) {
Path outDir = Paths.get(project.getRootProject().getBuildDir().getAbsolutePath(),
"reports", "api-diff",
baseLineVersion + "_to_" + project.getRootProject().getVersion());
return project.file(outDir.resolve(project.getName() + ".html").toString());
}
}
@@ -1,53 +0,0 @@
/*
* Copyright 2002-2022 the original author 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.build.hint;
import java.util.Collections;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.SetProperty;
/**
* Entry point to the DSL extension for the {@link RuntimeHintsAgentPlugin} Gradle plugin.
* @author Brian Clozel
*/
public class RuntimeHintsAgentExtension {
private final SetProperty<String> includedPackages;
private final SetProperty<String> excludedPackages;
public RuntimeHintsAgentExtension(ObjectFactory objectFactory) {
this.includedPackages = objectFactory.setProperty(String.class).convention(Collections.singleton("org.springframework"));
this.excludedPackages = objectFactory.setProperty(String.class).convention(Collections.emptySet());
}
public SetProperty<String> getIncludedPackages() {
return this.includedPackages;
}
public SetProperty<String> getExcludedPackages() {
return this.excludedPackages;
}
String asJavaAgentArgument() {
StringBuilder builder = new StringBuilder();
this.includedPackages.get().forEach(packageName -> builder.append('+').append(packageName).append(','));
this.excludedPackages.get().forEach(packageName -> builder.append('-').append(packageName).append(','));
return builder.toString();
}
}
@@ -1,56 +0,0 @@
/*
* Copyright 2002-2022 the original author 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.build.hint;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.tasks.bundling.Jar;
import org.gradle.api.tasks.testing.Test;
/**
* {@link Plugin} that configures the {@code RuntimeHints} Java agent to test tasks.
*
* @author Brian Clozel
*/
public class RuntimeHintsAgentPlugin implements Plugin<Project> {
public static final String RUNTIMEHINTS_TEST_TASK = "runtimeHintsTest";
private static final String EXTENSION_NAME = "runtimeHintsAgent";
@Override
public void apply(Project project) {
project.getPlugins().withType(JavaPlugin.class, javaPlugin -> {
RuntimeHintsAgentExtension agentExtension = project.getExtensions().create(EXTENSION_NAME,
RuntimeHintsAgentExtension.class, project.getObjects());
Test agentTest = project.getTasks().create(RUNTIMEHINTS_TEST_TASK, Test.class, test -> {
test.useJUnitPlatform(options -> {
options.includeTags("RuntimeHintsTests");
});
test.include("**/*Tests.class", "**/*Test.class");
test.systemProperty("java.awt.headless", "true");
});
project.afterEvaluate(p -> {
Jar jar = project.getRootProject().project("spring-core-test").getTasks().withType(Jar.class).named("jar").get();
agentTest.jvmArgs("-javaagent:" + jar.getArchiveFile().get().getAsFile() + "=" + agentExtension.asJavaAgentArgument());
});
project.getTasks().getByName("check", task -> task.dependsOn(agentTest));
});
}
}
@@ -1,56 +0,0 @@
/*
* Copyright 2002-2021 the original author 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.build.optional;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.tasks.SourceSetContainer;
/**
* A {@code Plugin} that adds support for Maven-style optional dependencies. Creates a new
* {@code optional} configuration. The {@code optional} configuration is part of the
* project's compile and runtime classpaths but does not affect the classpath of
* dependent projects.
*
* @author Andy Wilkinson
*/
public class OptionalDependenciesPlugin implements Plugin<Project> {
/**
* Name of the {@code optional} configuration.
*/
public static final String OPTIONAL_CONFIGURATION_NAME = "optional";
@Override
public void apply(Project project) {
Configuration optional = project.getConfigurations().create("optional");
optional.setCanBeConsumed(false);
optional.setCanBeResolved(false);
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
SourceSetContainer sourceSets = project.getExtensions().getByType(JavaPluginExtension.class)
.getSourceSets();
sourceSets.all((sourceSet) -> {
project.getConfigurations().getByName(sourceSet.getCompileClasspathConfigurationName()).extendsFrom(optional);
project.getConfigurations().getByName(sourceSet.getRuntimeClasspathConfigurationName()).extendsFrom(optional);
});
});
}
}
@@ -1,175 +0,0 @@
package org.springframework.build.shadow;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.gradle.api.DefaultTask;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.component.ModuleComponentSelector;
import org.gradle.api.artifacts.query.ArtifactResolutionQuery;
import org.gradle.api.artifacts.result.ArtifactResolutionResult;
import org.gradle.api.artifacts.result.ComponentArtifactsResult;
import org.gradle.api.artifacts.result.DependencyResult;
import org.gradle.api.artifacts.result.ResolutionResult;
import org.gradle.api.artifacts.result.ResolvedArtifactResult;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.FileCopyDetails;
import org.gradle.api.file.FileTree;
import org.gradle.api.tasks.Classpath;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.Nested;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.TaskAction;
import org.gradle.jvm.JvmLibrary;
import org.gradle.language.base.artifact.SourcesArtifact;
/**
* Gradle task to add source from shadowed jars into our own source jars.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
public class ShadowSource extends DefaultTask {
private final DirectoryProperty outputDirectory = getProject().getObjects().directoryProperty();
private List<Configuration> configurations = new ArrayList<>();
private final List<Relocation> relocations = new ArrayList<>();
@Classpath
@Optional
public List<Configuration> getConfigurations() {
return this.configurations;
}
public void setConfigurations(List<Configuration> configurations) {
this.configurations = configurations;
}
@Nested
public List<Relocation> getRelocations() {
return this.relocations;
}
public void relocate(String pattern, String destination) {
this.relocations.add(new Relocation(pattern, destination));
}
@OutputDirectory
DirectoryProperty getOutputDirectory() {
return this.outputDirectory;
}
@TaskAction
void syncSourceJarFiles() {
sync(getSourceJarFiles());
}
private List<File> getSourceJarFiles() {
List<File> sourceJarFiles = new ArrayList<>();
for (Configuration configuration : this.configurations) {
ResolutionResult resolutionResult = configuration.getIncoming().getResolutionResult();
resolutionResult.getRootComponent().get().getDependencies().forEach(dependency -> {
Set<ComponentArtifactsResult> artifactsResults = resolveSourceArtifacts(dependency);
for (ComponentArtifactsResult artifactResult : artifactsResults) {
artifactResult.getArtifacts(SourcesArtifact.class).forEach(sourceArtifact -> {
sourceJarFiles.add(((ResolvedArtifactResult) sourceArtifact).getFile());
});
}
});
}
return Collections.unmodifiableList(sourceJarFiles);
}
private Set<ComponentArtifactsResult> resolveSourceArtifacts(DependencyResult dependency) {
ModuleComponentSelector componentSelector = (ModuleComponentSelector) dependency.getRequested();
ArtifactResolutionQuery query = getProject().getDependencies().createArtifactResolutionQuery()
.forModule(componentSelector.getGroup(), componentSelector.getModule(), componentSelector.getVersion());
return executeQuery(query).getResolvedComponents();
}
@SuppressWarnings("unchecked")
private ArtifactResolutionResult executeQuery(ArtifactResolutionQuery query) {
return query.withArtifacts(JvmLibrary.class, SourcesArtifact.class).execute();
}
private void sync(List<File> sourceJarFiles) {
getProject().sync(spec -> {
spec.into(this.outputDirectory);
spec.eachFile(this::relocateFile);
spec.filter(this::transformContent);
spec.exclude("META-INF/**");
spec.setIncludeEmptyDirs(false);
sourceJarFiles.forEach(sourceJar -> spec.from(zipTree(sourceJar)));
});
}
private void relocateFile(FileCopyDetails details) {
String path = details.getPath();
for (Relocation relocation : this.relocations) {
path = relocation.relocatePath(path);
}
details.setPath(path);
}
private String transformContent(String content) {
for (Relocation relocation : this.relocations) {
content = relocation.transformContent(content);
}
return content;
}
private FileTree zipTree(File sourceJar) {
return getProject().zipTree(sourceJar);
}
/**
* A single relocation.
*/
static class Relocation {
private final String pattern;
private final String pathPattern;
private final String destination;
private final String pathDestination;
Relocation(String pattern, String destination) {
this.pattern = pattern;
this.pathPattern = pattern.replace('.', '/');
this.destination = destination;
this.pathDestination = destination.replace('.', '/');
}
@Input
public String getPattern() {
return this.pattern;
}
@Input
public String getDestination() {
return this.destination;
}
String relocatePath(String path) {
return path.replace(this.pathPattern, this.pathDestination);
}
public String transformContent(String content) {
return content.replaceAll("\\b" + this.pattern, this.destination);
}
}
}
@@ -0,0 +1 @@
implementation-class=org.springframework.build.gradle.DetectSplitPackagesPlugin
@@ -0,0 +1 @@
implementation-class=org.springframework.build.gradle.MergePlugin
@@ -0,0 +1 @@
implementation-class=org.springframework.build.gradle.TestSourceSetDependenciesPlugin
-57
View File
@@ -1,57 +0,0 @@
== Spring Framework Concourse pipeline
The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.0.x[Spring Framework 6.0.x].
=== Setting up your development environment
If you're part of the Spring Framework project on GitHub, you can get access to CI management features.
First, you need to go to https://ci.spring.io and install the client CLI for your platform (see bottom right of the screen).
You can then login with the instance using:
[source]
----
$ fly -t spring login -n spring-framework -c https://ci.spring.io
----
Once logged in, you should get something like:
[source]
----
$ fly ts
name url team expiry
spring https://ci.spring.io spring-framework Wed, 25 Mar 2020 17:45:26 UTC
----
=== Pipeline configuration and structure
The build pipelines are described in `pipeline.yml` file.
This file is listing Concourse resources, i.e. build inputs and outputs such as container images, artifact repositories, source repositories, notification services, etc.
It also describes jobs (a job is a sequence of inputs, tasks and outputs); jobs are organized by groups.
The `pipeline.yml` definition contains `((parameters))` which are loaded from the `parameters.yml` file or from our https://docs.cloudfoundry.org/credhub/[credhub instance].
You'll find in this folder the following resources:
* `pipeline.yml` the build pipeline
* `parameters.yml` the build parameters used for the pipeline
* `images/` holds the container images definitions used in this pipeline
* `scripts/` holds the build scripts that ship within the CI container images
* `tasks` contains the task definitions used in the main `pipeline.yml`
=== Updating the build pipeline
Updating files on the repository is not enough to update the build pipeline, as changes need to be applied.
The pipeline can be deployed using the following command:
[source]
----
$ fly -t spring set-pipeline -p spring-framework-6.0.x -c ci/pipeline.yml -l ci/parameters.yml
----
NOTE: This assumes that you have credhub integration configured with the appropriate secrets.
-20
View File
@@ -1,20 +0,0 @@
changelog:
repository: spring-projects/spring-framework
sections:
- title: ":star: New Features"
labels:
- "type: enhancement"
- title: ":lady_beetle: Bug Fixes"
labels:
- "type: bug"
- "type: regression"
- title: ":notebook_with_decorative_cover: Documentation"
labels:
- "type: documentation"
- title: ":hammer: Dependency Upgrades"
sort: "title"
labels:
- "type: dependency-upgrade"
contributors:
exclude:
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll"]
-10
View File
@@ -1,10 +0,0 @@
logging:
level:
io.spring.concourse: DEBUG
spring:
main:
banner-mode: off
sonatype:
exclude:
- 'build-info\.json'
- '.*\.zip'
-21
View File
@@ -1,21 +0,0 @@
== CI Images
These images are used by CI to run the actual builds.
To build the image locally run the following from this directory:
----
$ docker build --no-cache -f <image-folder>/Dockerfile .
----
For example
----
$ docker build --no-cache -f spring-framework-ci-image/Dockerfile .
----
To test run:
----
$ docker run -it --entrypoint /bin/bash <SHA>
----
-12
View File
@@ -1,12 +0,0 @@
FROM ubuntu:focal-20220922
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
RUN ./setup.sh
ENV JAVA_HOME /opt/openjdk/java17
ENV JDK17 /opt/openjdk/java17
ENV JDK18 /opt/openjdk/java18
ENV JDK19 /opt/openjdk/java19
ENV PATH $JAVA_HOME/bin:$PATH
-17
View File
@@ -1,17 +0,0 @@
#!/bin/bash
set -e
case "$1" in
java17)
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.5+8/bellsoft-jdk17.0.5+8-linux-amd64.tar.gz"
;;
java18)
echo "https://github.com/bell-sw/Liberica/releases/download/18.0.2.1%2B1/bellsoft-jdk18.0.2.1+1-linux-amd64.tar.gz"
;;
java19)
echo "https://github.com/bell-sw/Liberica/releases/download/19.0.1%2B11/bellsoft-jdk19.0.1+11-linux-amd64.tar.gz"
;;
*)
echo $"Unknown java version"
exit 1
esac
-40
View File
@@ -1,40 +0,0 @@
#!/bin/bash
set -ex
###########################################################
# UTILS
###########################################################
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install --no-install-recommends -y tzdata ca-certificates net-tools libxml2-utils git curl libudev1 libxml2-utils iptables iproute2 jq fontconfig
ln -fs /usr/share/zoneinfo/UTC /etc/localtime
dpkg-reconfigure --frontend noninteractive tzdata
rm -rf /var/lib/apt/lists/*
curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/concourse-java.sh > /opt/concourse-java.sh
###########################################################
# JAVA
###########################################################
mkdir -p /opt/openjdk
pushd /opt/openjdk > /dev/null
for jdk in java17 java18 java19
do
JDK_URL=$( /get-jdk-url.sh $jdk )
mkdir $jdk
pushd $jdk > /dev/null
curl -L ${JDK_URL} | tar zx --strip-components=1
test -f bin/java
test -f bin/javac
popd > /dev/null
done
popd
###########################################################
# GRADLE ENTERPRISE
###########################################################
cd /
mkdir ~/.gradle
echo 'systemProp.user.name=concourse' > ~/.gradle/gradle.properties
-14
View File
@@ -1,14 +0,0 @@
github-repo: "https://github.com/spring-projects/spring-framework.git"
github-repo-name: "spring-projects/spring-framework"
sonatype-staging-profile: "org.springframework"
docker-hub-organization: "springci"
artifactory-server: "https://repo.spring.io"
branch: "main"
milestone: "6.0.x"
build-name: "spring-framework"
pipeline-name: "spring-framework"
concourse-url: "https://ci.spring.io"
registry-mirror-host: docker.repo.spring.io
registry-mirror-username: ((artifactory-username))
registry-mirror-password: ((artifactory-password))
task-timeout: 1h00m
-480
View File
@@ -1,480 +0,0 @@
anchors:
git-repo-resource-source: &git-repo-resource-source
uri: ((github-repo))
username: ((github-username))
password: ((github-ci-release-token))
branch: ((branch))
gradle-enterprise-task-params: &gradle-enterprise-task-params
GRADLE_ENTERPRISE_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle_enterprise_cache_user.username))
GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle_enterprise_cache_user.password))
sonatype-task-params: &sonatype-task-params
SONATYPE_USERNAME: ((sonatype-username))
SONATYPE_PASSWORD: ((sonatype-password))
SONATYPE_URL: ((sonatype-url))
SONATYPE_STAGING_PROFILE: ((sonatype-staging-profile))
artifactory-task-params: &artifactory-task-params
ARTIFACTORY_SERVER: ((artifactory-server))
ARTIFACTORY_USERNAME: ((artifactory-username))
ARTIFACTORY_PASSWORD: ((artifactory-password))
build-project-task-params: &build-project-task-params
BRANCH: ((branch))
<<: *gradle-enterprise-task-params
docker-resource-source: &docker-resource-source
username: ((docker-hub-username))
password: ((docker-hub-password))
tag: ((milestone))
registry-mirror-vars: &registry-mirror-vars
registry-mirror-host: ((registry-mirror-host))
registry-mirror-username: ((registry-mirror-username))
registry-mirror-password: ((registry-mirror-password))
slack-fail-params: &slack-fail-params
text: >
:concourse-failed: <https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!>
[$TEXT_FILE_CONTENT]
text_file: git-repo/build/build-scan-uri.txt
silent: true
icon_emoji: ":concourse:"
username: concourse-ci
changelog-task-params: &changelog-task-params
name: generated-changelog/tag
tag: generated-changelog/tag
body: generated-changelog/changelog.md
github-task-params: &github-task-params
GITHUB_USERNAME: ((github-username))
GITHUB_TOKEN: ((github-ci-release-token))
resource_types:
- name: registry-image
type: registry-image
source:
repository: concourse/registry-image-resource
tag: 1.5.0
- name: artifactory-resource
type: registry-image
source:
repository: springio/artifactory-resource
tag: 0.0.17
- name: github-release
type: registry-image
source:
repository: concourse/github-release-resource
tag: 1.5.5
- name: github-status-resource
type: registry-image
source:
repository: dpb587/github-status-resource
tag: master
- name: pull-request
type: registry-image
source:
repository: teliaoss/github-pr-resource
tag: v0.23.0
- name: slack-notification
type: registry-image
source:
repository: cfcommunity/slack-notification-resource
tag: latest
resources:
- name: git-repo
type: git
icon: github
source:
<<: *git-repo-resource-source
- name: ci-images-git-repo
type: git
icon: github
source:
uri: ((github-repo))
branch: ((branch))
paths: ["ci/images/*"]
- name: ci-image
type: registry-image
icon: docker
source:
<<: *docker-resource-source
repository: ((docker-hub-organization))/spring-framework-ci
- name: every-morning
type: time
icon: alarm
source:
start: 8:00 AM
stop: 9:00 AM
location: Europe/Vienna
- name: artifactory-repo
type: artifactory-resource
icon: package-variant
source:
uri: ((artifactory-server))
username: ((artifactory-username))
password: ((artifactory-password))
build_name: ((build-name))
- name: git-pull-request
type: pull-request
icon: source-pull
source:
access_token: ((github-ci-pull-request-token))
repository: ((github-repo-name))
base_branch: ((branch))
ignore_paths: ["ci/*"]
- name: repo-status-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: build
- name: repo-status-jdk18-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: jdk18-build
- name: repo-status-jdk19-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: jdk19-build
- name: slack-alert
type: slack-notification
icon: slack
source:
url: ((slack-webhook-url))
- name: github-pre-release
type: github-release
icon: briefcase-download-outline
source:
owner: spring-projects
repository: spring-framework
access_token: ((github-ci-release-token))
pre_release: true
release: false
- name: github-release
type: github-release
icon: briefcase-download
source:
owner: spring-projects
repository: spring-framework
access_token: ((github-ci-release-token))
pre_release: false
jobs:
- name: build-ci-images
plan:
- get: git-repo
- get: ci-images-git-repo
trigger: true
- task: build-ci-image
privileged: true
file: git-repo/ci/tasks/build-ci-image.yml
output_mapping:
image: ci-image
vars:
ci-image-name: ci-image
<<: *registry-mirror-vars
- put: ci-image
params:
image: ci-image/image.tar
- name: build
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
trigger: true
- put: repo-status-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: build-project
image: ci-image
file: git-repo/ci/tasks/build-project.yml
privileged: true
timeout: ((task-timeout))
params:
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-build
params: { state: "success", commit: "git-repo" }
- put: artifactory-repo
params: &artifactory-params
signing_key: ((signing-key))
signing_passphrase: ((signing-passphrase))
repo: libs-snapshot-local
folder: distribution-repository
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
build_number: "${BUILD_PIPELINE_NAME}-${BUILD_JOB_NAME}-${BUILD_NAME}"
disable_checksum_uploads: true
threads: 8
artifact_set:
- include:
- "/**/framework-docs-*.zip"
properties:
"zip.name": "spring-framework"
"zip.displayname": "Spring Framework"
"zip.deployed": "false"
- include:
- "/**/framework-docs-*-docs.zip"
properties:
"zip.type": "docs"
- include:
- "/**/framework-docs-*-dist.zip"
properties:
"zip.type": "dist"
- include:
- "/**/framework-docs-*-schema.zip"
properties:
"zip.type": "schema"
get_params:
threads: 8
- name: jdk18-build
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
- get: every-morning
trigger: true
- put: repo-status-jdk18-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: check-project
image: ci-image
file: git-repo/ci/tasks/check-project.yml
privileged: true
timeout: ((task-timeout))
params:
TEST_TOOLCHAIN: 18
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk18-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-jdk18-build
params: { state: "success", commit: "git-repo" }
- name: jdk19-build
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
- get: every-morning
trigger: true
- put: repo-status-jdk19-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: check-project
image: ci-image
file: git-repo/ci/tasks/check-project.yml
privileged: true
timeout: ((task-timeout))
params:
TEST_TOOLCHAIN: 19
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk19-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-jdk19-build
params: { state: "success", commit: "git-repo" }
- name: build-pull-requests
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
resource: git-pull-request
trigger: true
version: every
- do:
- put: git-pull-request
params:
path: git-repo
status: pending
- task: build-pr
image: ci-image
file: git-repo/ci/tasks/build-pr.yml
privileged: true
timeout: ((task-timeout))
params:
BRANCH: ((branch))
on_success:
put: git-pull-request
params:
path: git-repo
status: success
on_failure:
put: git-pull-request
params:
path: git-repo
status: failure
- name: stage-milestone
serial: true
plan:
- get: ci-image
- get: git-repo
trigger: false
- task: stage
image: ci-image
file: git-repo/ci/tasks/stage-version.yml
params:
RELEASE_TYPE: M
<<: *gradle-enterprise-task-params
- put: artifactory-repo
params:
<<: *artifactory-params
repo: libs-staging-local
- put: git-repo
params:
repository: stage-git-repo
- name: promote-milestone
serial: true
plan:
- get: ci-image
- get: git-repo
trigger: false
- get: artifactory-repo
trigger: false
passed: [stage-milestone]
params:
download_artifacts: false
save_build_info: true
- task: promote
file: git-repo/ci/tasks/promote-version.yml
params:
RELEASE_TYPE: M
<<: *artifactory-task-params
- task: generate-changelog
file: git-repo/ci/tasks/generate-changelog.yml
params:
RELEASE_TYPE: M
<<: *github-task-params
- put: github-pre-release
params:
<<: *changelog-task-params
- name: stage-rc
serial: true
plan:
- get: ci-image
- get: git-repo
trigger: false
- task: stage
image: ci-image
file: git-repo/ci/tasks/stage-version.yml
params:
RELEASE_TYPE: RC
<<: *gradle-enterprise-task-params
- put: artifactory-repo
params:
<<: *artifactory-params
repo: libs-staging-local
- put: git-repo
params:
repository: stage-git-repo
- name: promote-rc
serial: true
plan:
- get: ci-image
- get: git-repo
trigger: false
- get: artifactory-repo
trigger: false
passed: [stage-rc]
params:
download_artifacts: false
save_build_info: true
- task: promote
file: git-repo/ci/tasks/promote-version.yml
params:
RELEASE_TYPE: RC
<<: *artifactory-task-params
- task: generate-changelog
file: git-repo/ci/tasks/generate-changelog.yml
params:
RELEASE_TYPE: RC
<<: *github-task-params
- put: github-pre-release
params:
<<: *changelog-task-params
- name: stage-release
serial: true
plan:
- get: ci-image
- get: git-repo
trigger: false
- task: stage
image: ci-image
file: git-repo/ci/tasks/stage-version.yml
params:
RELEASE_TYPE: RELEASE
<<: *gradle-enterprise-task-params
- put: artifactory-repo
params:
<<: *artifactory-params
repo: libs-staging-local
- put: git-repo
params:
repository: stage-git-repo
- name: promote-release
serial: true
plan:
- get: ci-image
- get: git-repo
trigger: false
- get: artifactory-repo
trigger: false
passed: [stage-release]
params:
download_artifacts: true
save_build_info: true
- task: promote
file: git-repo/ci/tasks/promote-version.yml
params:
RELEASE_TYPE: RELEASE
<<: *artifactory-task-params
<<: *sonatype-task-params
- name: create-github-release
serial: true
plan:
- get: ci-image
- get: git-repo
- get: artifactory-repo
trigger: true
passed: [promote-release]
params:
download_artifacts: false
save_build_info: true
- task: generate-changelog
file: git-repo/ci/tasks/generate-changelog.yml
params:
RELEASE_TYPE: RELEASE
<<: *github-task-params
- put: github-release
params:
<<: *changelog-task-params
groups:
- name: "builds"
jobs: ["build", "jdk18-build", "jdk19-build"]
- name: "releases"
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
- name: "ci-images"
jobs: ["build-ci-images"]
- name: "pull-requests"
jobs: [ "build-pull-requests" ]
-8
View File
@@ -1,8 +0,0 @@
#!/bin/bash
set -e
source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 check
popd > /dev/null
-9
View File
@@ -1,9 +0,0 @@
#!/bin/bash
set -e
source $(dirname $0)/common.sh
repository=$(pwd)/distribution-repository
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
popd > /dev/null
-9
View File
@@ -1,9 +0,0 @@
#!/bin/bash
set -e
source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK18 \
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check
popd > /dev/null
-2
View File
@@ -1,2 +0,0 @@
source /opt/concourse-java.sh
setup_symlinks
-12
View File
@@ -1,12 +0,0 @@
#!/bin/bash
set -e
CONFIG_DIR=git-repo/ci/config
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
java -jar /github-changelog-generator.jar \
--spring.config.location=${CONFIG_DIR}/changelog-generator.yml \
${version} generated-changelog/changelog.md
echo ${version} > generated-changelog/version
echo v${version} > generated-changelog/tag
-17
View File
@@ -1,17 +0,0 @@
#!/bin/bash
CONFIG_DIR=git-repo/ci/config
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
export BUILD_INFO_LOCATION=$(pwd)/artifactory-repo/build-info.json
java -jar /concourse-release-scripts.jar \
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
publishToCentral $RELEASE_TYPE $BUILD_INFO_LOCATION artifactory-repo || { exit 1; }
java -jar /concourse-release-scripts.jar \
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
promote $RELEASE_TYPE $BUILD_INFO_LOCATION || { exit 1; }
echo "Promotion complete"
echo $version > version/version
-50
View File
@@ -1,50 +0,0 @@
#!/bin/bash
set -e
source $(dirname $0)/common.sh
repository=$(pwd)/distribution-repository
pushd git-repo > /dev/null
git fetch --tags --all > /dev/null
popd > /dev/null
git clone git-repo stage-git-repo > /dev/null
pushd stage-git-repo > /dev/null
snapshotVersion=$( awk -F '=' '$1 == "version" { print $2 }' gradle.properties )
if [[ $RELEASE_TYPE = "M" ]]; then
stageVersion=$( get_next_milestone_release $snapshotVersion)
nextVersion=$snapshotVersion
elif [[ $RELEASE_TYPE = "RC" ]]; then
stageVersion=$( get_next_rc_release $snapshotVersion)
nextVersion=$snapshotVersion
elif [[ $RELEASE_TYPE = "RELEASE" ]]; then
stageVersion=$( get_next_release $snapshotVersion)
nextVersion=$( bump_version_number $snapshotVersion)
else
echo "Unknown release type $RELEASE_TYPE" >&2; exit 1;
fi
echo "Staging $stageVersion (next version will be $nextVersion)"
sed -i "s/version=$snapshotVersion/version=$stageVersion/" gradle.properties
git config user.name "Spring Builds" > /dev/null
git config user.email "spring-builds@users.noreply.github.com" > /dev/null
git add gradle.properties > /dev/null
git commit -m"Release v$stageVersion" > /dev/null
git tag -a "v$stageVersion" -m"Release v$stageVersion" > /dev/null
./gradlew --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
git reset --hard HEAD^ > /dev/null
if [[ $nextVersion != $snapshotVersion ]]; then
echo "Setting next development version (v$nextVersion)"
sed -i "s/version=$snapshotVersion/version=$nextVersion/" gradle.properties
git add gradle.properties > /dev/null
git commit -m"Next development version (v$nextVersion)" > /dev/null
fi;
echo "Staging Complete"
popd > /dev/null
-32
View File
@@ -1,32 +0,0 @@
---
platform: linux
image_resource:
type: registry-image
source:
repository: concourse/oci-build-task
tag: 0.10.0
registry_mirror:
host: ((registry-mirror-host))
username: ((registry-mirror-username))
password: ((registry-mirror-password))
inputs:
- name: ci-images-git-repo
outputs:
- name: image
caches:
- path: ci-image-cache
params:
CONTEXT: ci-images-git-repo/ci/images
DOCKERFILE: ci-images-git-repo/ci/images/ci-image/Dockerfile
DOCKER_HUB_AUTH: ((docker-hub-auth))
run:
path: /bin/sh
args:
- "-c"
- |
mkdir -p /root/.docker
cat > /root/.docker/config.json <<EOF
{ "auths": { "https://index.docker.io/v1/": { "auth": "$DOCKER_HUB_AUTH" }}}
EOF
build
-19
View File
@@ -1,19 +0,0 @@
---
platform: linux
inputs:
- name: git-repo
caches:
- path: gradle
params:
BRANCH:
CI: true
GRADLE_ENTERPRISE_ACCESS_KEY:
GRADLE_ENTERPRISE_CACHE_USERNAME:
GRADLE_ENTERPRISE_CACHE_PASSWORD:
GRADLE_ENTERPRISE_URL: https://ge.spring.io
run:
path: bash
args:
- -ec
- |
${PWD}/git-repo/ci/scripts/build-pr.sh
-22
View File
@@ -1,22 +0,0 @@
---
platform: linux
inputs:
- name: git-repo
outputs:
- name: distribution-repository
- name: git-repo
caches:
- path: gradle
params:
BRANCH:
CI: true
GRADLE_ENTERPRISE_ACCESS_KEY:
GRADLE_ENTERPRISE_CACHE_USERNAME:
GRADLE_ENTERPRISE_CACHE_PASSWORD:
GRADLE_ENTERPRISE_URL: https://ge.spring.io
run:
path: bash
args:
- -ec
- |
${PWD}/git-repo/ci/scripts/build-project.sh
-24
View File
@@ -1,24 +0,0 @@
---
platform: linux
inputs:
- name: git-repo
outputs:
- name: distribution-repository
- name: git-repo
caches:
- path: gradle
params:
BRANCH:
CI: true
MAIN_TOOLCHAIN:
TEST_TOOLCHAIN:
GRADLE_ENTERPRISE_ACCESS_KEY:
GRADLE_ENTERPRISE_CACHE_USERNAME:
GRADLE_ENTERPRISE_CACHE_PASSWORD:
GRADLE_ENTERPRISE_URL: https://ge.spring.io
run:
path: bash
args:
- -ec
- |
${PWD}/git-repo/ci/scripts/check-project.sh
-20
View File
@@ -1,20 +0,0 @@
---
platform: linux
image_resource:
type: registry-image
source:
repository: springio/github-changelog-generator
tag: '0.0.8'
inputs:
- name: git-repo
- name: artifactory-repo
outputs:
- name: generated-changelog
params:
GITHUB_ORGANIZATION:
GITHUB_REPO:
GITHUB_USERNAME:
GITHUB_TOKEN:
RELEASE_TYPE:
run:
path: git-repo/ci/scripts/generate-changelog.sh
-23
View File
@@ -1,23 +0,0 @@
---
platform: linux
image_resource:
type: registry-image
source:
repository: springio/concourse-release-scripts
tag: '0.3.4'
inputs:
- name: git-repo
- name: artifactory-repo
outputs:
- name: version
params:
RELEASE_TYPE:
ARTIFACTORY_SERVER:
ARTIFACTORY_USERNAME:
ARTIFACTORY_PASSWORD:
SONATYPE_USER:
SONATYPE_PASSWORD:
SONATYPE_URL:
SONATYPE_STAGING_PROFILE:
run:
path: git-repo/ci/scripts/promote-version.sh
-17
View File
@@ -1,17 +0,0 @@
---
platform: linux
inputs:
- name: git-repo
outputs:
- name: stage-git-repo
- name: distribution-repository
params:
RELEASE_TYPE:
CI: true
GRADLE_ENTERPRISE_CACHE_USERNAME:
GRADLE_ENTERPRISE_CACHE_PASSWORD:
GRADLE_ENTERPRISE_URL: https://ge.spring.io
caches:
- path: gradle
run:
path: git-repo/ci/scripts/stage-version.sh
-23
View File
@@ -1,23 +0,0 @@
description = "Spring Framework (Bill of Materials)"
apply plugin: 'java-platform'
apply from: "$rootDir/gradle/publications.gradle"
group = "org.springframework"
dependencies {
constraints {
parent.moduleProjects.sort { "$it.name" }.each {
api it
}
}
}
publishing {
publications {
mavenJava(MavenPublication) {
artifactId = 'spring-framework-bom'
from components.javaPlatform
}
}
}
-260
View File
@@ -1,260 +0,0 @@
description = "Spring Framework Docs"
apply plugin: 'kotlin'
apply plugin: 'org.asciidoctor.jvm.convert'
apply plugin: 'org.asciidoctor.jvm.pdf'
apply from: "${rootDir}/gradle/publications.gradle"
configurations {
asciidoctorExtensions
}
dependencies {
api(project(":spring-context"))
api(project(":spring-web"))
api("jakarta.servlet:jakarta.servlet-api")
implementation(project(":spring-core-test"))
implementation("org.assertj:assertj-core")
}
jar {
enabled = false
}
javadoc {
enabled = false
}
dependencies {
asciidoctorExtensions "io.spring.asciidoctor.backends:spring-asciidoctor-backends:0.0.3"
}
repositories {
maven {
url "https://repo.spring.io/release"
}
}
/**
* Produce Javadoc for all Spring Framework modules in "build/docs/javadoc"
*/
task api(type: Javadoc) {
group = "Documentation"
description = "Generates aggregated Javadoc API documentation."
title = "${rootProject.description} ${version} API"
dependsOn {
moduleProjects.collect {
it.tasks.getByName("jar")
}
}
doFirst {
classpath = files(
// ensure the javadoc process can resolve types compiled from .aj sources
project(":spring-aspects").sourceSets.main.output
)
classpath += files(moduleProjects.collect { it.sourceSets.main.compileClasspath })
}
options {
encoding = "UTF-8"
memberLevel = JavadocMemberLevel.PROTECTED
author = true
header = rootProject.description
use = true
overview = "framework-docs/src/docs/api/overview.html"
splitIndex = true
links(project.ext.javadocLinks)
addBooleanOption('Xdoclint:syntax', true) // only check syntax with doclint
addBooleanOption('Werror', true) // fail build on Javadoc warnings
}
source moduleProjects.collect { project ->
project.sourceSets.main.allJava
}
maxMemory = "1024m"
destinationDir = file("$buildDir/docs/javadoc")
}
/**
* Produce KDoc for all Spring Framework modules in "build/docs/kdoc"
*/
rootProject.tasks.dokkaHtmlMultiModule.configure {
dependsOn {
tasks.getByName("api")
}
moduleName.set("spring-framework")
outputDirectory.set(project.file("$buildDir/docs/kdoc"))
}
asciidoctorj {
version = '2.4.3'
fatalWarnings ".*"
options doctype: 'book', eruby: 'erubis'
attributes([
icons: 'font',
idprefix: '',
idseparator: '-',
revnumber: project.version,
sectanchors: '',
sectnums: '',
'spring-version': project.version
])
}
/**
* Generate the Spring Framework Reference documentation from
* "src/docs/asciidoc" in "build/docs/ref-docs/html5".
*/
asciidoctor {
baseDirFollowsSourceDir()
configurations "asciidoctorExtensions"
sources {
include '*.adoc'
}
resources {
from(sourceDir) {
include 'images/*.png'
}
}
outputDir "$buildDir/docs/ref-docs/html5"
outputOptions {
backends "spring-html"
}
forkOptions {
jvmArgs += ["--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED"]
}
logDocuments = true
}
asciidoctor.mustRunAfter "check"
/**
* Generate the Spring Framework Reference documentation from "src/docs/asciidoc"
* in "build/docs/ref-docs/pdf".
*/
asciidoctorPdf {
baseDirFollowsSourceDir()
configurations 'asciidoctorExtensions'
sources {
include 'spring-framework.adocbook'
}
outputDir "$buildDir/docs/ref-docs/pdf"
forkOptions {
jvmArgs += ["--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED"]
}
logDocuments = true
}
/**
* Zip all docs (API and reference) into a single archive
*/
task docsZip(type: Zip, dependsOn: ['api', 'asciidoctor', 'asciidoctorPdf', rootProject.tasks.dokkaHtmlMultiModule]) {
group = "Distribution"
description = "Builds -${archiveClassifier} archive containing api and reference " +
"for deployment at https://docs.spring.io/spring-framework/docs/."
archiveBaseName.set("spring-framework")
archiveClassifier.set("docs")
from("src/dist") {
include "changelog.txt"
}
from (api) {
into "javadoc-api"
}
from ("$asciidoctor.outputDir") {
into "reference/html"
}
from ("$asciidoctorPdf.outputDir") {
into "reference/pdf"
}
from (rootProject.tasks.dokkaHtmlMultiModule.outputDirectory) {
into "kdoc-api"
}
}
/**
* Zip all Spring Framework schemas into a single archive
*/
task schemaZip(type: Zip) {
group = "Distribution"
archiveBaseName.set("spring-framework")
archiveClassifier.set("schema")
description = "Builds -${archiveClassifier} archive containing all " +
"XSDs for deployment at https://springframework.org/schema."
duplicatesStrategy DuplicatesStrategy.EXCLUDE
moduleProjects.each { module ->
def Properties schemas = new Properties();
module.sourceSets.main.resources.find {
(it.path.endsWith("META-INF/spring.schemas") || it.path.endsWith("META-INF\\spring.schemas"))
}?.withInputStream { schemas.load(it) }
for (def key : schemas.keySet()) {
def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1')
assert shortName != key
File xsdFile = module.sourceSets.main.resources.find {
(it.path.endsWith(schemas.get(key)) || it.path.endsWith(schemas.get(key).replaceAll('\\/','\\\\')))
}
assert xsdFile != null
into (shortName) {
from xsdFile.path
}
}
}
}
/**
* Create a distribution zip with everything:
* docs, schemas, jars, source jars, javadoc jars
*/
task distZip(type: Zip, dependsOn: [docsZip, schemaZip]) {
group = "Distribution"
archiveBaseName.set("spring-framework")
archiveClassifier.set("dist")
description = "Builds -${archiveClassifier} archive, containing all jars and docs, " +
"suitable for community download page."
ext.baseDir = "spring-framework-${project.version}";
from("src/docs/dist") {
include "readme.txt"
include "license.txt"
include "notice.txt"
into "${baseDir}"
expand(copyright: new Date().format("yyyy"), version: project.version)
}
from(zipTree(docsZip.archiveFile)) {
into "${baseDir}/docs"
}
from(zipTree(schemaZip.archiveFile)) {
into "${baseDir}/schema"
}
moduleProjects.each { module ->
into ("${baseDir}/libs") {
from module.jar
if (module.tasks.findByPath("sourcesJar")) {
from module.sourcesJar
}
if (module.tasks.findByPath("javadocJar")) {
from module.javadocJar
}
}
}
}
distZip.mustRunAfter moduleProjects.check
publishing {
publications {
mavenJava(MavenPublication) {
artifact docsZip
artifact schemaZip
artifact distZip
}
}
}
@@ -1,7 +0,0 @@
<html>
<body>
<p>
This is the public API documentation for the <a href="https://github.com/spring-projects/spring-framework" target="_top">Spring Framework</a>.
</p>
</body>
</html>
@@ -1,80 +0,0 @@
[[appendix]]
= Appendix
include::attributes.adoc[]
include::page-layout.adoc[]
This part of the reference documentation covers topics that apply to multiple modules
within the core Spring Framework.
[[appendix-spring-properties]]
== Spring Properties
{api-spring-framework}/core/SpringProperties.html[`SpringProperties`] is a static holder
for properties that control certain low-level aspects of the Spring Framework. Users can
configure these properties via JVM system properties or programmatically via the
`SpringProperties.setProperty(String key, String value)` method. The latter may be
necessary if the deployment environment disallows custom JVM system properties. As an
alternative, these properties may be configured in a `spring.properties` file in the root
of the classpath -- for example, deployed within the application's JAR file.
The following table lists all currently supported Spring properties.
.Supported Spring Properties
|===
| Name | Description
| `spring.beaninfo.ignore`
| Instructs Spring to use the `Introspector.IGNORE_ALL_BEANINFO` mode when calling the
JavaBeans `Introspector`. See
{api-spring-framework}++/beans/CachedIntrospectionResults.html#IGNORE_BEANINFO_PROPERTY_NAME++[`CachedIntrospectionResults`]
for details.
| `spring.expression.compiler.mode`
| The mode to use when compiling expressions for the
<<core.adoc#expressions-compiler-configuration, Spring Expression Language>>.
| `spring.getenv.ignore`
| Instructs Spring to ignore operating system environment variables if a Spring
`Environment` property -- for example, a placeholder in a configuration String -- isn't
resolvable otherwise. See
{api-spring-framework}++/core/env/AbstractEnvironment.html#IGNORE_GETENV_PROPERTY_NAME++[`AbstractEnvironment`]
for details.
| `spring.index.ignore`
| Instructs Spring to ignore the components index located in
`META-INF/spring.components`. See <<core.adoc#beans-scanning-index, Generating an Index
of Candidate Components>>.
| `spring.jdbc.getParameterType.ignore`
| Instructs Spring to ignore `java.sql.ParameterMetaData.getParameterType` completely.
See the note in <<data-access.adoc#jdbc-batch-list, Batch Operations with a List of Objects>>.
| `spring.jndi.ignore`
| Instructs Spring to ignore a default JNDI environment, as an optimization for scenarios
where nothing is ever to be found for such JNDI fallback searches to begin with, avoiding
the repeated JNDI lookup overhead. See
{api-spring-framework}++/jndi/JndiLocatorDelegate.html#IGNORE_JNDI_PROPERTY_NAME++[`JndiLocatorDelegate`]
for details.
| `spring.objenesis.ignore`
| Instructs Spring to ignore Objenesis, not even attempting to use it. See
{api-spring-framework}++/objenesis/SpringObjenesis.html#IGNORE_OBJENESIS_PROPERTY_NAME++[`SpringObjenesis`]
for details.
| `spring.test.constructor.autowire.mode`
| The default _test constructor autowire mode_ to use if `@TestConstructor` is not present
on a test class. See <<testing.adoc#integration-testing-annotations-testconstructor,
Changing the default test constructor autowire mode>>.
| `spring.test.context.cache.maxSize`
| The maximum size of the context cache in the _Spring TestContext Framework_. See
<<testing.adoc#testcontext-ctx-management-caching, Context Caching>>.
| `spring.test.enclosing.configuration`
| The default _enclosing configuration inheritance mode_ to use if
`@NestedTestConfiguration` is not present on a test class. See
<<testing.adoc#integration-testing-annotations-nestedtestconfiguration, Changing the
default enclosing configuration inheritance mode>>.
|===
@@ -1,18 +0,0 @@
:chomp: default headers packages
:docs-site: https://docs.spring.io
// Spring Framework
:docs-spring-framework: {docs-site}/spring-framework/docs/{spring-version}
:api-spring-framework: {docs-spring-framework}/javadoc-api/org/springframework
:docs-java: {docdir}/../../main/java/org/springframework/docs
:docs-kotlin: {docdir}/../../main/kotlin/org/springframework/docs
:docs-resources: {docdir}/../../main/resources
:spring-framework-main-code: https://github.com/spring-projects/spring-framework/tree/main
// Spring portfolio Links
:docs-spring-boot: {docs-site}/spring-boot/docs/current/reference
:docs-spring-gemfire: {docs-site}/spring-gemfire/docs/current/reference
:docs-spring-security: {docs-site}/spring-security/reference
// Third-party Links
:docs-graalvm: https://www.graalvm.org/22.3/reference-manual
:gh-rsocket: https://github.com/rsocket
:gh-rsocket-extensions: {gh-rsocket}/rsocket/blob/master/Extensions
:gh-rsocket-java: {gh-rsocket}/rsocket-java
@@ -1,43 +0,0 @@
[[spring-core]]
= Core Technologies
include::attributes.adoc[]
include::page-layout.adoc[]
This part of the reference documentation covers all the technologies that are
absolutely integral to the Spring Framework.
Foremost amongst these is the Spring Framework's Inversion of Control (IoC) container.
A thorough treatment of the Spring Framework's IoC container is closely followed by
comprehensive coverage of Spring's Aspect-Oriented Programming (AOP) technologies.
The Spring Framework has its own AOP framework, which is conceptually easy to
understand and which successfully addresses the 80% sweet spot of AOP requirements
in Java enterprise programming.
Coverage of Spring's integration with AspectJ (currently the richest -- in terms of
features -- and certainly most mature AOP implementation in the Java enterprise space)
is also provided.
AOT processing can be used to optimize your application ahead-of-time. It is typically
used for native image deployment using GraalVM.
include::core/core-beans.adoc[leveloffset=+1]
include::core/core-resources.adoc[leveloffset=+1]
include::core/core-validation.adoc[leveloffset=+1]
include::core/core-expressions.adoc[leveloffset=+1]
include::core/core-aop.adoc[leveloffset=+1]
include::core/core-aop-api.adoc[leveloffset=+1]
include::core/core-null-safety.adoc[leveloffset=+1]
include::core/core-databuffer-codec.adoc[leveloffset=+1]
include::core/core-spring-jcl.adoc[leveloffset=+1]
include::core/core-aot.adoc[leveloffset=+1]
include::core/core-appendix.adoc[leveloffset=+1]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,307 +0,0 @@
[[core.aot]]
= Ahead of Time Optimizations
This chapter covers Spring's Ahead of Time (AOT) optimizations.
For AOT support specific to integration tests, see <<testing.adoc#testcontext-aot, Ahead of Time Support for Tests>>.
[[core.aot.introduction]]
== Introduction to Ahead of Time Optimizations
Spring's support for AOT optimizations is meant to inspect an `ApplicationContext` at build time and apply decisions and discovery logic that usually happens at runtime.
Doing so allows building an application startup arrangement that is more straightforward and focused on a fixed set of features based mainly on the classpath and the `Environment`.
Applying such optimizations early implies the following restrictions:
* The classpath is fixed and fully defined at build time.
* The beans defined in your application cannot change at runtime, meaning:
** `@Profile`, in particular profile-specific configuration needs to be chosen at build time.
** Environment properties that impact the presence of a bean (`@Conditional`) are only considered at build time.
When these restrictions are in place, it becomes possible to perform ahead-of-time processing at build time and generate additional assets.
A Spring AOT processed application typically generates:
* Java source code
* Bytecode (usually for dynamic proxies)
* {api-spring-framework}/aot/hint/RuntimeHints.html[`RuntimeHints`] for the use of reflection, resource loading, serialization, and JDK proxies.
NOTE: At the moment, AOT is focused on allowing Spring applications to be deployed as native images using GraalVM.
We intend to support more JVM-based use cases in future generations.
[[core.aot.basics]]
== AOT engine overview
The entry point of the AOT engine for processing an `ApplicationContext` arrangement is `ApplicationContextAotGenerator`. It takes care of the following steps, based on a `GenericApplicationContext` that represents the application to optimize and a {api-spring-framework}/aot/generate/GenerationContext.html[`GenerationContext`]:
* Refresh an `ApplicationContext` for AOT processing. Contrary to a traditional refresh, this version only creates bean definitions, not bean instances.
* Invoke the available `BeanFactoryInitializationAotProcessor` implementations and apply their contributions against the `GenerationContext`.
For instance, a core implementation iterates over all candidate bean definitions and generates the necessary code to restore the state of the `BeanFactory`.
Once this process completes, the `GenerationContext` will have been updated with the generated code, resources, and classes that are necessary for the application to run.
The `RuntimeHints` instance can also be used to generate the relevant GraalVM native image configuration files.
`ApplicationContextAotGenerator#processAheadOfTime` returns the class name of the `ApplicationContextInitializer` entry point that allows the context to be started with AOT optimizations.
Those steps are covered in greater detail in the sections below.
[[core.aot.refresh]]
== Refresh for AOT Processing
Refresh for AOT processing is supported on all `GenericApplicationContext` implementations.
An application context is created with any number of entry points, usually in the form of `@Configuration`-annotated classes.
Let's look at a basic example:
include::code:AotProcessingSample[tag=myapplication]
Starting this application with the regular runtime involves a number of steps including classpath scanning, configuration class parsing, bean instantiation, and lifecycle callback handling.
Refresh for AOT processing only applies a subset of what happens with a <<beans-introduction,regular `refresh`>>.
AOT processing can be triggered as follows:
include::code:AotProcessingSample[tag=aotcontext]
In this mode, <<beans-factory-extension-factory-postprocessors,`BeanFactoryPostProcessor` implementations>> are invoked as usual.
This includes configuration class parsing, import selectors, classpath scanning, etc.
Such steps make sure that the `BeanRegistry` contains the relevant bean definitions for the application.
If bean definitions are guarded by conditions (such as `@Profile`), these are discarded at this stage.
Because this mode does not actually create bean instances, `BeanPostProcessor` implementations are not invoked, except for specific variants that are relevant for AOT processing.
These are:
* `MergedBeanDefinitionPostProcessor` implementations post-process bean definitions to extract additional settings, such as `init` and `destroy` methods.
* `SmartInstantiationAwareBeanPostProcessor` implementations determine a more precise bean type if necessary.
This makes sure to create any proxy that will be required at runtime.
One this part completes, the `BeanFactory` contains the bean definitions that are necessary for the application to run. It does not trigger bean instantiation but allows the AOT engine to inspect the beans that will be created at runtime.
[[core.aot.bean-factory-initialization-contributions]]
== Bean Factory Initialization AOT Contributions
Components that want to participate in this step can implement the {api-spring-framework}/beans/factory/aot/BeanFactoryInitializationAotProcessor.html[`BeanFactoryInitializationAotProcessor`] interface.
Each implementation can return an AOT contribution, based on the state of the bean factory.
An AOT contribution is a component that contributes generated code that reproduces a particular behavior.
It can also contribute `RuntimeHints` to indicate the need for reflection, resource loading, serialization, or JDK proxies.
A `BeanFactoryInitializationAotProcessor` implementation can be registered in `META-INF/spring/aot.factories` with a key equal to the fully qualified name of the interface.
A `BeanFactoryInitializationAotProcessor` can also be implemented directly by a bean.
In this mode, the bean provides an AOT contribution equivalent to the feature it provides with a regular runtime.
Consequently, such a bean is automatically excluded from the AOT-optimized context.
[NOTE]
====
If a bean implements the `BeanFactoryInitializationAotProcessor` interface, the bean and **all** of its dependencies will be initialized during AOT processing.
We generally recommend that this interface is only implemented by infrastructure beans such as `BeanFactoryPostProcessor` which have limited dependencies and are already initialized early in the bean factory lifecycle.
If such a bean is registered using an `@Bean` factory method, ensure the method is `static` so that its enclosing `@Configuration` class does not have to be initialized.
====
[[core.aot.bean-registration-contributions]]
=== Bean Registration AOT Contributions
A core `BeanFactoryInitializationAotProcessor` implementation is responsible for collecting the necessary contributions for each candidate `BeanDefinition`.
It does so using a dedicated `BeanRegistrationAotProcessor`.
This interface is used as follows:
* Implemented by a `BeanPostProcessor` bean, to replace its runtime behavior.
For instance <<beans-factory-extension-bpp-examples-aabpp,`AutowiredAnnotationBeanPostProcessor`>> implements this interface to generate code that injects members annotated with `@Autowired`.
* Implemented by a type registered in `META-INF/spring/aot.factories` with a key equal to the fully qualified name of the interface.
Typically used when the bean definition needs to be tuned for specific features of the core framework.
[NOTE]
====
If a bean implements the `BeanRegistrationAotProcessor` interface, the bean and **all** of its dependencies will be initialized during AOT processing.
We generally recommend that this interface is only implemented by infrastructure beans such as `BeanFactoryPostProcessor` which have limited dependencies and are already initialized early in the bean factory lifecycle.
If such a bean is registered using an `@Bean` factory method, ensure the method is `static` so that its enclosing `@Configuration` class does not have to be initialized.
====
If no `BeanRegistrationAotProcessor` handles a particular registered bean, a default implementation processes it.
This is the default behavior, since tuning the generated code for a bean definition should be restricted to corner cases.
Taking our previous example, let's assume that `DataSourceConfiguration` is as follows:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration(proxyBeanMethods = false)
public class DataSourceConfiguration {
@Bean
public SimpleDataSource dataSource() {
return new SimpleDataSource();
}
}
----
Since there isn't any particular condition on this class, `dataSourceConfiguration` and `dataSource` are identified as candidates.
The AOT engine will convert the configuration class above to code similar to the following:
[source,java,indent=0,role="primary"]
.Java
----
/**
* Bean definitions for {@link DataSourceConfiguration}
*/
public class DataSourceConfiguration__BeanDefinitions {
/**
* Get the bean definition for 'dataSourceConfiguration'
*/
public static BeanDefinition getDataSourceConfigurationBeanDefinition() {
Class<?> beanType = DataSourceConfiguration.class;
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType);
beanDefinition.setInstanceSupplier(DataSourceConfiguration::new);
return beanDefinition;
}
/**
* Get the bean instance supplier for 'dataSource'.
*/
private static BeanInstanceSupplier<SimpleDataSource> getDataSourceInstanceSupplier() {
return BeanInstanceSupplier.<SimpleDataSource>forFactoryMethod(DataSourceConfiguration.class, "dataSource")
.withGenerator((registeredBean) -> registeredBean.getBeanFactory().getBean(DataSourceConfiguration.class).dataSource());
}
/**
* Get the bean definition for 'dataSource'
*/
public static BeanDefinition getDataSourceBeanDefinition() {
Class<?> beanType = SimpleDataSource.class;
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType);
beanDefinition.setInstanceSupplier(getDataSourceInstanceSupplier());
return beanDefinition;
}
}
----
NOTE: The exact code generated may differ depending on the exact nature of your bean definitions.
The generated code above creates bean definitions equivalent to the `@Configuration` class, but in a direct way and without the use of reflection if at all possible.
There is a bean definition for `dataSourceConfiguration` and one for `dataSourceBean`.
When a `datasource` instance is required, a `BeanInstanceSupplier` is called.
This supplier invokes the `dataSource()` method on the `dataSourceConfiguration` bean.
[[core.aot.hints]]
== Runtime Hints
Running an application as a native image requires additional information compared to a regular JVM runtime.
For instance, GraalVM needs to know ahead of time if a component uses reflection.
Similarly, classpath resources are not shipped in a native image unless specified explicitly.
Consequently, if the application needs to load a resource, it must be referenced from the corresponding GraalVM native image configuration file.
The {api-spring-framework}/aot/hint/RuntimeHints.html[`RuntimeHints`] API collects the need for reflection, resource loading, serialization, and JDK proxies at runtime.
The following example makes sure that `config/app.properties` can be loaded from the classpath at runtime within a native image:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
runtimeHints.resources().registerPattern("config/app.properties");
----
A number of contracts are handled automatically during AOT processing.
For instance, the return type of a `@Controller` method is inspected, and relevant reflection hints are added if Spring detects that the type should be serialized (typically to JSON).
For cases that the core container cannot infer, you can register such hints programmatically.
A number of convenient annotations are also provided for common use cases.
[[core.aot.hints.import-runtime-hints]]
=== `@ImportRuntimeHints`
`RuntimeHintsRegistrar` implementations allow you to get a callback to the `RuntimeHints` instance managed by the AOT engine.
Implementations of this interface can be registered using `@ImportRuntimeHints` on any Spring bean or `@Bean` factory method.
`RuntimeHintsRegistrar` implementations are detected and invoked at build time.
include::code:SpellCheckService[]
If at all possible, `@ImportRuntimeHints` should be used as close as possible to the component that requires the hints.
This way, if the component is not contributed to the `BeanFactory`, the hints won't be contributed either.
It is also possible to register an implementation statically by adding an entry in `META-INF/spring/aot.factories` with a key equal to the fully qualified name of the `RuntimeHintsRegistrar` interface.
[[core.aot.hints.reflective]]
=== `@Reflective`
{api-spring-framework}/aot/hint/annotation/Reflective.html[`@Reflective`] provides an idiomatic way to flag the need for reflection on an annotated element.
For instance, `@EventListener` is meta-annotated with `@Reflective` since the underlying implementation invokes the annotated method using reflection.
By default, only Spring beans are considered and an invocation hint is registered for the annotated element.
This can be tuned by specifying a custom `ReflectiveProcessor` implementation via the
`@Reflective` annotation.
Library authors can reuse this annotation for their own purposes.
If components other than Spring beans need to be processed, a `BeanFactoryInitializationAotProcessor` can detect the relevant types and use `ReflectiveRuntimeHintsRegistrar` to process them.
[[core.aot.hints.register-reflection-for-binding]]
=== `@RegisterReflectionForBinding`
{api-spring-framework}/aot/hint/annotation/RegisterReflectionForBinding.html[`@RegisterReflectionForBinding`] is a specialization of `@Reflective` that registers the need for serializing arbitrary types.
A typical use case is the use of DTOs that the container cannot infer, such as using a web client within a method body.
`@RegisterReflectionForBinding` can be applied to any Spring bean at the class level, but it can also be applied directly to a method, field, or constructor to better indicate where the hints are actually required.
The following example registers `Account` for serialization.
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
public class OrderService {
@RegisterReflectionForBinding(Account.class)
public void process(Order order) {
// ...
}
}
----
[[core.aot.hints.testing]]
=== Testing Runtime Hints
Spring Core also ships `RuntimeHintsPredicates`, a utility for checking that existing hints match a particular use case.
This can be used in your own tests to validate that a `RuntimeHintsRegistrar` contains the expected results.
We can write a test for our `SpellCheckService` and ensure that we will be able to load a dictionary at runtime:
include::code:SpellCheckServiceTests[tag=hintspredicates]
With `RuntimeHintsPredicates`, we can check for reflection, resource, serialization, or proxy generation hints.
This approach works well for unit tests but implies that the runtime behavior of a component is well known.
You can learn more about the global runtime behavior of an application by running its test suite (or the app itself) with the {docs-graalvm}/native-image/metadata/AutomaticMetadataCollection/[GraalVM tracing agent].
This agent will record all relevant calls requiring GraalVM hints at runtime and write them out as JSON configuration files.
For more targeted discovery and testing, Spring Framework ships a dedicated module with core AOT testing utilities, `"org.springframework:spring-core-test"`.
This module contains the RuntimeHints Agent, a Java agent that records all method invocations that are related to runtime hints and helps you to assert that a given `RuntimeHints` instance covers all recorded invocations.
Let's consider a piece of infrastructure for which we'd like to test the hints we're contributing during the AOT processing phase.
include::code:SampleReflection[]
We can then write a unit test (no native compilation required) that checks our contributed hints:
include::code:SampleReflectionRuntimeHintsTests[]
If you forgot to contribute a hint, the test will fail and provide some details about the invocation:
[source,txt,indent=0,subs="verbatim,quotes"]
----
org.springframework.docs.core.aot.hints.testing.SampleReflection performReflection
INFO: Spring version:6.0.0-SNAPSHOT
Missing <"ReflectionHints"> for invocation <java.lang.Class#forName>
with arguments ["org.springframework.core.SpringVersion",
false,
jdk.internal.loader.ClassLoaders$AppClassLoader@251a69d7].
Stacktrace:
<"org.springframework.util.ClassUtils#forName, Line 284
io.spring.runtimehintstesting.SampleReflection#performReflection, Line 19
io.spring.runtimehintstesting.SampleReflectionRuntimeHintsTests#lambda$shouldRegisterReflectionHints$0, Line 25
----
There are various ways to configure this Java agent in your build, so please refer to the documentation of your build tool and test execution plugin.
The agent itself can be configured to instrument specific packages (by default, only `org.springframework` is instrumented).
You'll find more details in the {spring-framework-main-code}/buildSrc/README.md[Spring Framework `buildSrc` README] file.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,181 +0,0 @@
[[databuffers]]
= Data Buffers and Codecs
Java NIO provides `ByteBuffer` but many libraries build their own byte buffer API on top,
especially for network operations where reusing buffers and/or using direct buffers is
beneficial for performance. For example Netty has the `ByteBuf` hierarchy, Undertow uses
XNIO, Jetty uses pooled byte buffers with a callback to be released, and so on.
The `spring-core` module provides a set of abstractions to work with various byte buffer
APIs as follows:
* <<databuffers-factory>> abstracts the creation of a data buffer.
* <<databuffers-buffer>> represents a byte buffer, which may be
<<databuffers-buffer-pooled, pooled>>.
* <<databuffers-utils>> offers utility methods for data buffers.
* <<Codecs>> decode or encode data buffer streams into higher level objects.
[[databuffers-factory]]
== `DataBufferFactory`
`DataBufferFactory` is used to create data buffers in one of two ways:
. Allocate a new data buffer, optionally specifying capacity upfront, if known, which is
more efficient even though implementations of `DataBuffer` can grow and shrink on demand.
. Wrap an existing `byte[]` or `java.nio.ByteBuffer`, which decorates the given data with
a `DataBuffer` implementation and that does not involve allocation.
Note that WebFlux applications do not create a `DataBufferFactory` directly but instead
access it through the `ServerHttpResponse` or the `ClientHttpRequest` on the client side.
The type of factory depends on the underlying client or server, e.g.
`NettyDataBufferFactory` for Reactor Netty, `DefaultDataBufferFactory` for others.
[[databuffers-buffer]]
== `DataBuffer`
The `DataBuffer` interface offers similar operations as `java.nio.ByteBuffer` but also
brings a few additional benefits some of which are inspired by the Netty `ByteBuf`.
Below is a partial list of benefits:
* Read and write with independent positions, i.e. not requiring a call to `flip()` to
alternate between read and write.
* Capacity expanded on demand as with `java.lang.StringBuilder`.
* Pooled buffers and reference counting via <<databuffers-buffer-pooled>>.
* View a buffer as `java.nio.ByteBuffer`, `InputStream`, or `OutputStream`.
* Determine the index, or the last index, for a given byte.
[[databuffers-buffer-pooled]]
== `PooledDataBuffer`
As explained in the Javadoc for
https://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html[ByteBuffer],
byte buffers can be direct or non-direct. Direct buffers may reside outside the Java heap
which eliminates the need for copying for native I/O operations. That makes direct buffers
particularly useful for receiving and sending data over a socket, but they're also more
expensive to create and release, which leads to the idea of pooling buffers.
`PooledDataBuffer` is an extension of `DataBuffer` that helps with reference counting which
is essential for byte buffer pooling. How does it work? When a `PooledDataBuffer` is
allocated the reference count is at 1. Calls to `retain()` increment the count, while
calls to `release()` decrement it. As long as the count is above 0, the buffer is
guaranteed not to be released. When the count is decreased to 0, the pooled buffer can be
released, which in practice could mean the reserved memory for the buffer is returned to
the memory pool.
Note that instead of operating on `PooledDataBuffer` directly, in most cases it's better
to use the convenience methods in `DataBufferUtils` that apply release or retain to a
`DataBuffer` only if it is an instance of `PooledDataBuffer`.
[[databuffers-utils]]
== `DataBufferUtils`
`DataBufferUtils` offers a number of utility methods to operate on data buffers:
* Join a stream of data buffers into a single buffer possibly with zero copy, e.g. via
composite buffers, if that's supported by the underlying byte buffer API.
* Turn `InputStream` or NIO `Channel` into `Flux<DataBuffer>`, and vice versa a
`Publisher<DataBuffer>` into `OutputStream` or NIO `Channel`.
* Methods to release or retain a `DataBuffer` if the buffer is an instance of
`PooledDataBuffer`.
* Skip or take from a stream of bytes until a specific byte count.
[[codecs]]
== Codecs
The `org.springframework.core.codec` package provides the following strategy interfaces:
* `Encoder` to encode `Publisher<T>` into a stream of data buffers.
* `Decoder` to decode `Publisher<DataBuffer>` into a stream of higher level objects.
The `spring-core` module provides `byte[]`, `ByteBuffer`, `DataBuffer`, `Resource`, and
`String` encoder and decoder implementations. The `spring-web` module adds Jackson JSON,
Jackson Smile, JAXB2, Protocol Buffers and other encoders and decoders. See
<<web-reactive.adoc#webflux-codecs, Codecs>> in the WebFlux section.
[[databuffers-using]]
== Using `DataBuffer`
When working with data buffers, special care must be taken to ensure buffers are released
since they may be <<databuffers-buffer-pooled, pooled>>. We'll use codecs to illustrate
how that works but the concepts apply more generally. Let's see what codecs must do
internally to manage data buffers.
A `Decoder` is the last to read input data buffers, before creating higher level
objects, and therefore it must release them as follows:
. If a `Decoder` simply reads each input buffer and is ready to
release it immediately, it can do so via `DataBufferUtils.release(dataBuffer)`.
. If a `Decoder` is using `Flux` or `Mono` operators such as `flatMap`, `reduce`, and
others that prefetch and cache data items internally, or is using operators such as
`filter`, `skip`, and others that leave out items, then
`doOnDiscard(DataBuffer.class, DataBufferUtils::release)` must be added to the
composition chain to ensure such buffers are released prior to being discarded, possibly
also as a result of an error or cancellation signal.
. If a `Decoder` holds on to one or more data buffers in any other way, it must
ensure they are released when fully read, or in case of an error or cancellation signals that
take place before the cached data buffers have been read and released.
Note that `DataBufferUtils#join` offers a safe and efficient way to aggregate a data
buffer stream into a single data buffer. Likewise `skipUntilByteCount` and
`takeUntilByteCount` are additional safe methods for decoders to use.
An `Encoder` allocates data buffers that others must read (and release). So an `Encoder`
doesn't have much to do. However an `Encoder` must take care to release a data buffer if
a serialization error occurs while populating the buffer with data. For example:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
DataBuffer buffer = factory.allocateBuffer();
boolean release = true;
try {
// serialize and populate buffer..
release = false;
}
finally {
if (release) {
DataBufferUtils.release(buffer);
}
}
return buffer;
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val buffer = factory.allocateBuffer()
var release = true
try {
// serialize and populate buffer..
release = false
} finally {
if (release) {
DataBufferUtils.release(buffer)
}
}
return buffer
----
The consumer of an `Encoder` is responsible for releasing the data buffers it receives.
In a WebFlux application, the output of the `Encoder` is used to write to the HTTP server
response, or to the client HTTP request, in which case releasing the data buffers is the
responsibility of the code writing to the server response, or to the client request.
Note that when running on Netty, there are debugging options for
https://github.com/netty/netty/wiki/Reference-counted-objects#troubleshooting-buffer-leaks[troubleshooting buffer leaks].
File diff suppressed because it is too large Load Diff
@@ -1,56 +0,0 @@
[[null-safety]]
= Null-safety
Although Java does not let you express null-safety with its type system, the Spring Framework
now provides the following annotations in the `org.springframework.lang` package to let you
declare nullability of APIs and fields:
* {api-spring-framework}/lang/Nullable.html[`@Nullable`]: Annotation to indicate that a
specific parameter, return value, or field can be `null`.
* {api-spring-framework}/lang/NonNull.html[`@NonNull`]: Annotation to indicate that a specific
parameter, return value, or field cannot be `null` (not needed on parameters / return values
and fields where `@NonNullApi` and `@NonNullFields` apply, respectively).
* {api-spring-framework}/lang/NonNullApi.html[`@NonNullApi`]: Annotation at the package level
that declares non-null as the default semantics for parameters and return values.
* {api-spring-framework}/lang/NonNullFields.html[`@NonNullFields`]: Annotation at the package
level that declares non-null as the default semantics for fields.
The Spring Framework itself leverages these annotations, but they can also be used in any
Spring-based Java project to declare null-safe APIs and optionally null-safe fields.
Generic type arguments, varargs and array elements nullability are not supported yet but
should be in an upcoming release, see https://jira.spring.io/browse/SPR-15942[SPR-15942]
for up-to-date information. Nullability declarations are expected to be fine-tuned between
Spring Framework releases, including minor ones. Nullability of types used inside method
bodies is outside of the scope of this feature.
NOTE: Other common libraries such as Reactor and Spring Data provide null-safe APIs that
use a similar nullability arrangement, delivering a consistent overall experience for
Spring application developers.
== Use cases
In addition to providing an explicit declaration for Spring Framework API nullability,
these annotations can be used by an IDE (such as IDEA or Eclipse) to provide useful
warnings related to null-safety in order to avoid `NullPointerException` at runtime.
They are also used to make Spring API null-safe in Kotlin projects, since Kotlin natively
supports https://kotlinlang.org/docs/reference/null-safety.html[null-safety]. More details
are available in the <<languages#kotlin-null-safety, Kotlin support documentation>>.
== JSR-305 meta-annotations
Spring annotations are meta-annotated with https://jcp.org/en/jsr/detail?id=305[JSR 305]
annotations (a dormant but wide-spread JSR). JSR-305 meta-annotations let tooling vendors
like IDEA or Kotlin provide null-safety support in a generic way, without having to
hard-code support for Spring annotations.
It is not necessary nor recommended to add a JSR-305 dependency to the project classpath to
take advantage of Spring null-safe API. Only projects such as Spring-based libraries that use
null-safety annotations in their codebase should add `com.google.code.findbugs:jsr305:3.0.2`
with `compileOnly` Gradle configuration or Maven `provided` scope to avoid compile warnings.
@@ -1,965 +0,0 @@
[[resources]]
= Resources
This chapter covers how Spring handles resources and how you can work with resources in
Spring. It includes the following topics:
* <<resources-introduction>>
* <<resources-resource>>
* <<resources-implementations>>
* <<resources-resourceloader>>
* <<resources-resourcepatternresolver>>
* <<resources-resourceloaderaware>>
* <<resources-as-dependencies>>
* <<resources-app-ctx>>
[[resources-introduction]]
== Introduction
Java's standard `java.net.URL` class and standard handlers for various URL prefixes,
unfortunately, are not quite adequate enough for all access to low-level resources. For
example, there is no standardized `URL` implementation that may be used to access a
resource that needs to be obtained from the classpath or relative to a
`ServletContext`. While it is possible to register new handlers for specialized `URL`
prefixes (similar to existing handlers for prefixes such as `http:`), this is generally
quite complicated, and the `URL` interface still lacks some desirable functionality,
such as a method to check for the existence of the resource being pointed to.
[[resources-resource]]
== The `Resource` Interface
Spring's `Resource` interface located in the `org.springframework.core.io.` package is
meant to be a more capable interface for abstracting access to low-level resources. The
following listing provides an overview of the `Resource` interface. See the
{api-spring-framework}/core/io/Resource.html[`Resource`] javadoc for further details.
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface Resource extends InputStreamSource {
boolean exists();
boolean isReadable();
boolean isOpen();
boolean isFile();
URL getURL() throws IOException;
URI getURI() throws IOException;
File getFile() throws IOException;
ReadableByteChannel readableChannel() throws IOException;
long contentLength() throws IOException;
long lastModified() throws IOException;
Resource createRelative(String relativePath) throws IOException;
String getFilename();
String getDescription();
}
----
As the definition of the `Resource` interface shows, it extends the `InputStreamSource`
interface. The following listing shows the definition of the `InputStreamSource`
interface:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
----
Some of the most important methods from the `Resource` interface are:
* `getInputStream()`: Locates and opens the resource, returning an `InputStream` for
reading from the resource. It is expected that each invocation returns a fresh
`InputStream`. It is the responsibility of the caller to close the stream.
* `exists()`: Returns a `boolean` indicating whether this resource actually exists in
physical form.
* `isOpen()`: Returns a `boolean` indicating whether this resource represents a handle
with an open stream. If `true`, the `InputStream` cannot be read multiple times and
must be read once only and then closed to avoid resource leaks. Returns `false` for
all usual resource implementations, with the exception of `InputStreamResource`.
* `getDescription()`: Returns a description for this resource, to be used for error
output when working with the resource. This is often the fully qualified file name or
the actual URL of the resource.
Other methods let you obtain an actual `URL` or `File` object representing the
resource (if the underlying implementation is compatible and supports that
functionality).
Some implementations of the `Resource` interface also implement the extended
{api-spring-framework}/core/io/WritableResource.html[`WritableResource`] interface
for a resource that supports writing to it.
Spring itself uses the `Resource` abstraction extensively, as an argument type in
many method signatures when a resource is needed. Other methods in some Spring APIs
(such as the constructors to various `ApplicationContext` implementations) take a
`String` which in unadorned or simple form is used to create a `Resource` appropriate to
that context implementation or, via special prefixes on the `String` path, let the
caller specify that a specific `Resource` implementation must be created and used.
While the `Resource` interface is used a lot with Spring and by Spring, it is actually
very convenient to use as a general utility class by itself in your own code, for access
to resources, even when your code does not know or care about any other parts of Spring.
While this couples your code to Spring, it really only couples it to this small set of
utility classes, which serves as a more capable replacement for `URL` and can be
considered equivalent to any other library you would use for this purpose.
NOTE: The `Resource` abstraction does not replace functionality. It wraps it where
possible. For example, a `UrlResource` wraps a URL and uses the wrapped `URL` to do its
work.
[[resources-implementations]]
== Built-in `Resource` Implementations
Spring includes several built-in `Resource` implementations:
* <<resources-implementations-urlresource>>
* <<resources-implementations-classpathresource>>
* <<resources-implementations-filesystemresource>>
* <<resources-implementations-pathresource>>
* <<resources-implementations-servletcontextresource>>
* <<resources-implementations-inputstreamresource>>
* <<resources-implementations-bytearrayresource>>
For a complete list of `Resource` implementations available in Spring, consult the
"All Known Implementing Classes" section of the
{api-spring-framework}/core/io/Resource.html[`Resource`] javadoc.
[[resources-implementations-urlresource]]
=== `UrlResource`
`UrlResource` wraps a `java.net.URL` and can be used to access any object that is
normally accessible with a URL, such as files, an HTTPS target, an FTP target, and
others. All URLs have a standardized `String` representation, such that appropriate
standardized prefixes are used to indicate one URL type from another. This includes
`file:` for accessing filesystem paths, `https:` for accessing resources through the
HTTPS protocol, `ftp:` for accessing resources through FTP, and others.
A `UrlResource` is created by Java code by explicitly using the `UrlResource` constructor
but is often created implicitly when you call an API method that takes a `String`
argument meant to represent a path. For the latter case, a JavaBeans `PropertyEditor`
ultimately decides which type of `Resource` to create. If the path string contains a
well-known (to property editor, that is) prefix (such as `classpath:`), it creates an
appropriate specialized `Resource` for that prefix. However, if it does not recognize the
prefix, it assumes the string is a standard URL string and creates a `UrlResource`.
[[resources-implementations-classpathresource]]
=== `ClassPathResource`
This class represents a resource that should be obtained from the classpath. It uses
either the thread context class loader, a given class loader, or a given class for
loading resources.
This `Resource` implementation supports resolution as a `java.io.File` if the class path
resource resides in the file system but not for classpath resources that reside in a
jar and have not been expanded (by the servlet engine or whatever the environment is)
to the filesystem. To address this, the various `Resource` implementations always support
resolution as a `java.net.URL`.
A `ClassPathResource` is created by Java code by explicitly using the `ClassPathResource`
constructor but is often created implicitly when you call an API method that takes a
`String` argument meant to represent a path. For the latter case, a JavaBeans
`PropertyEditor` recognizes the special prefix, `classpath:`, on the string path and
creates a `ClassPathResource` in that case.
[[resources-implementations-filesystemresource]]
=== `FileSystemResource`
This is a `Resource` implementation for `java.io.File` handles. It also supports
`java.nio.file.Path` handles, applying Spring's standard String-based path
transformations but performing all operations via the `java.nio.file.Files` API. For pure
`java.nio.path.Path` based support use a `PathResource` instead. `FileSystemResource`
supports resolution as a `File` and as a `URL`.
[[resources-implementations-pathresource]]
=== `PathResource`
This is a `Resource` implementation for `java.nio.file.Path` handles, performing all
operations and transformations via the `Path` API. It supports resolution as a `File` and
as a `URL` and also implements the extended `WritableResource` interface. `PathResource`
is effectively a pure `java.nio.path.Path` based alternative to `FileSystemResource` with
different `createRelative` behavior.
[[resources-implementations-servletcontextresource]]
=== `ServletContextResource`
This is a `Resource` implementation for `ServletContext` resources that interprets
relative paths within the relevant web application's root directory.
It always supports stream access and URL access but allows `java.io.File` access only
when the web application archive is expanded and the resource is physically on the
filesystem. Whether or not it is expanded and on the filesystem or accessed
directly from the JAR or somewhere else like a database (which is conceivable) is actually
dependent on the Servlet container.
[[resources-implementations-inputstreamresource]]
=== `InputStreamResource`
An `InputStreamResource` is a `Resource` implementation for a given `InputStream`. It
should be used only if no specific `Resource` implementation is applicable. In
particular, prefer `ByteArrayResource` or any of the file-based `Resource`
implementations where possible.
In contrast to other `Resource` implementations, this is a descriptor for an
already-opened resource. Therefore, it returns `true` from `isOpen()`. Do not use it if
you need to keep the resource descriptor somewhere or if you need to read a stream
multiple times.
[[resources-implementations-bytearrayresource]]
=== `ByteArrayResource`
This is a `Resource` implementation for a given byte array. It creates a
`ByteArrayInputStream` for the given byte array.
It is useful for loading content from any given byte array without having to resort to a
single-use `InputStreamResource`.
[[resources-resourceloader]]
== The `ResourceLoader` Interface
The `ResourceLoader` interface is meant to be implemented by objects that can return
(that is, load) `Resource` instances. The following listing shows the `ResourceLoader`
interface definition:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface ResourceLoader {
Resource getResource(String location);
ClassLoader getClassLoader();
}
----
All application contexts implement the `ResourceLoader` interface. Therefore, all
application contexts may be used to obtain `Resource` instances.
When you call `getResource()` on a specific application context, and the location path
specified doesn't have a specific prefix, you get back a `Resource` type that is
appropriate to that particular application context. For example, assume the following
snippet of code was run against a `ClassPathXmlApplicationContext` instance:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val template = ctx.getResource("some/resource/path/myTemplate.txt")
----
Against a `ClassPathXmlApplicationContext`, that code returns a `ClassPathResource`. If
the same method were run against a `FileSystemXmlApplicationContext` instance, it would
return a `FileSystemResource`. For a `WebApplicationContext`, it would return a
`ServletContextResource`. It would similarly return appropriate objects for each context.
As a result, you can load resources in a fashion appropriate to the particular application
context.
On the other hand, you may also force `ClassPathResource` to be used, regardless of the
application context type, by specifying the special `classpath:` prefix, as the following
example shows:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val template = ctx.getResource("classpath:some/resource/path/myTemplate.txt")
----
Similarly, you can force a `UrlResource` to be used by specifying any of the standard
`java.net.URL` prefixes. The following examples use the `file` and `https` prefixes:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val template = ctx.getResource("file:///some/resource/path/myTemplate.txt")
----
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt")
----
The following table summarizes the strategy for converting `String` objects to `Resource`
objects:
[[resources-resource-strings]]
.Resource strings
|===
| Prefix| Example| Explanation
| classpath:
| `classpath:com/myapp/config.xml`
| Loaded from the classpath.
| file:
| `\file:///data/config.xml`
| Loaded as a `URL` from the filesystem. See also <<resources-filesystemresource-caveats>>.
| https:
| `\https://myserver/logo.png`
| Loaded as a `URL`.
| (none)
| `/data/config.xml`
| Depends on the underlying `ApplicationContext`.
|===
[[resources-resourcepatternresolver]]
== The `ResourcePatternResolver` Interface
The `ResourcePatternResolver` interface is an extension to the `ResourceLoader` interface
which defines a strategy for resolving a location pattern (for example, an Ant-style path
pattern) into `Resource` objects.
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface ResourcePatternResolver extends ResourceLoader {
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
Resource[] getResources(String locationPattern) throws IOException;
}
----
As can be seen above, this interface also defines a special `classpath*:` resource prefix
for all matching resources from the class path. Note that the resource location is
expected to be a path without placeholders in this case -- for example,
`classpath*:/config/beans.xml`. JAR files or different directories in the class path can
contain multiple files with the same path and the same name. See
<<resources-app-ctx-wildcards-in-resource-paths>> and its subsections for further details
on wildcard support with the `classpath*:` resource prefix.
A passed-in `ResourceLoader` (for example, one supplied via
<<resources-resourceloaderaware,`ResourceLoaderAware`>> semantics) can be checked whether
it implements this extended interface too.
`PathMatchingResourcePatternResolver` is a standalone implementation that is usable
outside an `ApplicationContext` and is also used by `ResourceArrayPropertyEditor` for
populating `Resource[]` bean properties. `PathMatchingResourcePatternResolver` is able to
resolve a specified resource location path into one or more matching `Resource` objects.
The source path may be a simple path which has a one-to-one mapping to a target
`Resource`, or alternatively may contain the special `classpath*:` prefix and/or internal
Ant-style regular expressions (matched using Spring's
`org.springframework.util.AntPathMatcher` utility). Both of the latter are effectively
wildcards.
[NOTE]
====
The default `ResourceLoader` in any standard `ApplicationContext` is in fact an instance
of `PathMatchingResourcePatternResolver` which implements the `ResourcePatternResolver`
interface. The same is true for the `ApplicationContext` instance itself which also
implements the `ResourcePatternResolver` interface and delegates to the default
`PathMatchingResourcePatternResolver`.
====
[[resources-resourceloaderaware]]
== The `ResourceLoaderAware` Interface
The `ResourceLoaderAware` interface is a special callback interface which identifies
components that expect to be provided a `ResourceLoader` reference. The following listing
shows the definition of the `ResourceLoaderAware` interface:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface ResourceLoaderAware {
void setResourceLoader(ResourceLoader resourceLoader);
}
----
When a class implements `ResourceLoaderAware` and is deployed into an application context
(as a Spring-managed bean), it is recognized as `ResourceLoaderAware` by the application
context. The application context then invokes `setResourceLoader(ResourceLoader)`,
supplying itself as the argument (remember, all application contexts in Spring implement
the `ResourceLoader` interface).
Since an `ApplicationContext` is a `ResourceLoader`, the bean could also implement the
`ApplicationContextAware` interface and use the supplied application context directly to
load resources. However, in general, it is better to use the specialized `ResourceLoader`
interface if that is all you need. The code would be coupled only to the resource loading
interface (which can be considered a utility interface) and not to the whole Spring
`ApplicationContext` interface.
In application components, you may also rely upon autowiring of the `ResourceLoader` as
an alternative to implementing the `ResourceLoaderAware` interface. The _traditional_
`constructor` and `byType` autowiring modes (as described in <<beans-factory-autowire>>)
are capable of providing a `ResourceLoader` for either a constructor argument or a
setter method parameter, respectively. For more flexibility (including the ability to
autowire fields and multiple parameter methods), consider using the annotation-based
autowiring features. In that case, the `ResourceLoader` is autowired into a field,
constructor argument, or method parameter that expects the `ResourceLoader` type as long
as the field, constructor, or method in question carries the `@Autowired` annotation.
For more information, see <<beans-autowired-annotation>>.
NOTE: To load one or more `Resource` objects for a resource path that contains wildcards
or makes use of the special `classpath*:` resource prefix, consider having an instance of
<<resources-resourcepatternresolver,`ResourcePatternResolver`>> autowired into your
application components instead of `ResourceLoader`.
[[resources-as-dependencies]]
== Resources as Dependencies
If the bean itself is going to determine and supply the resource path through some sort
of dynamic process, it probably makes sense for the bean to use the `ResourceLoader` or
`ResourcePatternResolver` interface to load resources. For example, consider the loading
of a template of some sort, where the specific resource that is needed depends on the
role of the user. If the resources are static, it makes sense to eliminate the use of the
`ResourceLoader` interface (or `ResourcePatternResolver` interface) completely, have the
bean expose the `Resource` properties it needs, and expect them to be injected into it.
What makes it trivial to then inject these properties is that all application contexts
register and use a special JavaBeans `PropertyEditor`, which can convert `String` paths
to `Resource` objects. For example, the following `MyBean` class has a `template`
property of type `Resource`.
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
package example;
public class MyBean {
private Resource template;
public setTemplate(Resource template) {
this.template = template;
}
// ...
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MyBean(var template: Resource)
----
In an XML configuration file, the `template` property can be configured with a simple
string for that resource, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="myBean" class="example.MyBean">
<property name="template" value="some/resource/path/myTemplate.txt"/>
</bean>
----
Note that the resource path has no prefix. Consequently, because the application context
itself is going to be used as the `ResourceLoader`, the resource is loaded through a
`ClassPathResource`, a `FileSystemResource`, or a `ServletContextResource`, depending on
the exact type of the application context.
If you need to force a specific `Resource` type to be used, you can use a prefix. The
following two examples show how to force a `ClassPathResource` and a `UrlResource` (the
latter being used to access a file in the filesystem):
[source,xml,indent=0,subs="verbatim,quotes"]
----
<property name="template" value="classpath:some/resource/path/myTemplate.txt">
----
[source,xml,indent=0,subs="verbatim,quotes"]
----
<property name="template" value="file:///some/resource/path/myTemplate.txt"/>
----
If the `MyBean` class is refactored for use with annotation-driven configuration, the
path to `myTemplate.txt` can be stored under a key named `template.path` -- for example,
in a properties file made available to the Spring `Environment` (see
<<beans-environment>>). The template path can then be referenced via the `@Value`
annotation using a property placeholder (see <<beans-value-annotations>>). Spring will
retrieve the value of the template path as a string, and a special `PropertyEditor` will
convert the string to a `Resource` object to be injected into the `MyBean` constructor.
The following example demonstrates how to achieve this.
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
public class MyBean {
private final Resource template;
public MyBean(@Value("${template.path}") Resource template) {
this.template = template;
}
// ...
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Component
class MyBean(@Value("\${template.path}") private val template: Resource)
----
If we want to support multiple templates discovered under the same path in multiple
locations in the classpath -- for example, in multiple jars in the classpath -- we can
use the special `classpath*:` prefix and wildcarding to define a `templates.path` key as
`classpath*:/config/templates/*.txt`. If we redefine the `MyBean` class as follows,
Spring will convert the template path pattern into an array of `Resource` objects that
can be injected into the `MyBean` constructor.
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
public class MyBean {
private final Resource[] templates;
public MyBean(@Value("${templates.path}") Resource[] templates) {
this.templates = templates;
}
// ...
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Component
class MyBean(@Value("\${templates.path}") private val templates: Resource[])
----
[[resources-app-ctx]]
== Application Contexts and Resource Paths
This section covers how to create application contexts with resources, including shortcuts
that work with XML, how to use wildcards, and other details.
[[resources-app-ctx-construction]]
=== Constructing Application Contexts
An application context constructor (for a specific application context type) generally
takes a string or array of strings as the location paths of the resources, such as
XML files that make up the definition of the context.
When such a location path does not have a prefix, the specific `Resource` type built from
that path and used to load the bean definitions depends on and is appropriate to the
specific application context. For example, consider the following example, which creates a
`ClassPathXmlApplicationContext`:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = ClassPathXmlApplicationContext("conf/appContext.xml")
----
The bean definitions are loaded from the classpath, because a `ClassPathResource` is
used. However, consider the following example, which creates a `FileSystemXmlApplicationContext`:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/appContext.xml");
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = FileSystemXmlApplicationContext("conf/appContext.xml")
----
Now the bean definitions are loaded from a filesystem location (in this case, relative to
the current working directory).
Note that the use of the special `classpath` prefix or a standard URL prefix on the
location path overrides the default type of `Resource` created to load the bean
definitions. Consider the following example:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx =
new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = FileSystemXmlApplicationContext("classpath:conf/appContext.xml")
----
Using `FileSystemXmlApplicationContext` loads the bean definitions from the classpath.
However, it is still a `FileSystemXmlApplicationContext`. If it is subsequently used as a
`ResourceLoader`, any unprefixed paths are still treated as filesystem paths.
[[resources-app-ctx-classpathxml]]
==== Constructing `ClassPathXmlApplicationContext` Instances -- Shortcuts
The `ClassPathXmlApplicationContext` exposes a number of constructors to enable
convenient instantiation. The basic idea is that you can supply merely a string array
that contains only the filenames of the XML files themselves (without the leading path
information) and also supply a `Class`. The `ClassPathXmlApplicationContext` then derives
the path information from the supplied class.
Consider the following directory layout:
[literal,subs="verbatim,quotes"]
----
com/
example/
services.xml
repositories.xml
MessengerService.class
----
The following example shows how a `ClassPathXmlApplicationContext` instance composed of
the beans defined in files named `services.xml` and `repositories.xml` (which are on the
classpath) can be instantiated:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] {"services.xml", "repositories.xml"}, MessengerService.class);
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = ClassPathXmlApplicationContext(arrayOf("services.xml", "repositories.xml"), MessengerService::class.java)
----
See the {api-spring-framework}/context/support/ClassPathXmlApplicationContext.html[`ClassPathXmlApplicationContext`]
javadoc for details on the various constructors.
[[resources-app-ctx-wildcards-in-resource-paths]]
=== Wildcards in Application Context Constructor Resource Paths
The resource paths in application context constructor values may be simple paths (as
shown earlier), each of which has a one-to-one mapping to a target `Resource` or,
alternately, may contain the special `classpath*:` prefix or internal Ant-style patterns
(matched by using Spring's `PathMatcher` utility). Both of the latter are effectively
wildcards.
One use for this mechanism is when you need to do component-style application assembly. All
components can _publish_ context definition fragments to a well-known location path, and,
when the final application context is created using the same path prefixed with
`classpath*:`, all component fragments are automatically picked up.
Note that this wildcarding is specific to the use of resource paths in application context
constructors (or when you use the `PathMatcher` utility class hierarchy directly) and is
resolved at construction time. It has nothing to do with the `Resource` type itself.
You cannot use the `classpath*:` prefix to construct an actual `Resource`, as
a resource points to just one resource at a time.
[[resources-app-ctx-ant-patterns-in-paths]]
==== Ant-style Patterns
Path locations can contain Ant-style patterns, as the following example shows:
[literal,subs="verbatim,quotes"]
----
/WEB-INF/\*-context.xml
com/mycompany/\**/applicationContext.xml
file:C:/some/path/\*-context.xml
classpath:com/mycompany/**/applicationContext.xml
----
When the path location contains an Ant-style pattern, the resolver follows a more complex
procedure to try to resolve the wildcard. It produces a `Resource` for the path up to the
last non-wildcard segment and obtains a URL from it. If this URL is not a `jar:` URL or
container-specific variant (such as `zip:` in WebLogic, `wsjar` in WebSphere, and so on),
a `java.io.File` is obtained from it and used to resolve the wildcard by traversing the
filesystem. In the case of a jar URL, the resolver either gets a
`java.net.JarURLConnection` from it or manually parses the jar URL and then traverses the
contents of the jar file to resolve the wildcards.
[[resources-app-ctx-portability]]
===== Implications on Portability
If the specified path is already a `file` URL (either implicitly because the base
`ResourceLoader` is a filesystem one or explicitly), wildcarding is guaranteed to
work in a completely portable fashion.
If the specified path is a `classpath` location, the resolver must obtain the last
non-wildcard path segment URL by making a `Classloader.getResource()` call. Since this
is just a node of the path (not the file at the end), it is actually undefined (in the
`ClassLoader` javadoc) exactly what sort of a URL is returned in this case. In practice,
it is always a `java.io.File` representing the directory (where the classpath resource
resolves to a filesystem location) or a jar URL of some sort (where the classpath resource
resolves to a jar location). Still, there is a portability concern on this operation.
If a jar URL is obtained for the last non-wildcard segment, the resolver must be able to
get a `java.net.JarURLConnection` from it or manually parse the jar URL, to be able to
walk the contents of the jar and resolve the wildcard. This does work in most environments
but fails in others, and we strongly recommend that the wildcard resolution of resources
coming from jars be thoroughly tested in your specific environment before you rely on it.
[[resources-classpath-wildcards]]
==== The `classpath*:` Prefix
When constructing an XML-based application context, a location string may use the
special `classpath*:` prefix, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = ClassPathXmlApplicationContext("classpath*:conf/appContext.xml")
----
This special prefix specifies that all classpath resources that match the given name
must be obtained (internally, this essentially happens through a call to
`ClassLoader.getResources(...)`) and then merged to form the final application
context definition.
NOTE: The wildcard classpath relies on the `getResources()` method of the underlying
`ClassLoader`. As most application servers nowadays supply their own `ClassLoader`
implementation, the behavior might differ, especially when dealing with jar files. A
simple test to check if `classpath*` works is to use the `ClassLoader` to load a file from
within a jar on the classpath:
`getClass().getClassLoader().getResources("<someFileInsideTheJar>")`. Try this test with
files that have the same name but reside in two different locations -- for example, files
with the same name and same path but in different jars on the classpath. In case an
inappropriate result is returned, check the application server documentation for settings
that might affect the `ClassLoader` behavior.
You can also combine the `classpath*:` prefix with a `PathMatcher` pattern in the
rest of the location path (for example, `classpath*:META-INF/*-beans.xml`). In this
case, the resolution strategy is fairly simple: A `ClassLoader.getResources()` call is
used on the last non-wildcard path segment to get all the matching resources in the
class loader hierarchy and then, off each resource, the same `PathMatcher` resolution
strategy described earlier is used for the wildcard subpath.
[[resources-wildcards-in-path-other-stuff]]
==== Other Notes Relating to Wildcards
Note that `classpath*:`, when combined with Ant-style patterns, only works
reliably with at least one root directory before the pattern starts, unless the actual
target files reside in the file system. This means that a pattern such as
`classpath*:*.xml` might not retrieve files from the root of jar files but rather only
from the root of expanded directories.
Spring's ability to retrieve classpath entries originates from the JDK's
`ClassLoader.getResources()` method, which only returns file system locations for an
empty string (indicating potential roots to search). Spring evaluates
`URLClassLoader` runtime configuration and the `java.class.path` manifest in jar files
as well, but this is not guaranteed to lead to portable behavior.
[NOTE]
====
The scanning of classpath packages requires the presence of corresponding directory
entries in the classpath. When you build JARs with Ant, do not activate the `files-only`
switch of the JAR task. Also, classpath directories may not get exposed based on security
policies in some environments -- for example, stand-alone applications on JDK 1.7.0_45
and higher (which requires 'Trusted-Library' to be set up in your manifests. See
https://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources).
On JDK 9's module path (Jigsaw), Spring's classpath scanning generally works as expected.
Putting resources into a dedicated directory is highly recommendable here as well,
avoiding the aforementioned portability problems with searching the jar file root level.
====
Ant-style patterns with `classpath:` resources are not guaranteed to find matching
resources if the root package to search is available in multiple classpath locations.
Consider the following example of a resource location:
[literal,subs="verbatim,quotes"]
----
com/mycompany/package1/service-context.xml
----
Now consider an Ant-style path that someone might use to try to find that file:
[literal,subs="verbatim,quotes"]
----
classpath:com/mycompany/**/service-context.xml
----
Such a resource may exist in only one location in the classpath, but when a path such as
the preceding example is used to try to resolve it, the resolver works off the (first)
URL returned by `getResource("com/mycompany");`. If this base package node exists in
multiple `ClassLoader` locations, the desired resource may not exist in the first
location found. Therefore, in such cases you should prefer using `classpath*:` with the
same Ant-style pattern, which searches all classpath locations that contain the
`com.mycompany` base package: `classpath*:com/mycompany/**/service-context.xml`.
[[resources-filesystemresource-caveats]]
=== `FileSystemResource` Caveats
A `FileSystemResource` that is not attached to a `FileSystemApplicationContext` (that
is, when a `FileSystemApplicationContext` is not the actual `ResourceLoader`) treats
absolute and relative paths as you would expect. Relative paths are relative to the
current working directory, while absolute paths are relative to the root of the
filesystem.
For backwards compatibility (historical) reasons however, this changes when the
`FileSystemApplicationContext` is the `ResourceLoader`. The
`FileSystemApplicationContext` forces all attached `FileSystemResource` instances
to treat all location paths as relative, whether they start with a leading slash or not.
In practice, this means the following examples are equivalent:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/context.xml");
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = FileSystemXmlApplicationContext("conf/context.xml")
----
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx =
new FileSystemXmlApplicationContext("/conf/context.xml");
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = FileSystemXmlApplicationContext("/conf/context.xml")
----
The following examples are also equivalent (even though it would make sense for them to be different, as one
case is relative and the other absolute):
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("some/resource/path/myTemplate.txt");
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx: FileSystemXmlApplicationContext = ...
ctx.getResource("some/resource/path/myTemplate.txt")
----
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("/some/resource/path/myTemplate.txt");
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx: FileSystemXmlApplicationContext = ...
ctx.getResource("/some/resource/path/myTemplate.txt")
----
In practice, if you need true absolute filesystem paths, you should avoid using
absolute paths with `FileSystemResource` or `FileSystemXmlApplicationContext` and
force the use of a `UrlResource` by using the `file:` URL prefix. The following examples
show how to do so:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// actual context type doesn't matter, the Resource will always be UrlResource
ctx.getResource("file:///some/resource/path/myTemplate.txt");
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// actual context type doesn't matter, the Resource will always be UrlResource
ctx.getResource("file:///some/resource/path/myTemplate.txt")
----
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// force this FileSystemXmlApplicationContext to load its definition via a UrlResource
ApplicationContext ctx =
new FileSystemXmlApplicationContext("file:///conf/context.xml");
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// force this FileSystemXmlApplicationContext to load its definition via a UrlResource
val ctx = FileSystemXmlApplicationContext("file:///conf/context.xml")
----
@@ -1,41 +0,0 @@
[[spring-jcl]]
= Logging
Since Spring Framework 5.0, Spring comes with its own Commons Logging bridge implemented
in the `spring-jcl` module. The implementation checks for the presence of the Log4j 2.x
API and the SLF4J 1.7 API in the classpath and uses the first one of those found as the
logging implementation, falling back to the Java platform's core logging facilities (also
known as _JUL_ or `java.util.logging`) if neither Log4j 2.x nor SLF4J is available.
Put Log4j 2.x or Logback (or another SLF4J provider) in your classpath, without any extra
bridges, and let the framework auto-adapt to your choice. For further information see the
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-logging[Spring
Boot Logging Reference Documentation].
[NOTE]
====
Spring's Commons Logging variant is only meant to be used for infrastructure logging
purposes in the core framework and in extensions.
For logging needs within application code, prefer direct use of Log4j 2.x, SLF4J, or JUL.
====
A `Log` implementation may be retrieved via `org.apache.commons.logging.LogFactory` as in
the following example.
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class MyBean {
private final Log log = LogFactory.getLog(getClass());
// ...
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MyBean {
private val log = LogFactory.getLog(javaClass)
// ...
}
----
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,95 +0,0 @@
[[data.access.appendix]]
= Appendix
[[data.access.xsd-schemas]]
== XML Schemas
This part of the appendix lists XML schemas for data access, including the following:
* <<xsd-schemas-tx>>
* <<xsd-schemas-jdbc>>
[[xsd-schemas-tx]]
=== The `tx` Schema
The `tx` tags deal with configuring all of those beans in Spring's comprehensive support
for transactions. These tags are covered in the chapter entitled
<<data-access.adoc#transaction, Transaction Management>>.
TIP: We strongly encourage you to look at the `'spring-tx.xsd'` file that ships with the
Spring distribution. This file contains the XML Schema for Spring's transaction
configuration and covers all of the various elements in the `tx` namespace, including
attribute defaults and similar information. This file is documented inline, and, thus,
the information is not repeated here in the interests of adhering to the DRY (Don't
Repeat Yourself) principle.
In the interest of completeness, to use the elements in the `tx` schema, you need to have
the following preamble at the top of your Spring XML configuration file. The text in the
following snippet references the correct schema so that the tags in the `tx` namespace
are available to you:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?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:tx="http://www.springframework.org/schema/tx" <1>
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd <2>
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- bean definitions here -->
</beans>
----
<1> Declare usage of the `tx` namespace.
<2> Specify the location (with other schema locations).
NOTE: Often, when you use the elements in the `tx` namespace, you are also using the
elements from the `aop` namespace (since the declarative transaction support in Spring is
implemented by using AOP). The preceding XML snippet contains the relevant lines needed
to reference the `aop` schema so that the elements in the `aop` namespace are available
to you.
[[xsd-schemas-jdbc]]
=== The `jdbc` Schema
The `jdbc` elements let you quickly configure an embedded database or initialize an
existing data source. These elements are documented in
<<data-access.adoc#jdbc-embedded-database-support, Embedded Database Support>> and
<<data-access.adoc#jdbc-initializing-datasource, Initializing a DataSource>>, respectively.
To use the elements in the `jdbc` schema, you need to have the following preamble at the
top of your Spring XML configuration file. The text in the following snippet references
the correct schema so that the elements in the `jdbc` namespace are available to you:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?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:jdbc="http://www.springframework.org/schema/jdbc" <1>
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jdbc
https://www.springframework.org/schema/jdbc/spring-jdbc.xsd"> <2>
<!-- bean definitions here -->
</beans>
----
<1> Declare usage of the `jdbc` namespace.
<2> Specify the location (with other schema locations).
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

@@ -1,612 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generated by Microsoft Visio 11.0, SVG Export, v1.0 spring-overview.svg Page-1 -->
<svg
xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="6.4728541in"
height="5.9522467in"
viewBox="0 0 466.04561 428.56238"
xml:space="preserve"
class="st5"
id="svg5499"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="mvc-splitted-contexts.svg"
style="font-size:12px;overflow:visible;color-interpolation-filters:sRGB;fill:none;fill-rule:evenodd;stroke-linecap:square;stroke-miterlimit:3"
inkscape:export-filename="/Users/seb/Workspace/spring-framework/src/asciidoc/images/mvc-splitted-contexts.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"><metadata
id="metadata5713"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="https://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs5711"><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker9162"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path9164" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker8834"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path8836" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker8518"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path8520" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker8214"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path8216" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker7922"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path7924" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker7641"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path7644" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker7373"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path7375" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker7117"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path7119" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker6873"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path6875" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker6641"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path6643" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker6421"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path6423" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker6213"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path6215" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker6017"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path6019" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker5833"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path5835" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker5661"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path5663" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker5501"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path5503" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker5353"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path5355" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker5217"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path5219" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker5093"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path5095" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker4981"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path4983" /></marker><marker
inkscape:stockid="Arrow2Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Mend"
style="overflow:visible;"
inkscape:isstock="true"
inkscape:collect="always"><path
id="path7394"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.6) rotate(180) translate(0,0)" /></marker><marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="marker8121"
style="overflow:visible;"
inkscape:isstock="true"><path
id="path8123"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(1.1) rotate(180) translate(1,0)" /></marker><marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="marker8033"
style="overflow:visible;"
inkscape:isstock="true"><path
id="path8035"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
transform="scale(0.4) rotate(180) translate(10,0)" /></marker><marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="marker7957"
style="overflow:visible;"
inkscape:isstock="true"><path
id="path7959"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
transform="scale(0.4) rotate(180) translate(10,0)" /></marker><marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Mend"
style="overflow:visible;"
inkscape:isstock="true"><path
id="path7376"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
transform="scale(0.4) rotate(180) translate(10,0)" /></marker><marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Lend"
style="overflow:visible;"
inkscape:isstock="true"><path
id="path7388"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(1.1) rotate(180) translate(1,0)" /></marker><marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Lend"
style="overflow:visible;"
inkscape:isstock="true"><path
id="path7370"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
transform="scale(0.8) rotate(180) translate(12.5,0)" /></marker></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="1005"
id="namedview5709"
showgrid="false"
inkscape:zoom="1.1640492"
inkscape:cx="134.86698"
inkscape:cy="203.59898"
inkscape:window-x="84"
inkscape:window-y="176"
inkscape:window-maximized="0"
inkscape:current-layer="g5503"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" /><v:documentProperties
v:langID="1033"
v:viewMarkup="false" /><style
type="text/css"
id="style5501"><![CDATA[
.st1 {fill:#969696;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
.st2 {fill:#dde2cd;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
.st3 {fill:#000000;font-family:Arial;font-size:2.50001em;font-weight:bold}
.st4 {font-size:0.333333em;font-weight:normal}
.st5 {fill:none;fill-rule:evenodd;font-size:12;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
]]></style><g
v:mID="0"
v:index="1"
v:groupContext="foregroundPage"
id="g5503"
transform="matrix(0.99998201,0,0,1.0824094,-40.812382,-98.908648)"><rect
style="fill:#dde2cd;fill-opacity:1;stroke:#000000;stroke-width:1.53790233;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect6599"
width="382.68423"
height="146.09897"
x="87.884865"
y="148.26482" /><v:userDefs><v:ud
v:nameU="SchemeName"
v:val="VT4(Default)" /></v:userDefs><rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1.53790233;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect5725"
width="464.31128"
height="374.11411"
x="41.684383"
y="112.3262" /><title
id="title5505">Page-1</title><v:pageProperties
v:drawingScale="0.0393701"
v:pageScale="0.0393701"
v:drawingUnits="24"
v:shadowOffsetX="8.50394"
v:shadowOffsetY="-8.50394" /><v:layer
v:name="Connector"
v:index="0" /><rect
style="fill:#dde2cd;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.53790233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect5715"
width="322.8194"
height="43.63184"
x="119.95335"
y="-135.66222"
transform="scale(1,-1)" /><text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:30.7580471px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="168.843"
y="124.32391"
id="text5717"
sodipodi:linespacing="125%"
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
id="tspan5719"
x="168.843"
y="124.32391"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:sans-serif;-inkscape-font-specification:sans-serif">DispatcherServlet</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="89.770851"
y="181.20923"
id="text6589"
sodipodi:linespacing="125%"
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
id="tspan6591"
x="89.770851"
y="181.20923"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:sans-serif;-inkscape-font-specification:sans-serif">Servlet WebApplicationContext</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="260.00443"
y="198.41273"
id="text6593"
sodipodi:linespacing="125%"
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
id="tspan6595"
x="260.00443"
y="198.41273"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle">(containing controllers, view resolvers,</tspan><tspan
sodipodi:role="line"
x="260.00443"
y="212.83057"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle"
id="tspan6597">and other web-related beans)</tspan></text>
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect6620"
width="82.040657"
height="36.72575"
x="114.52653"
y="-259.43161"
transform="scale(1,-1)" /><rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.89166164;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect6622"
width="87.843979"
height="36.697304"
x="223.39864"
y="-287.19809"
transform="scale(1,-1)" /><rect
transform="scale(1,-1)"
y="-264.81918"
x="117.92834"
height="36.72575"
width="82.040657"
id="rect6614"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="121.24728"
y="260.14957"
id="text6616"
sodipodi:linespacing="125%"
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
id="tspan6618"
x="121.24728"
y="260.14957"
style="font-size:11.53426838px">Controllers</tspan></text>
<text
transform="scale(1.0403984,0.96117025)"
sodipodi:linespacing="125%"
id="text6624"
y="282.70709"
x="219.61203"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-size:11.53426838px"
y="282.70709"
x="219.61203"
id="tspan6626"
sodipodi:role="line">ViewResolver</tspan></text>
<rect
transform="scale(1,-1)"
y="-259.30249"
x="332.5405"
height="36.577778"
width="114.4539"
id="rect6628"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.0161339;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="327.51276"
y="255.28464"
id="text6630"
sodipodi:linespacing="125%"
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
id="tspan6632"
x="327.51276"
y="255.28464"
style="font-size:11.53426838px">HandlerMapping</tspan></text>
<rect
y="338.69724"
x="87.803261"
height="121.5683"
width="382.84744"
id="rect6634"
style="fill:#dde2cd;fill-opacity:1;stroke:#000000;stroke-width:1.53790233;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
transform="scale(1.0403984,0.96117025)"
sodipodi:linespacing="125%"
id="text6636"
y="376.61673"
x="108.61351"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:sans-serif;-inkscape-font-specification:sans-serif"
y="376.61673"
x="108.61351"
id="tspan6638"
sodipodi:role="line">Root WebApplicationContext</tspan></text>
<text
transform="scale(1.0403984,0.96117025)"
sodipodi:linespacing="125%"
id="text6640"
y="395.35812"
x="260.93863"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan6644"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle"
y="395.35812"
x="260.93863"
sodipodi:role="line">(containing middle-tier services, datasources, etc.)</tspan></text>
<rect
transform="scale(1,-1)"
y="-440.06805"
x="164.32933"
height="36.72575"
width="82.040657"
id="rect6648"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect6650"
width="82.040657"
height="36.72575"
x="167.73116"
y="-445.45563"
transform="scale(1,-1)" /><text
transform="scale(1.0403984,0.96117025)"
sodipodi:linespacing="125%"
id="text6652"
y="448.55054"
x="175.87148"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-size:11.53426838px"
y="448.55054"
x="175.87148"
id="tspan6654"
sodipodi:role="line">Services</tspan></text>
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88435173;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect6656"
width="86.393044"
height="36.704323"
x="306.86328"
y="-439.60837"
transform="scale(1,-1)" /><rect
transform="scale(1,-1)"
y="-444.99475"
x="310.26624"
height="36.701977"
width="86.876686"
id="rect6658"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88679528;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="305.30771"
y="448.55054"
id="text6660"
sodipodi:linespacing="125%"
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
id="tspan6662"
x="305.30771"
y="448.55054"
style="font-size:11.53426838px">Repositories</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.76895118px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 265.33234,295.60379 c 0,42.65169 0,42.65169 0,0 z"
id="path7643"
inkscape:connector-curvature="0" /><path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.46028023;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
d="m 270.43505,294.70585 c 0,39.4721 0,39.87903 0,39.87903"
id="path7645"
inkscape:connector-curvature="0" /><text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="352.55331"
y="333.03622"
id="text4963"
sodipodi:linespacing="125%"
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
x="352.55331"
y="333.03622"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle"
id="tspan4965">Delegates if no bean found</tspan></text>
</g></svg>

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

@@ -1,9 +0,0 @@
<div id="header">
<h1>Spring Framework Documentation</h1>
<!--
<div class="details">
<span id="revnumber">{revnumber}</span>
</div>
-->
<span><strong>{revnumber}</strong></span>
</div>
@@ -1,38 +0,0 @@
:noheader:
= Spring Framework Documentation
include::attributes.adoc[]
[horizontal]
<<overview.adoc#overview, Overview>> :: History, Design Philosophy, Feedback,
Getting Started.
<<core.adoc#spring-core, Core>> :: IoC Container, Events, Resources, i18n,
Validation, Data Binding, Type Conversion, SpEL, AOP, AOT.
<<testing.adoc#testing, Testing>> :: Mock Objects, TestContext Framework,
Spring MVC Test, WebTestClient.
<<data-access.adoc#spring-data-tier, Data Access>> :: Transactions, DAO Support,
JDBC, R2DBC, O/R Mapping, XML Marshalling.
<<web.adoc#spring-web, Web Servlet>> :: Spring MVC, WebSocket, SockJS,
STOMP Messaging.
<<web-reactive.adoc#spring-webflux, Web Reactive>> :: Spring WebFlux, WebClient,
WebSocket, RSocket.
<<integration.adoc#spring-integration, Integration>> :: REST Clients, JMS, JCA, JMX,
Email, Tasks, Scheduling, Caching, Observability.
<<languages.adoc#languages, Languages>> :: Kotlin, Groovy, Dynamic Languages.
<<appendix.adoc#appendix, Appendix>> :: Spring properties.
https://github.com/spring-projects/spring-framework/wiki[Wiki] :: What's New,
Upgrade Notes, Supported Versions, additional cross-version information.
NOTE: This documentation is also available in {docs-spring-framework}/reference/pdf/spring-framework.pdf[PDF] format.
Rod Johnson, Juergen Hoeller, Keith Donald, Colin Sampaleanu, Rob Harrop, Thomas Risberg,
Alef Arendsen, Darren Davison, Dmitriy Kopylenko, Mark Pollack, Thierry Templier, Erwin
Vervaet, Portia Tung, Ben Hale, Adrian Colyer, John Lewis, Costin Leau, Mark Fisher, Sam
Brannen, Ramnivas Laddad, Arjen Poutsma, Chris Beams, Tareq Abedrabbo, Andy Clement, Dave
Syer, Oliver Gierke, Rossen Stoyanchev, Phillip Webb, Rob Winch, Brian Clozel, Stephane
Nicoll, Sebastien Deleuze, Jay Bryant, Mark Paluch
Copyright © 2002 - 2022 VMware, Inc. All Rights Reserved.
Copies of this document may be made for your own use and for distribution to others,
provided that you do not charge any fee for such copies and further provided that each
copy contains this Copyright Notice, whether distributed in print or electronically.
@@ -1,23 +0,0 @@
[[spring-integration]]
= Integration
include::attributes.adoc[]
include::page-layout.adoc[]
This part of the reference documentation covers Spring Framework's integration with
a number of technologies.
include::integration/rest-clients.adoc[leveloffset=+1]
include::integration/jms.adoc[leveloffset=+1]
include::integration/jmx.adoc[leveloffset=+1]
include::integration/email.adoc[leveloffset=+1]
include::integration/scheduling.adoc[leveloffset=+1]
include::integration/cache.adoc[leveloffset=+1]
include::integration/observability.adoc[leveloffset=+1]
include::integration/integration-appendix.adoc[leveloffset=+1]
File diff suppressed because it is too large Load Diff
@@ -1,302 +0,0 @@
[[mail]]
= Email
This section describes how to send email with the Spring Framework.
.Library dependencies
****
The following JAR needs to be on the classpath of your application in order to use
the Spring Framework's email library:
* The https://eclipse-ee4j.github.io/mail/[JavaMail / Jakarta Mail 1.6] library
This library is freely available on the web -- for example, in Maven Central as
`com.sun.mail:jakarta.mail`. Please make sure to use the latest 1.6.x version
rather than Jakarta Mail 2.0 (which comes with a different package namespace).
****
The Spring Framework provides a helpful utility library for sending email that shields
you from the specifics of the underlying mailing system and is responsible for
low-level resource handling on behalf of the client.
The `org.springframework.mail` package is the root level package for the Spring
Framework's email support. The central interface for sending emails is the `MailSender`
interface. A simple value object that encapsulates the properties of a simple mail such
as `from` and `to` (plus many others) is the `SimpleMailMessage` class. This package
also contains a hierarchy of checked exceptions that provide a higher level of
abstraction over the lower level mail system exceptions, with the root exception being
`MailException`. See the {api-spring-framework}/mail/MailException.html[javadoc]
for more information on the rich mail exception hierarchy.
The `org.springframework.mail.javamail.JavaMailSender` interface adds specialized
JavaMail features, such as MIME message support to the `MailSender` interface
(from which it inherits). `JavaMailSender` also provides a callback interface called
`org.springframework.mail.javamail.MimeMessagePreparator` for preparing a `MimeMessage`.
[[mail-usage]]
== Usage
Assume that we have a business interface called `OrderManager`, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface OrderManager {
void placeOrder(Order order);
}
----
Further assume that we have a requirement stating that an email message with an
order number needs to be generated and sent to a customer who placed the relevant order.
[[mail-usage-simple]]
=== Basic `MailSender` and `SimpleMailMessage` Usage
The following example shows how to use `MailSender` and `SimpleMailMessage` to send an
email when someone places an order:
[source,java,indent=0,subs="verbatim,quotes"]
----
import org.springframework.mail.MailException;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;
public class SimpleOrderManager implements OrderManager {
private MailSender mailSender;
private SimpleMailMessage templateMessage;
public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}
public void setTemplateMessage(SimpleMailMessage templateMessage) {
this.templateMessage = templateMessage;
}
public void placeOrder(Order order) {
// Do the business calculations...
// Call the collaborators to persist the order...
// Create a thread safe "copy" of the template message and customize it
SimpleMailMessage msg = new SimpleMailMessage(this.templateMessage);
msg.setTo(order.getCustomer().getEmailAddress());
msg.setText(
"Dear " + order.getCustomer().getFirstName()
+ order.getCustomer().getLastName()
+ ", thank you for placing order. Your order number is "
+ order.getOrderNumber());
try {
this.mailSender.send(msg);
}
catch (MailException ex) {
// simply log it and go on...
System.err.println(ex.getMessage());
}
}
}
----
The following example shows the bean definitions for the preceding code:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="mail.mycompany.example"/>
</bean>
<!-- this is a template message that we can pre-load with default state -->
<bean id="templateMessage" class="org.springframework.mail.SimpleMailMessage">
<property name="from" value="customerservice@mycompany.example"/>
<property name="subject" value="Your order"/>
</bean>
<bean id="orderManager" class="com.mycompany.businessapp.support.SimpleOrderManager">
<property name="mailSender" ref="mailSender"/>
<property name="templateMessage" ref="templateMessage"/>
</bean>
----
[[mail-usage-mime]]
=== Using `JavaMailSender` and `MimeMessagePreparator`
This section describes another implementation of `OrderManager` that uses the `MimeMessagePreparator`
callback interface. In the following example, the `mailSender` property is of type
`JavaMailSender` so that we are able to use the JavaMail `MimeMessage` class:
[source,java,indent=0,subs="verbatim,quotes"]
----
import jakarta.mail.Message;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.internet.MimeMessage;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessagePreparator;
public class SimpleOrderManager implements OrderManager {
private JavaMailSender mailSender;
public void setMailSender(JavaMailSender mailSender) {
this.mailSender = mailSender;
}
public void placeOrder(final Order order) {
// Do the business calculations...
// Call the collaborators to persist the order...
MimeMessagePreparator preparator = new MimeMessagePreparator() {
public void prepare(MimeMessage mimeMessage) throws Exception {
mimeMessage.setRecipient(Message.RecipientType.TO,
new InternetAddress(order.getCustomer().getEmailAddress()));
mimeMessage.setFrom(new InternetAddress("mail@mycompany.example"));
mimeMessage.setText("Dear " + order.getCustomer().getFirstName() + " " +
order.getCustomer().getLastName() + ", thanks for your order. " +
"Your order number is " + order.getOrderNumber() + ".");
}
};
try {
this.mailSender.send(preparator);
}
catch (MailException ex) {
// simply log it and go on...
System.err.println(ex.getMessage());
}
}
}
----
NOTE: The mail code is a crosscutting concern and could well be a candidate for
refactoring into a <<core.adoc#aop, custom Spring AOP aspect>>, which could then
be run at appropriate joinpoints on the `OrderManager` target.
The Spring Framework's mail support ships with the standard JavaMail implementation.
See the relevant javadoc for more information.
[[mail-javamail-mime]]
== Using the JavaMail `MimeMessageHelper`
A class that comes in pretty handy when dealing with JavaMail messages is
`org.springframework.mail.javamail.MimeMessageHelper`, which shields you from
having to use the verbose JavaMail API. Using the `MimeMessageHelper`, it is
pretty easy to create a `MimeMessage`, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
// of course you would use DI in any real-world cases
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("mail.host.com");
MimeMessage message = sender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message);
helper.setTo("test@host.com");
helper.setText("Thank you for ordering!");
sender.send(message);
----
[[mail-javamail-mime-attachments]]
=== Sending Attachments and Inline Resources
Multipart email messages allow for both attachments and inline resources. Examples of
inline resources include an image or a stylesheet that you want to use in your message but
that you do not want displayed as an attachment.
[[mail-javamail-mime-attachments-attachment]]
==== Attachments
The following example shows you how to use the `MimeMessageHelper` to send an email
with a single JPEG image attachment:
[source,java,indent=0,subs="verbatim,quotes"]
----
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("mail.host.com");
MimeMessage message = sender.createMimeMessage();
// use the true flag to indicate you need a multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo("test@host.com");
helper.setText("Check out this image!");
// let's attach the infamous windows Sample file (this time copied to c:/)
FileSystemResource file = new FileSystemResource(new File("c:/Sample.jpg"));
helper.addAttachment("CoolImage.jpg", file);
sender.send(message);
----
[[mail-javamail-mime-attachments-inline]]
==== Inline Resources
The following example shows you how to use the `MimeMessageHelper` to send an email
with an inline image:
[source,java,indent=0,subs="verbatim,quotes"]
----
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("mail.host.com");
MimeMessage message = sender.createMimeMessage();
// use the true flag to indicate you need a multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo("test@host.com");
// use the true flag to indicate the text included is HTML
helper.setText("<html><body><img src='cid:identifier1234'></body></html>", true);
// let's include the infamous windows Sample file (this time copied to c:/)
FileSystemResource res = new FileSystemResource(new File("c:/Sample.jpg"));
helper.addInline("identifier1234", res);
sender.send(message);
----
WARNING: Inline resources are added to the `MimeMessage` by using the specified `Content-ID`
(`identifier1234` in the above example). The order in which you add the text
and the resource are very important. Be sure to first add the text and then
the resources. If you are doing it the other way around, it does not work.
[[mail-templates]]
=== Creating Email Content by Using a Templating Library
The code in the examples shown in the previous sections explicitly created the content of the email message,
by using methods calls such as `message.setText(..)`. This is fine for simple cases, and it
is okay in the context of the aforementioned examples, where the intent was to show you
the very basics of the API.
In your typical enterprise application, though, developers often do not create the content
of email messages by using the previously shown approach for a number of reasons:
* Creating HTML-based email content in Java code is tedious and error prone.
* There is no clear separation between display logic and business logic.
* Changing the display structure of the email content requires writing Java code,
recompiling, redeploying, and so on.
Typically, the approach taken to address these issues is to use a template library (such
as FreeMarker) to define the display structure of email content. This leaves your code
tasked only with creating the data that is to be rendered in the email template and
sending the email. It is definitely a best practice when the content of your email messages
becomes even moderately complex, and, with the Spring Framework's support classes for
FreeMarker, it becomes quite easy to do.
@@ -1,339 +0,0 @@
[[integration.appendix]]
= Appendix
[[integration.appendix.xsd-schemas]]
== XML Schemas
This part of the appendix lists XML schemas related to integration technologies.
[[integration.appendix.xsd-schemas-jee]]
=== The `jee` Schema
The `jee` elements deal with issues related to Jakarta EE (Enterprise Edition) configuration,
such as looking up a JNDI object and defining EJB references.
To use the elements in the `jee` schema, you need to have the following preamble at the top
of your Spring XML configuration file. The text in the following snippet references the
correct schema so that the elements in the `jee` namespace are available to you:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?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:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee
https://www.springframework.org/schema/jee/spring-jee.xsd">
<!-- bean definitions here -->
</beans>
----
[[integration.appendix.xsd-schemas-jee-jndi-lookup]]
==== <jee:jndi-lookup/> (simple)
The following example shows how to use JNDI to look up a data source without the `jee` schema:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc/MyDataSource"/>
</bean>
<bean id="userDao" class="com.foo.JdbcUserDao">
<!-- Spring will do the cast automatically (as usual) -->
<property name="dataSource" ref="dataSource"/>
</bean>
----
The following example shows how to use JNDI to look up a data source with the `jee`
schema:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/MyDataSource"/>
<bean id="userDao" class="com.foo.JdbcUserDao">
<!-- Spring will do the cast automatically (as usual) -->
<property name="dataSource" ref="dataSource"/>
</bean>
----
[[integration.appendix.xsd-schemas-jee-jndi-lookup-environment-single]]
==== `<jee:jndi-lookup/>` (with Single JNDI Environment Setting)
The following example shows how to use JNDI to look up an environment variable without
`jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="simple" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc/MyDataSource"/>
<property name="jndiEnvironment">
<props>
<prop key="ping">pong</prop>
</props>
</property>
</bean>
----
The following example shows how to use JNDI to look up an environment variable with `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<jee:jndi-lookup id="simple" jndi-name="jdbc/MyDataSource">
<jee:environment>ping=pong</jee:environment>
</jee:jndi-lookup>
----
[[integration.appendix.xsd-schemas-jee-jndi-lookup-environment-multiple]]
==== `<jee:jndi-lookup/>` (with Multiple JNDI Environment Settings)
The following example shows how to use JNDI to look up multiple environment variables
without `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="simple" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc/MyDataSource"/>
<property name="jndiEnvironment">
<props>
<prop key="sing">song</prop>
<prop key="ping">pong</prop>
</props>
</property>
</bean>
----
The following example shows how to use JNDI to look up multiple environment variables with
`jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<jee:jndi-lookup id="simple" jndi-name="jdbc/MyDataSource">
<!-- newline-separated, key-value pairs for the environment (standard Properties format) -->
<jee:environment>
sing=song
ping=pong
</jee:environment>
</jee:jndi-lookup>
----
[[integration.appendix.xsd-schemas-jee-jndi-lookup-complex]]
==== `<jee:jndi-lookup/>` (Complex)
The following example shows how to use JNDI to look up a data source and a number of
different properties without `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="simple" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc/MyDataSource"/>
<property name="cache" value="true"/>
<property name="resourceRef" value="true"/>
<property name="lookupOnStartup" value="false"/>
<property name="expectedType" value="com.myapp.DefaultThing"/>
<property name="proxyInterface" value="com.myapp.Thing"/>
</bean>
----
The following example shows how to use JNDI to look up a data source and a number of
different properties with `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<jee:jndi-lookup id="simple"
jndi-name="jdbc/MyDataSource"
cache="true"
resource-ref="true"
lookup-on-startup="false"
expected-type="com.myapp.DefaultThing"
proxy-interface="com.myapp.Thing"/>
----
[[integration.appendix.xsd-schemas-jee-local-slsb]]
==== `<jee:local-slsb/>` (Simple)
The `<jee:local-slsb/>` element configures a reference to a local EJB Stateless Session Bean.
The following example shows how to configures a reference to a local EJB Stateless Session Bean
without `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="simple"
class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
<property name="jndiName" value="ejb/RentalServiceBean"/>
<property name="businessInterface" value="com.foo.service.RentalService"/>
</bean>
----
The following example shows how to configures a reference to a local EJB Stateless Session Bean
with `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<jee:local-slsb id="simpleSlsb" jndi-name="ejb/RentalServiceBean"
business-interface="com.foo.service.RentalService"/>
----
[[integration.appendix.xsd-schemas-jee-local-slsb-complex]]
==== `<jee:local-slsb/>` (Complex)
The `<jee:local-slsb/>` element configures a reference to a local EJB Stateless Session Bean.
The following example shows how to configures a reference to a local EJB Stateless Session Bean
and a number of properties without `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="complexLocalEjb"
class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
<property name="jndiName" value="ejb/RentalServiceBean"/>
<property name="businessInterface" value="com.example.service.RentalService"/>
<property name="cacheHome" value="true"/>
<property name="lookupHomeOnStartup" value="true"/>
<property name="resourceRef" value="true"/>
</bean>
----
The following example shows how to configures a reference to a local EJB Stateless Session Bean
and a number of properties with `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<jee:local-slsb id="complexLocalEjb"
jndi-name="ejb/RentalServiceBean"
business-interface="com.foo.service.RentalService"
cache-home="true"
lookup-home-on-startup="true"
resource-ref="true">
----
[[integration.appendix.xsd-schemas-jee-remote-slsb]]
==== <jee:remote-slsb/>
The `<jee:remote-slsb/>` element configures a reference to a `remote` EJB Stateless Session Bean.
The following example shows how to configures a reference to a remote EJB Stateless Session Bean
without `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="complexRemoteEjb"
class="org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean">
<property name="jndiName" value="ejb/MyRemoteBean"/>
<property name="businessInterface" value="com.foo.service.RentalService"/>
<property name="cacheHome" value="true"/>
<property name="lookupHomeOnStartup" value="true"/>
<property name="resourceRef" value="true"/>
<property name="homeInterface" value="com.foo.service.RentalService"/>
<property name="refreshHomeOnConnectFailure" value="true"/>
</bean>
----
The following example shows how to configures a reference to a remote EJB Stateless Session Bean
with `jee`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<jee:remote-slsb id="complexRemoteEjb"
jndi-name="ejb/MyRemoteBean"
business-interface="com.foo.service.RentalService"
cache-home="true"
lookup-home-on-startup="true"
resource-ref="true"
home-interface="com.foo.service.RentalService"
refresh-home-on-connect-failure="true">
----
[[integration.appendix.xsd-schemas-jms]]
=== The `jms` Schema
The `jms` elements deal with configuring JMS-related beans, such as Spring's
<<integration.adoc#jms-mdp, Message Listener Containers>>. These elements are detailed in the
section of the <<integration.adoc#jms, JMS chapter>> entitled <<integration.adoc#jms-namespace,
JMS Namespace Support>>. See that chapter for full details on this support
and the `jms` elements themselves.
In the interest of completeness, to use the elements in the `jms` schema, you need to have
the following preamble at the top of your Spring XML configuration file. The text in the
following snippet references the correct schema so that the elements in the `jms` namespace
are available to you:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?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:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jms
https://www.springframework.org/schema/jms/spring-jms.xsd">
<!-- bean definitions here -->
</beans>
----
[[integration.appendix.xsd-schemas-context-mbe]]
=== Using `<context:mbean-export/>`
This element is detailed in
<<integration.adoc#jmx-context-mbeanexport, Configuring Annotation-based MBean Export>>.
[[integration.appendix.xsd-schemas-cache]]
=== The `cache` Schema
You can use the `cache` elements to enable support for Spring's `@CacheEvict`, `@CachePut`,
and `@Caching` annotations. It it also supports declarative XML-based caching. See
<<integration.adoc#cache-annotation-enable, Enabling Caching Annotations>> and
<<integration.adoc#cache-declarative-xml, Declarative XML-based Caching>> for details.
To use the elements in the `cache` schema, you need to have the following preamble at the
top of your Spring XML configuration file. The text in the following snippet references
the correct schema so that the elements in the `cache` namespace are available to you:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache
https://www.springframework.org/schema/cache/spring-cache.xsd">
<!-- bean definitions here -->
</beans>
----
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,175 +0,0 @@
[[integration.observability]]
= Observability Support
Micrometer defines an https://micrometer.io/docs/observation[Observation concept that enables both Metrics and Traces] in applications.
Metrics support offers a way to create timers, gauges or counters for collecting statistics about the runtime behavior of your application.
Metrics can help you to track error rates, usage patterns, performance and more.
Traces provide a holistic view of an entire system, crossing application boundaries; you can zoom in on particular user requests and follow their entire completion across applications.
Spring Framework instruments various parts of its own codebase to publish observations if an `ObservationRegistry` is configured.
You can learn more about {docs-spring-boot}/html/actuator.html#actuator.metrics[configuring the observability infrastructure in Spring Boot].
[[integration.observability.concepts]]
== Micrometer Observation concepts
If you are not familiar with Micrometer Observation, here's a quick summary of the new concepts you should know about.
* `Observation` is the actual recording of something happening in your application. This is processed by `ObservationHandler` implementations to produce metrics or traces.
* Each observation has a corresponding `ObservationContext` implementation; this type holds all the relevant information for extracting metadata for it.
In the case of an HTTP server observation, the context implementation could hold the HTTP request, the HTTP response, any Exception thrown during processing...
* Each `Observation` holds `KeyValues` metadata. In the case of a server HTTP observation, this could be the HTTP request method, the HTTP response status...
This metadata is contributed by `ObservationConvention` implementations which should declare the type of `ObservationContext` they support.
* `KeyValues` are said to be "low cardinality" if there is a low, bounded number of possible values for the `KeyValue` tuple (HTTP method is a good example).
Low cardinality values are contributed to metrics only.
High cardinality values are on the other hand unbounded (for example, HTTP request URIs) and are only contributed to Traces.
* An `ObservationDocumentation` documents all observations in a particular domain, listing the expected key names and their meaning.
[[integration.observability.config]]
== Configuring Observations
Global configuration options are available at the `ObservationRegistry#observationConfig()` level.
Each instrumented component will provide two extension points:
* setting the `ObservationRegistry`; if not set, observations will not be recorded and will be no-ops
* providing a custom `ObservationConvention` to change the default observation name and extracted `KeyValues`
[[integration.observability.config.conventions]]
=== Using custom Observation conventions
Let's take the example of the Spring MVC "http.server.requests" metrics instrumentation with the `ServerHttpObservationFilter`.
This observation is using a `ServerRequestObservationConvention` with a `ServerRequestObservationContext`; custom conventions can be configured on the Servlet filter.
If you would like to customize the metadata produced with the observation, you can extend the `DefaultServerRequestObservationConvention` for your requirements:
include::code:ExtendedServerRequestObservationConvention[]
If you want full control, you can then implement the entire convention contract for the observation you're interested in:
include::code:CustomServerRequestObservationConvention[]
You can also achieve similar goals using a custom `ObservationFilter` - adding or removing key values for an observation.
Filters do not replace the default convention and are used as a post-processing component.
include::code:ServerRequestObservationFilter[]
You can configure `ObservationFilter` instances on the `ObservationRegistry`.
[[integration.observability.http-server]]
== HTTP Server instrumentation
HTTP server exchanges observations are created with the name `"http.server.requests"` for Servlet and Reactive applications.
[[integration.observability.http-server.servlet]]
=== Servlet applications
Applications need to configure the `org.springframework.web.filter.ServerHttpObservationFilter` Servlet filter in their application.
It is using the `org.springframework.http.server.observation.DefaultServerRequestObservationConvention` by default, backed by the `ServerRequestObservationContext`.
By default, the following `KeyValues` are created:
.Low cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`exception` _(required)_|Name of the exception thrown during the exchange, or `KeyValue#NONE_VALUE`} if no exception happened.
|`method` _(required)_|Name of HTTP request method or `"none"` if the request was not received properly.
|`outcome` _(required)_|Outcome of the HTTP server exchange.
|`status` _(required)_|HTTP response raw status code, or `"UNKNOWN"` if no response was created.
|`uri` _(required)_|URI pattern for the matching handler if available, falling back to `REDIRECTION` for 3xx responses, `NOT_FOUND` for 404 responses, `root` for requests with no path info, and `UNKNOWN` for all other requests.
|===
.High cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`http.url` _(required)_|HTTP request URI.
|===
[[integration.observability.http-server.reactive]]
=== Reactive applications
Applications need to configure the `org.springframework.web.filter.reactive.ServerHttpObservationFilter` reactive `WebFilter` in their application.
It is using the `org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention` by default, backed by the `ServerRequestObservationContext`.
By default, the following `KeyValues` are created:
.Low cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`exception` _(required)_|Name of the exception thrown during the exchange, or `"none"` if no exception happened.
|`method` _(required)_|Name of HTTP request method or `"none"` if the request was not received properly.
|`outcome` _(required)_|Outcome of the HTTP server exchange.
|`status` _(required)_|HTTP response raw status code, or `"UNKNOWN"` if no response was created.
|`uri` _(required)_|URI pattern for the matching handler if available, falling back to `REDIRECTION` for 3xx responses, `NOT_FOUND` for 404 responses, `root` for requests with no path info, and `UNKNOWN` for all other requests.
|===
.High cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`http.url` _(required)_|HTTP request URI.
|===
[[integration.observability.http-client]]
== HTTP Client instrumentation
HTTP client exchanges observations are created with the name `"http.client.requests"` for blocking and reactive clients.
Unlike their server counterparts, the instrumentation is implemented directly in the client so the only required step is to configure an `ObservationRegistry` on the client.
[[integration.observability.http-client.resttemplate]]
=== RestTemplate
Instrumentation is using the `org.springframework.http.client.observation.ClientRequestObservationConvention` by default, backed by the `ClientRequestObservationContext`.
.Low cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`exception` _(required)_|Name of the exception thrown during the exchange, or `"none"` if no exception happened.
|`method` _(required)_|Name of HTTP request method or `"none"` if the request could not be created.
|`outcome` _(required)_|Outcome of the HTTP client exchange.
|`status` _(required)_|HTTP response raw status code, or `"IO_ERROR"` in case of `IOException`, or `"CLIENT_ERROR"` if no response was received.
|`uri` _(required)_|URI template used for HTTP request, or `"none"` if none was provided.
|===
.High cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`client.name` _(required)_|Client name derived from the request URI host.
|`http.url` _(required)_|HTTP request URI.
|===
[[integration.observability.http-client.webclient]]
=== WebClient
Instrumentation is using the `org.springframework.web.reactive.function.client.ClientRequestObservationConvention` by default, backed by the `ClientRequestObservationContext`.
.Low cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`exception` _(required)_|Name of the exception thrown during the exchange, or `"none"` if no exception happened.
|`method` _(required)_|Name of HTTP request method or `"none"` if the request could not be created.
|`outcome` _(required)_|Outcome of the HTTP client exchange.
|`status` _(required)_|HTTP response raw status code, or `"IO_ERROR"` in case of `IOException`, or `"CLIENT_ERROR"` if no response was received.
|`uri` _(required)_|URI template used for HTTP request, or `"none"` if none was provided.
|===
.High cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`client.name` _(required)_|Client name derived from the request URI host.
|`http.url` _(required)_|HTTP request URI.
|===
@@ -1,517 +0,0 @@
[[rest-client-access]]
= REST Clients
The Spring Framework provides the following choices for making calls to REST endpoints:
* <<rest-webclient>> - non-blocking, reactive client w fluent API.
* <<rest-resttemplate>> - synchronous client with template method API.
* <<rest-http-interface>> - annotated interface with generated, dynamic proxy implementation.
[[rest-webclient]]
== `WebClient`
`WebClient` is a non-blocking, reactive client to perform HTTP requests. It was
introduced in 5.0 and offers an alternative to the `RestTemplate`, with support for
synchronous, asynchronous, and streaming scenarios.
`WebClient` supports the following:
* Non-blocking I/O.
* Reactive Streams back pressure.
* High concurrency with fewer hardware resources.
* Functional-style, fluent API that takes advantage of Java 8 lambdas.
* Synchronous and asynchronous interactions.
* Streaming up to or streaming down from a server.
See <<web-reactive.adoc#webflux-client, WebClient>> for more details.
[[rest-resttemplate]]
== `RestTemplate`
The `RestTemplate` provides a higher level API over HTTP client libraries. It makes it
easy to invoke REST endpoints in a single line. It exposes the following groups of
overloaded methods:
NOTE: `RestTemplate` is in maintenance mode, with only requests for minor
changes and bugs to be accepted. Please, consider using the
<<web-reactive.adoc#webflux-client, WebClient>> instead.
[[rest-overview-of-resttemplate-methods-tbl]]
.RestTemplate methods
[cols="1,3"]
|===
| Method group | Description
| `getForObject`
| Retrieves a representation via GET.
| `getForEntity`
| Retrieves a `ResponseEntity` (that is, status, headers, and body) by using GET.
| `headForHeaders`
| Retrieves all headers for a resource by using HEAD.
| `postForLocation`
| Creates a new resource by using POST and returns the `Location` header from the response.
| `postForObject`
| Creates a new resource by using POST and returns the representation from the response.
| `postForEntity`
| Creates a new resource by using POST and returns the representation from the response.
| `put`
| Creates or updates a resource by using PUT.
| `patchForObject`
| Updates a resource by using PATCH and returns the representation from the response.
Note that the JDK `HttpURLConnection` does not support `PATCH`, but Apache
HttpComponents and others do.
| `delete`
| Deletes the resources at the specified URI by using DELETE.
| `optionsForAllow`
| Retrieves allowed HTTP methods for a resource by using ALLOW.
| `exchange`
| More generalized (and less opinionated) version of the preceding methods that provides extra
flexibility when needed. It accepts a `RequestEntity` (including HTTP method, URL, headers,
and body as input) and returns a `ResponseEntity`.
These methods allow the use of `ParameterizedTypeReference` instead of `Class` to specify
a response type with generics.
| `execute`
| The most generalized way to perform a request, with full control over request
preparation and response extraction through callback interfaces.
|===
[[rest-resttemplate-create]]
=== Initialization
The default constructor uses `java.net.HttpURLConnection` to perform requests. You can
switch to a different HTTP library with an implementation of `ClientHttpRequestFactory`.
There is built-in support for the following:
* Apache HttpComponents
* Netty
* OkHttp
For example, to switch to Apache HttpComponents, you can use the following:
[source,java,indent=0,subs="verbatim,quotes"]
----
RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
----
Each `ClientHttpRequestFactory` exposes configuration options specific to the underlying
HTTP client library -- for example, for credentials, connection pooling, and other details.
TIP: Note that the `java.net` implementation for HTTP requests can raise an exception when
accessing the status of a response that represents an error (such as 401). If this is an
issue, switch to another HTTP client library.
[[rest-resttemplate-uri]]
==== URIs
Many of the `RestTemplate` methods accept a URI template and URI template variables,
either as a `String` variable argument, or as `Map<String,String>`.
The following example uses a `String` variable argument:
[source,java,indent=0,subs="verbatim,quotes"]
----
String result = restTemplate.getForObject(
"https://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");
----
The following example uses a `Map<String, String>`:
[source,java,indent=0,subs="verbatim,quotes"]
----
Map<String, String> vars = Collections.singletonMap("hotel", "42");
String result = restTemplate.getForObject(
"https://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
----
Keep in mind URI templates are automatically encoded, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
restTemplate.getForObject("https://example.com/hotel list", String.class);
// Results in request to "https://example.com/hotel%20list"
----
You can use the `uriTemplateHandler` property of `RestTemplate` to customize how URIs
are encoded. Alternatively, you can prepare a `java.net.URI` and pass it into one of
the `RestTemplate` methods that accepts a `URI`.
For more details on working with and encoding URIs, see <<web.adoc#mvc-uri-building, URI Links>>.
[[rest-template-headers]]
==== Headers
You can use the `exchange()` methods to specify request headers, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
String uriTemplate = "https://example.com/hotels/{hotel}";
URI uri = UriComponentsBuilder.fromUriString(uriTemplate).build(42);
RequestEntity<Void> requestEntity = RequestEntity.get(uri)
.header("MyRequestHeader", "MyValue")
.build();
ResponseEntity<String> response = template.exchange(requestEntity, String.class);
String responseHeader = response.getHeaders().getFirst("MyResponseHeader");
String body = response.getBody();
----
You can obtain response headers through many `RestTemplate` method variants that return
`ResponseEntity`.
[[rest-template-body]]
=== Body
Objects passed into and returned from `RestTemplate` methods are converted to and from raw
content with the help of an `HttpMessageConverter`.
On a POST, an input object is serialized to the request body, as the following example shows:
----
URI location = template.postForLocation("https://example.com/people", person);
----
You need not explicitly set the Content-Type header of the request. In most cases,
you can find a compatible message converter based on the source `Object` type, and the chosen
message converter sets the content type accordingly. If necessary, you can use the
`exchange` methods to explicitly provide the `Content-Type` request header, and that, in
turn, influences what message converter is selected.
On a GET, the body of the response is deserialized to an output `Object`, as the following example shows:
----
Person person = restTemplate.getForObject("https://example.com/people/{id}", Person.class, 42);
----
The `Accept` header of the request does not need to be explicitly set. In most cases,
a compatible message converter can be found based on the expected response type, which
then helps to populate the `Accept` header. If necessary, you can use the `exchange`
methods to provide the `Accept` header explicitly.
By default, `RestTemplate` registers all built-in
<<rest-message-conversion, message converters>>, depending on classpath checks that help
to determine what optional conversion libraries are present. You can also set the message
converters to use explicitly.
[[rest-message-conversion]]
==== Message Conversion
[.small]#<<web-reactive.adoc#webflux-codecs, WebFlux>>#
The `spring-web` module contains the `HttpMessageConverter` contract for reading and
writing the body of HTTP requests and responses through `InputStream` and `OutputStream`.
`HttpMessageConverter` instances are used on the client side (for example, in the `RestTemplate`) and
on the server side (for example, in Spring MVC REST controllers).
Concrete implementations for the main media (MIME) types are provided in the framework
and are, by default, registered with the `RestTemplate` on the client side and with
`RequestMappingHandlerAdapter` on the server side (see
<<web.adoc#mvc-config-message-converters, Configuring Message Converters>>).
The implementations of `HttpMessageConverter` are described in the following sections.
For all converters, a default media type is used, but you can override it by setting the
`supportedMediaTypes` bean property. The following table describes each implementation:
[[rest-message-converters-tbl]]
.HttpMessageConverter Implementations
[cols="1,3"]
|===
| MessageConverter | Description
| `StringHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write `String` instances from the HTTP
request and response. By default, this converter supports all text media types
(`text/{asterisk}`) and writes with a `Content-Type` of `text/plain`.
| `FormHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write form data from the HTTP
request and response. By default, this converter reads and writes the
`application/x-www-form-urlencoded` media type. Form data is read from and written into a
`MultiValueMap<String, String>`. The converter can also write (but not read) multipart
data read from a `MultiValueMap<String, Object>`. By default, `multipart/form-data` is
supported. As of Spring Framework 5.2, additional multipart subtypes can be supported for
writing form data. Consult the javadoc for `FormHttpMessageConverter` for further details.
| `ByteArrayHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write byte arrays from the
HTTP request and response. By default, this converter supports all media types (`{asterisk}/{asterisk}`)
and writes with a `Content-Type` of `application/octet-stream`. You can override this
by setting the `supportedMediaTypes` property and overriding `getContentType(byte[])`.
| `MarshallingHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write XML by using Spring's
`Marshaller` and `Unmarshaller` abstractions from the `org.springframework.oxm` package.
This converter requires a `Marshaller` and `Unmarshaller` before it can be used. You can inject these
through constructor or bean properties. By default, this converter supports
`text/xml` and `application/xml`.
| `MappingJackson2HttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write JSON by using Jackson's
`ObjectMapper`. You can customize JSON mapping as needed through the use of Jackson's
provided annotations. When you need further control (for cases where custom JSON
serializers/deserializers need to be provided for specific types), you can inject a custom `ObjectMapper`
through the `ObjectMapper` property. By default, this
converter supports `application/json`.
| `MappingJackson2XmlHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write XML by using
https://github.com/FasterXML/jackson-dataformat-xml[Jackson XML] extension's
`XmlMapper`. You can customize XML mapping as needed through the use of JAXB
or Jackson's provided annotations. When you need further control (for cases where custom XML
serializers/deserializers need to be provided for specific types), you can inject a custom `XmlMapper`
through the `ObjectMapper` property. By default, this
converter supports `application/xml`.
| `SourceHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write
`javax.xml.transform.Source` from the HTTP request and response. Only `DOMSource`,
`SAXSource`, and `StreamSource` are supported. By default, this converter supports
`text/xml` and `application/xml`.
| `BufferedImageHttpMessageConverter`
| An `HttpMessageConverter` implementation that can read and write
`java.awt.image.BufferedImage` from the HTTP request and response. This converter reads
and writes the media type supported by the Java I/O API.
|===
[[rest-template-jsonview]]
=== Jackson JSON Views
You can specify a https://www.baeldung.com/jackson-json-view-annotation[Jackson JSON View]
to serialize only a subset of the object properties, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
MappingJacksonValue value = new MappingJacksonValue(new User("eric", "7!jd#h23"));
value.setSerializationView(User.WithoutPasswordView.class);
RequestEntity<MappingJacksonValue> requestEntity =
RequestEntity.post(new URI("https://example.com/user")).body(value);
ResponseEntity<String> response = template.exchange(requestEntity, String.class);
----
[[rest-template-multipart]]
=== Multipart
To send multipart data, you need to provide a `MultiValueMap<String, Object>` whose values
may be an `Object` for part content, a `Resource` for a file part, or an `HttpEntity` for
part content with headers. For example:
[source,java,indent=0,subs="verbatim,quotes"]
----
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
parts.add("fieldPart", "fieldValue");
parts.add("filePart", new FileSystemResource("...logo.png"));
parts.add("jsonPart", new Person("Jason"));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
parts.add("xmlPart", new HttpEntity<>(myBean, headers));
----
In most cases, you do not have to specify the `Content-Type` for each part. The content
type is determined automatically based on the `HttpMessageConverter` chosen to serialize
it or, in the case of a `Resource` based on the file extension. If necessary, you can
explicitly provide the `MediaType` with an `HttpEntity` wrapper.
Once the `MultiValueMap` is ready, you can pass it to the `RestTemplate`, as show below:
[source,java,indent=0,subs="verbatim,quotes"]
----
MultiValueMap<String, Object> parts = ...;
template.postForObject("https://example.com/upload", parts, Void.class);
----
If the `MultiValueMap` contains at least one non-`String` value, the `Content-Type` is set
to `multipart/form-data` by the `FormHttpMessageConverter`. If the `MultiValueMap` has
`String` values the `Content-Type` is defaulted to `application/x-www-form-urlencoded`.
If necessary the `Content-Type` may also be set explicitly.
[[rest-http-interface]]
== HTTP Interface
The Spring Framework lets you define an HTTP service as a Java interface with annotated
methods for HTTP exchanges. You can then generate a proxy that implements this interface
and performs the exchanges. This helps to simplify HTTP remote access which often
involves a facade that wraps the details of using the underlying HTTP client.
One, declare an interface with `@HttpExchange` methods:
[source,java,indent=0,subs="verbatim,quotes"]
----
interface RepositoryService {
@GetExchange("/repos/{owner}/{repo}")
Repository getRepository(@PathVariable String owner, @PathVariable String repo);
// more HTTP exchange methods...
}
----
Two, create a proxy that will perform the declared HTTP exchanges:
[source,java,indent=0,subs="verbatim,quotes"]
----
WebClient client = WebClient.builder().baseUrl("https://api.github.com/").build();
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
RepositoryService service = factory.createClient(RepositoryService.class);
----
`@HttpExchange` is supported at the type level where it applies to all methods:
[source,java,indent=0,subs="verbatim,quotes"]
----
@HttpExchange(url = "/repos/{owner}/{repo}", accept = "application/vnd.github.v3+json")
interface RepositoryService {
@GetExchange
Repository getRepository(@PathVariable String owner, @PathVariable String repo);
@PatchExchange(contentType = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
void updateRepository(@PathVariable String owner, @PathVariable String repo,
@RequestParam String name, @RequestParam String description, @RequestParam String homepage);
}
----
[[rest-http-interface-method-parameters]]
=== Method Parameters
Annotated, HTTP exchange methods support flexible method signatures with the following
method parameters:
[cols="1,2", options="header"]
|===
| Method argument | Description
| `URI`
| Dynamically set the URL for the request, overriding the annotation's `url` attribute.
| `HttpMethod`
| Dynamically set the HTTP method for the request, overriding the annotation's `method` attribute
| `@RequestHeader`
| Add a request header or mutliple headers. The argument may be a `Map<String, ?>` or
`MultiValueMap<String, ?>` with multiple headers, a `Collection<?>` of values, or an
individual value. Type conversion is supported for non-String values.
| `@PathVariable`
| Add a variable for expand a placeholder in the request URL. The argument may be a
`Map<String, ?>` with multiple variables, or an individual value. Type conversion
is supported for non-String values.
| `@RequestBody`
| Provide the body of the request either as an Object to be serialized, or a
Reactive Streams `Publisher` such as `Mono`, `Flux`, or any other async type supported
through the configured `ReactiveAdapterRegistry`.
| `@RequestParam`
| Add a request parameter or mutliple parameters. The argument may be a `Map<String, ?>`
or `MultiValueMap<String, ?>` with multiple parameters, a `Collection<?>` of values, or
an individual value. Type conversion is supported for non-String values.
When `"content-type"` is set to `"application/x-www-form-urlencoded"`, request
parameters are encoded in the request body. Otherwise, they are added as URL query
parameters.
| `@RequestPart`
| Add a request part, which may be a String (form field), `Resource` (file part),
Object (entity to be encoded, e.g. as JSON), `HttpEntity` (part content and headers),
a Spring `Part`, or Reactive Streams `Publisher` of any of the above.
| `@CookieValue`
| Add a cookie or mutliple cookies. The argument may be a `Map<String, ?>` or
`MultiValueMap<String, ?>` with multiple cookies, a `Collection<?>` of values, or an
individual value. Type conversion is supported for non-String values.
|===
[[rest-http-interface-return-values]]
=== Return Values
Annotated, HTTP exchange methods support the following return values:
[cols="1,2", options="header"]
|===
| Method return value | Description
| `void`, `Mono<Void>`
| Perform the given request, and release the response content, if any.
| `HttpHeaders`, `Mono<HttpHeaders>`
| Perform the given request, release the response content, if any, and return the
response headers.
| `<T>`, `Mono<T>`
| Perform the given request and decode the response content to the declared return type.
| `<T>`, `Flux<T>`
| Perform the given request and decode the response content to a stream of the declared
element type.
| `ResponseEntity<Void>`, `Mono<ResponseEntity<Void>>`
| Perform the given request, and release the response content, if any, and return a
`ResponseEntity` with the status and headers.
| `ResponseEntity<T>`, `Mono<ResponseEntity<T>>`
| Perform the given request, decode the response content to the declared return type, and
return a `ResponseEntity` with the status, headers, and the decoded body.
| `Mono<ResponseEntity<Flux<T>>`
| Perform the given request, decode the response content to a stream of the declared
element type, and return a `ResponseEntity` with the status, headers, and the decoded
response body stream.
|===
TIP: You can also use any other async or reactive types registered in the
`ReactiveAdapterRegistry`.
[[rest-http-interface-exceptions]]
=== Exception Handling
By default, `WebClient` raises `WebClientResponseException` for 4xx and 5xx HTTP status
codes. To customize this, you can register a response status handler that applies to all
responses performed through the client:
[source,java,indent=0,subs="verbatim,quotes"]
----
WebClient webClient = WebClient.builder()
.defaultStatusHandler(HttpStatusCode::isError, resp -> ...)
.build();
WebClientAdapter clientAdapter = WebClientAdapter.forClient(webClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory
.builder(clientAdapter).build();
----
For more details and options, such as suppressing error status codes, see the Javadoc of
`defaultStatusHandler` in `WebClient.Builder`.
@@ -1,967 +0,0 @@
[[scheduling]]
= Task Execution and Scheduling
The Spring Framework provides abstractions for the asynchronous execution and scheduling of
tasks with the `TaskExecutor` and `TaskScheduler` interfaces, respectively. Spring also
features implementations of those interfaces that support thread pools or delegation to
CommonJ within an application server environment. Ultimately, the use of these
implementations behind the common interfaces abstracts away the differences between Java
SE 5, Java SE 6, and Jakarta EE environments.
Spring also features integration classes to support scheduling with the `Timer`
(part of the JDK since 1.3) and the Quartz Scheduler ( https://www.quartz-scheduler.org/[]).
You can set up both of those schedulers by using a `FactoryBean` with optional references to
`Timer` or `Trigger` instances, respectively. Furthermore, a convenience class for both
the Quartz Scheduler and the `Timer` is available that lets you invoke a method of
an existing target object (analogous to the normal `MethodInvokingFactoryBean`
operation).
[[scheduling-task-executor]]
== The Spring `TaskExecutor` Abstraction
Executors are the JDK name for the concept of thread pools. The "`executor`" naming is
due to the fact that there is no guarantee that the underlying implementation is
actually a pool. An executor may be single-threaded or even synchronous. Spring's
abstraction hides implementation details between the Java SE and Jakarta EE environments.
Spring's `TaskExecutor` interface is identical to the `java.util.concurrent.Executor`
interface. In fact, originally, its primary reason for existence was to abstract away
the need for Java 5 when using thread pools. The interface has a single method
(`execute(Runnable task)`) that accepts a task for execution based on the semantics
and configuration of the thread pool.
The `TaskExecutor` was originally created to give other Spring components an abstraction
for thread pooling where needed. Components such as the `ApplicationEventMulticaster`,
JMS's `AbstractMessageListenerContainer`, and Quartz integration all use the
`TaskExecutor` abstraction to pool threads. However, if your beans need thread pooling
behavior, you can also use this abstraction for your own needs.
[[scheduling-task-executor-types]]
=== `TaskExecutor` Types
Spring includes a number of pre-built implementations of `TaskExecutor`.
In all likelihood, you should never need to implement your own.
The variants that Spring provides are as follows:
* `SyncTaskExecutor`:
This implementation does not run invocations asynchronously. Instead, each
invocation takes place in the calling thread. It is primarily used in situations
where multi-threading is not necessary, such as in simple test cases.
* `SimpleAsyncTaskExecutor`:
This implementation does not reuse any threads. Rather, it starts up a new thread
for each invocation. However, it does support a concurrency limit that blocks
any invocations that are over the limit until a slot has been freed up. If you
are looking for true pooling, see `ThreadPoolTaskExecutor`, later in this list.
* `ConcurrentTaskExecutor`:
This implementation is an adapter for a `java.util.concurrent.Executor` instance.
There is an alternative (`ThreadPoolTaskExecutor`) that exposes the `Executor`
configuration parameters as bean properties. There is rarely a need to use
`ConcurrentTaskExecutor` directly. However, if the `ThreadPoolTaskExecutor` is not
flexible enough for your needs, `ConcurrentTaskExecutor` is an alternative.
* `ThreadPoolTaskExecutor`:
This implementation is most commonly used. It exposes bean properties for
configuring a `java.util.concurrent.ThreadPoolExecutor` and wraps it in a `TaskExecutor`.
If you need to adapt to a different kind of `java.util.concurrent.Executor`, we
recommend that you use a `ConcurrentTaskExecutor` instead.
* `DefaultManagedTaskExecutor`:
This implementation uses a JNDI-obtained `ManagedExecutorService` in a JSR-236
compatible runtime environment (such as a Jakarta EE application server),
replacing a CommonJ WorkManager for that purpose.
[[scheduling-task-executor-usage]]
=== Using a `TaskExecutor`
Spring's `TaskExecutor` implementations are used as simple JavaBeans. In the following example,
we define a bean that uses the `ThreadPoolTaskExecutor` to asynchronously print
out a set of messages:
[source,java,indent=0,subs="verbatim,quotes"]
----
import org.springframework.core.task.TaskExecutor;
public class TaskExecutorExample {
private class MessagePrinterTask implements Runnable {
private String message;
public MessagePrinterTask(String message) {
this.message = message;
}
public void run() {
System.out.println(message);
}
}
private TaskExecutor taskExecutor;
public TaskExecutorExample(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
public void printMessages() {
for(int i = 0; i < 25; i++) {
taskExecutor.execute(new MessagePrinterTask("Message" + i));
}
}
}
----
As you can see, rather than retrieving a thread from the pool and executing it yourself,
you add your `Runnable` to the queue. Then the `TaskExecutor` uses its internal rules to
decide when the task gets run.
To configure the rules that the `TaskExecutor` uses, we expose simple bean properties:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5"/>
<property name="maxPoolSize" value="10"/>
<property name="queueCapacity" value="25"/>
</bean>
<bean id="taskExecutorExample" class="TaskExecutorExample">
<constructor-arg ref="taskExecutor"/>
</bean>
----
[[scheduling-task-scheduler]]
== The Spring `TaskScheduler` Abstraction
In addition to the `TaskExecutor` abstraction, Spring 3.0 introduced a `TaskScheduler`
with a variety of methods for scheduling tasks to run at some point in the future.
The following listing shows the `TaskScheduler` interface definition:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface TaskScheduler {
ScheduledFuture schedule(Runnable task, Trigger trigger);
ScheduledFuture schedule(Runnable task, Instant startTime);
ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period);
ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period);
ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay);
ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay);
----
The simplest method is the one named `schedule` that takes only a `Runnable` and an `Instant`.
That causes the task to run once after the specified time. All of the other methods
are capable of scheduling tasks to run repeatedly. The fixed-rate and fixed-delay
methods are for simple, periodic execution, but the method that accepts a `Trigger` is
much more flexible.
[[scheduling-trigger-interface]]
=== `Trigger` Interface
The `Trigger` interface is essentially inspired by JSR-236 which, as of Spring 3.0,
was not yet officially implemented. The basic idea of the `Trigger` is that execution
times may be determined based on past execution outcomes or even arbitrary conditions.
If these determinations do take into account the outcome of the preceding execution,
that information is available within a `TriggerContext`. The `Trigger` interface itself
is quite simple, as the following listing shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface Trigger {
Date nextExecutionTime(TriggerContext triggerContext);
}
----
The `TriggerContext` is the most important part. It encapsulates all of
the relevant data and is open for extension in the future, if necessary. The
`TriggerContext` is an interface (a `SimpleTriggerContext` implementation is used by
default). The following listing shows the available methods for `Trigger` implementations.
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface TriggerContext {
Date lastScheduledExecutionTime();
Date lastActualExecutionTime();
Date lastCompletionTime();
}
----
[[scheduling-trigger-implementations]]
=== `Trigger` Implementations
Spring provides two implementations of the `Trigger` interface. The most interesting one
is the `CronTrigger`. It enables the scheduling of tasks based on
<<scheduling-cron-expression,cron expressions>>.
For example, the following task is scheduled to run 15 minutes past each hour but only
during the 9-to-5 "`business hours`" on weekdays:
[source,java,indent=0]
[subs="verbatim"]
----
scheduler.schedule(task, new CronTrigger("0 15 9-17 * * MON-FRI"));
----
The other implementation is a `PeriodicTrigger` that accepts a fixed
period, an optional initial delay value, and a boolean to indicate whether the period
should be interpreted as a fixed-rate or a fixed-delay. Since the `TaskScheduler`
interface already defines methods for scheduling tasks at a fixed rate or with a
fixed delay, those methods should be used directly whenever possible. The value of the
`PeriodicTrigger` implementation is that you can use it within components that rely on
the `Trigger` abstraction. For example, it may be convenient to allow periodic triggers,
cron-based triggers, and even custom trigger implementations to be used interchangeably.
Such a component could take advantage of dependency injection so that you can configure such `Triggers`
externally and, therefore, easily modify or extend them.
[[scheduling-task-scheduler-implementations]]
=== `TaskScheduler` implementations
As with Spring's `TaskExecutor` abstraction, the primary benefit of the `TaskScheduler`
arrangement is that an application's scheduling needs are decoupled from the deployment
environment. This abstraction level is particularly relevant when deploying to an
application server environment where threads should not be created directly by the
application itself. For such scenarios, Spring provides a `TimerManagerTaskScheduler`
that delegates to a CommonJ `TimerManager` on WebLogic or WebSphere as well as a more recent
`DefaultManagedTaskScheduler` that delegates to a JSR-236 `ManagedScheduledExecutorService`
in a Jakarta EE environment. Both are typically configured with a JNDI lookup.
Whenever external thread management is not a requirement, a simpler alternative is
a local `ScheduledExecutorService` setup within the application, which can be adapted
through Spring's `ConcurrentTaskScheduler`. As a convenience, Spring also provides a
`ThreadPoolTaskScheduler`, which internally delegates to a `ScheduledExecutorService`
to provide common bean-style configuration along the lines of `ThreadPoolTaskExecutor`.
These variants work perfectly fine for locally embedded thread pool setups in lenient
application server environments, as well -- in particular on Tomcat and Jetty.
[[scheduling-annotation-support]]
== Annotation Support for Scheduling and Asynchronous Execution
Spring provides annotation support for both task scheduling and asynchronous method
execution.
[[scheduling-enable-annotation-support]]
=== Enable Scheduling Annotations
To enable support for `@Scheduled` and `@Async` annotations, you can add `@EnableScheduling` and
`@EnableAsync` to one of your `@Configuration` classes, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableAsync
@EnableScheduling
public class AppConfig {
}
----
You can pick and choose the relevant annotations for your application. For example,
if you need only support for `@Scheduled`, you can omit `@EnableAsync`. For more
fine-grained control, you can additionally implement the `SchedulingConfigurer`
interface, the `AsyncConfigurer` interface, or both. See the
{api-spring-framework}/scheduling/annotation/SchedulingConfigurer.html[`SchedulingConfigurer`]
and {api-spring-framework}/scheduling/annotation/AsyncConfigurer.html[`AsyncConfigurer`]
javadoc for full details.
If you prefer XML configuration, you can use the `<task:annotation-driven>` element,
as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="5"/>
<task:scheduler id="myScheduler" pool-size="10"/>
----
Note that, with the preceding XML, an executor reference is provided for handling those
tasks that correspond to methods with the `@Async` annotation, and the scheduler
reference is provided for managing those methods annotated with `@Scheduled`.
NOTE: The default advice mode for processing `@Async` annotations is `proxy` which allows
for interception of calls through the proxy only. Local calls within the same class
cannot get intercepted that way. For a more advanced mode of interception, consider
switching to `aspectj` mode in combination with compile-time or load-time weaving.
[[scheduling-annotation-support-scheduled]]
=== The `@Scheduled` annotation
You can add the `@Scheduled` annotation to a method, along with trigger metadata. For
example, the following method is invoked every five seconds (5000 milliseconds) with a
fixed delay, meaning that the period is measured from the completion time of each
preceding invocation.
[source,java,indent=0,subs="verbatim,quotes"]
----
@Scheduled(fixedDelay = 5000)
public void doSomething() {
// something that should run periodically
}
----
[NOTE]
====
By default, milliseconds will be used as the time unit for fixed delay, fixed rate, and
initial delay values. If you would like to use a different time unit such as seconds or
minutes, you can configure this via the `timeUnit` attribute in `@Scheduled`.
For example, the previous example can also be written as follows.
[source,java,indent=0,subs="verbatim,quotes"]
----
@Scheduled(fixedDelay = 5, timeUnit = TimeUnit.SECONDS)
public void doSomething() {
// something that should run periodically
}
----
====
If you need a fixed-rate execution, you can use the `fixedRate` attribute within the
annotation. The following method is invoked every five seconds (measured between the
successive start times of each invocation).
[source,java,indent=0,subs="verbatim,quotes"]
----
@Scheduled(fixedRate = 5, timeUnit = TimeUnit.SECONDS)
public void doSomething() {
// something that should run periodically
}
----
For fixed-delay and fixed-rate tasks, you can specify an initial delay by indicating the
amount of time to wait before the first execution of the method, as the following
`fixedRate` example shows.
[source,java,indent=0,subs="verbatim,quotes"]
----
@Scheduled(initialDelay = 1000, fixedRate = 5000)
public void doSomething() {
// something that should run periodically
}
----
If simple periodic scheduling is not expressive enough, you can provide a
<<scheduling-cron-expression,cron expression>>.
The following example runs only on weekdays:
[source,java,indent=0]
[subs="verbatim"]
----
@Scheduled(cron="*/5 * * * * MON-FRI")
public void doSomething() {
// something that should run on weekdays only
}
----
TIP: You can also use the `zone` attribute to specify the time zone in which the cron
expression is resolved.
Notice that the methods to be scheduled must have void returns and must not accept any
arguments. If the method needs to interact with other objects from the application
context, those would typically have been provided through dependency injection.
[NOTE]
====
As of Spring Framework 4.3, `@Scheduled` methods are supported on beans of any scope.
Make sure that you are not initializing multiple instances of the same `@Scheduled`
annotation class at runtime, unless you do want to schedule callbacks to each such
instance. Related to this, make sure that you do not use `@Configurable` on bean
classes that are annotated with `@Scheduled` and registered as regular Spring beans
with the container. Otherwise, you would get double initialization (once through the
container and once through the `@Configurable` aspect), with the consequence of each
`@Scheduled` method being invoked twice.
====
[[scheduling-annotation-support-async]]
=== The `@Async` annotation
You can provide the `@Async` annotation on a method so that invocation of that method
occurs asynchronously. In other words, the caller returns immediately upon
invocation, while the actual execution of the method occurs in a task that has been
submitted to a Spring `TaskExecutor`. In the simplest case, you can apply the annotation
to a method that returns `void`, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Async
void doSomething() {
// this will be run asynchronously
}
----
Unlike the methods annotated with the `@Scheduled` annotation, these methods can expect
arguments, because they are invoked in the "`normal`" way by callers at runtime rather
than from a scheduled task being managed by the container. For example, the following code is
a legitimate application of the `@Async` annotation:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Async
void doSomething(String s) {
// this will be run asynchronously
}
----
Even methods that return a value can be invoked asynchronously. However, such methods
are required to have a `Future`-typed return value. This still provides the benefit of
asynchronous execution so that the caller can perform other tasks prior to calling
`get()` on that `Future`. The following example shows how to use `@Async` on a method
that returns a value:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Async
Future<String> returnSomething(int i) {
// this will be run asynchronously
}
----
TIP: `@Async` methods may not only declare a regular `java.util.concurrent.Future` return type
but also Spring's `org.springframework.util.concurrent.ListenableFuture` or, as of Spring
4.2, JDK 8's `java.util.concurrent.CompletableFuture`, for richer interaction with the
asynchronous task and for immediate composition with further processing steps.
You can not use `@Async` in conjunction with lifecycle callbacks such as
`@PostConstruct`. To asynchronously initialize Spring beans, you currently have to use
a separate initializing Spring bean that then invokes the `@Async` annotated method on the
target, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
public class SampleBeanImpl implements SampleBean {
@Async
void doSomething() {
// ...
}
}
public class SampleBeanInitializer {
private final SampleBean bean;
public SampleBeanInitializer(SampleBean bean) {
this.bean = bean;
}
@PostConstruct
public void initialize() {
bean.doSomething();
}
}
----
NOTE: There is no direct XML equivalent for `@Async`, since such methods should be designed
for asynchronous execution in the first place, not externally re-declared to be asynchronous.
However, you can manually set up Spring's `AsyncExecutionInterceptor` with Spring AOP,
in combination with a custom pointcut.
[[scheduling-annotation-support-qualification]]
=== Executor Qualification with `@Async`
By default, when specifying `@Async` on a method, the executor that is used is the
one <<scheduling-enable-annotation-support, configured when enabling async support>>,
i.e. the "`annotation-driven`" element if you are using XML or your `AsyncConfigurer`
implementation, if any. However, you can use the `value` attribute of the `@Async`
annotation when you need to indicate that an executor other than the default should be
used when executing a given method. The following example shows how to do so:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Async("otherExecutor")
void doSomething(String s) {
// this will be run asynchronously by "otherExecutor"
}
----
In this case, `"otherExecutor"` can be the name of any `Executor` bean in the Spring
container, or it may be the name of a qualifier associated with any `Executor` (for example, as
specified with the `<qualifier>` element or Spring's `@Qualifier` annotation).
[[scheduling-annotation-support-exception]]
=== Exception Management with `@Async`
When an `@Async` method has a `Future`-typed return value, it is easy to manage
an exception that was thrown during the method execution, as this exception is
thrown when calling `get` on the `Future` result. With a `void` return type,
however, the exception is uncaught and cannot be transmitted. You can provide an
`AsyncUncaughtExceptionHandler` to handle such exceptions. The following example shows
how to do so:
[source,java,indent=0,subs="verbatim,quotes"]
----
public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
// handle exception
}
}
----
By default, the exception is merely logged. You can define a custom `AsyncUncaughtExceptionHandler`
by using `AsyncConfigurer` or the `<task:annotation-driven/>` XML element.
[[scheduling-task-namespace]]
== The `task` Namespace
As of version 3.0, Spring includes an XML namespace for configuring `TaskExecutor` and
`TaskScheduler` instances. It also provides a convenient way to configure tasks to be
scheduled with a trigger.
[[scheduling-task-namespace-scheduler]]
=== The 'scheduler' Element
The following element creates a `ThreadPoolTaskScheduler` instance with the
specified thread pool size:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<task:scheduler id="scheduler" pool-size="10"/>
----
The value provided for the `id` attribute is used as the prefix for thread names
within the pool. The `scheduler` element is relatively straightforward. If you do not
provide a `pool-size` attribute, the default thread pool has only a single thread.
There are no other configuration options for the scheduler.
[[scheduling-task-namespace-executor]]
=== The `executor` Element
The following creates a `ThreadPoolTaskExecutor` instance:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<task:executor id="executor" pool-size="10"/>
----
As with the scheduler shown in the <<scheduling-task-namespace-scheduler, previous section>>,
the value provided for the `id` attribute is used as the prefix for thread names within
the pool. As far as the pool size is concerned, the `executor` element supports more
configuration options than the `scheduler` element. For one thing, the thread pool for
a `ThreadPoolTaskExecutor` is itself more configurable. Rather than only a single size,
an executor's thread pool can have different values for the core and the max size.
If you provide a single value, the executor has a fixed-size thread pool (the core and
max sizes are the same). However, the `executor` element's `pool-size` attribute also
accepts a range in the form of `min-max`. The following example sets a minimum value of
`5` and a maximum value of `25`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<task:executor
id="executorWithPoolSizeRange"
pool-size="5-25"
queue-capacity="100"/>
----
In the preceding configuration, a `queue-capacity` value has also been provided.
The configuration of the thread pool should also be considered in light of the
executor's queue capacity. For the full description of the relationship between pool
size and queue capacity, see the documentation for
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadPoolExecutor.html[`ThreadPoolExecutor`].
The main idea is that, when a task is submitted, the executor first tries to use a
free thread if the number of active threads is currently less than the core size.
If the core size has been reached, the task is added to the queue, as long as its
capacity has not yet been reached. Only then, if the queue's capacity has been
reached, does the executor create a new thread beyond the core size. If the max size
has also been reached, then the executor rejects the task.
By default, the queue is unbounded, but this is rarely the desired configuration,
because it can lead to `OutOfMemoryErrors` if enough tasks are added to that queue while
all pool threads are busy. Furthermore, if the queue is unbounded, the max size has
no effect at all. Since the executor always tries the queue before creating a new
thread beyond the core size, a queue must have a finite capacity for the thread pool to
grow beyond the core size (this is why a fixed-size pool is the only sensible case
when using an unbounded queue).
Consider the case, as mentioned above, when a task is rejected. By default, when a
task is rejected, a thread pool executor throws a `TaskRejectedException`. However,
the rejection policy is actually configurable. The exception is thrown when using
the default rejection policy, which is the `AbortPolicy` implementation.
For applications where some tasks can be skipped under heavy load, you can instead
configure either `DiscardPolicy` or `DiscardOldestPolicy`. Another option that works
well for applications that need to throttle the submitted tasks under heavy load is
the `CallerRunsPolicy`. Instead of throwing an exception or discarding tasks,
that policy forces the thread that is calling the submit method to run the task itself.
The idea is that such a caller is busy while running that task and not able to submit
other tasks immediately. Therefore, it provides a simple way to throttle the incoming
load while maintaining the limits of the thread pool and queue. Typically, this allows
the executor to "`catch up`" on the tasks it is handling and thereby frees up some
capacity on the queue, in the pool, or both. You can choose any of these options from an
enumeration of values available for the `rejection-policy` attribute on the `executor`
element.
The following example shows an `executor` element with a number of attributes to specify
various behaviors:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<task:executor
id="executorWithCallerRunsPolicy"
pool-size="5-25"
queue-capacity="100"
rejection-policy="CALLER_RUNS"/>
----
Finally, the `keep-alive` setting determines the time limit (in seconds) for which threads
may remain idle before being stopped. If there are more than the core number of threads
currently in the pool, after waiting this amount of time without processing a task, excess
threads get stopped. A time value of zero causes excess threads to stop
immediately after executing a task without remaining follow-up work in the task queue.
The following example sets the `keep-alive` value to two minutes:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<task:executor
id="executorWithKeepAlive"
pool-size="5-25"
keep-alive="120"/>
----
[[scheduling-task-namespace-scheduled-tasks]]
=== The 'scheduled-tasks' Element
The most powerful feature of Spring's task namespace is the support for configuring
tasks to be scheduled within a Spring Application Context. This follows an approach
similar to other "`method-invokers`" in Spring, such as that provided by the JMS namespace
for configuring message-driven POJOs. Basically, a `ref` attribute can point to any
Spring-managed object, and the `method` attribute provides the name of a method to be
invoked on that object. The following listing shows a simple example:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<task:scheduled-tasks scheduler="myScheduler">
<task:scheduled ref="beanA" method="methodA" fixed-delay="5000"/>
</task:scheduled-tasks>
<task:scheduler id="myScheduler" pool-size="10"/>
----
The scheduler is referenced by the outer element, and each individual
task includes the configuration of its trigger metadata. In the preceding example, that
metadata defines a periodic trigger with a fixed delay indicating the number of
milliseconds to wait after each task execution has completed. Another option is
`fixed-rate`, indicating how often the method should be run regardless of how long
any previous execution takes. Additionally, for both `fixed-delay` and `fixed-rate` tasks, you can specify an
'initial-delay' parameter, indicating the number of milliseconds to wait
before the first execution of the method. For more control, you can instead provide a `cron` attribute
to provide a <<scheduling-cron-expression,cron expression>>.
The following example shows these other options:
[source,xml,indent=0]
[subs="verbatim"]
----
<task:scheduled-tasks scheduler="myScheduler">
<task:scheduled ref="beanA" method="methodA" fixed-delay="5000" initial-delay="1000"/>
<task:scheduled ref="beanB" method="methodB" fixed-rate="5000"/>
<task:scheduled ref="beanC" method="methodC" cron="*/5 * * * * MON-FRI"/>
</task:scheduled-tasks>
<task:scheduler id="myScheduler" pool-size="10"/>
----
[[scheduling-cron-expression]]
== Cron Expressions
All Spring cron expressions have to conform to the same format, whether you are using them in
<<scheduling-annotation-support-scheduled,`@Scheduled` annotations>>,
<<scheduling-task-namespace-scheduled-tasks,`task:scheduled-tasks` elements>>,
or someplace else.
A well-formed cron expression, such as `* * * * * *`, consists of six space-separated time and date
fields, each with its own range of valid values:
....
┌───────────── second (0-59)
│ ┌───────────── minute (0 - 59)
│ │ ┌───────────── hour (0 - 23)
│ │ │ ┌───────────── day of the month (1 - 31)
│ │ │ │ ┌───────────── month (1 - 12) (or JAN-DEC)
│ │ │ │ │ ┌───────────── day of the week (0 - 7)
│ │ │ │ │ │ (0 or 7 is Sunday, or MON-SUN)
│ │ │ │ │ │
* * * * * *
....
There are some rules that apply:
* A field may be an asterisk (`*`), which always stands for "`first-last`".
For the day-of-the-month or day-of-the-week fields, a question mark (`?`) may be used instead of an
asterisk.
* Commas (`,`) are used to separate items of a list.
* Two numbers separated with a hyphen (`-`) express a range of numbers.
The specified range is inclusive.
* Following a range (or `*`) with `/` specifies the interval of the number's value through the range.
* English names can also be used for the month and day-of-week fields.
Use the first three letters of the particular day or month (case does not matter).
* The day-of-month and day-of-week fields can contain an `L` character, which has a different meaning.
** In the day-of-month field, `L` stands for _the last day of the month_.
If followed by a negative offset (that is, `L-n`), it means _``n``th-to-last day of the month_.
** In the day-of-week field, `L` stands for _the last day of the week_.
If prefixed by a number or three-letter name (`dL` or `DDDL`), it means _the last day of week (`d`
or `DDD`) in the month_.
* The day-of-month field can be `nW`, which stands for _the nearest weekday to day of the month ``n``_.
If `n` falls on Saturday, this yields the Friday before it.
If `n` falls on Sunday, this yields the Monday after, which also happens if `n` is `1` and falls on
a Saturday (that is: `1W` stands for _the first weekday of the month_).
* If the day-of-month field is `LW`, it means _the last weekday of the month_.
* The day-of-week field can be `d#n` (or `DDD#n`), which stands for _the ``n``th day of week `d`
(or ``DDD``) in the month_.
Here are some examples:
|===
| Cron Expression | Meaning
|`0 0 * * * *` | top of every hour of every day
|`*/10 * * * * *` | every ten seconds
| `0 0 8-10 * * *` | 8, 9 and 10 o'clock of every day
| `0 0 6,19 * * *` | 6:00 AM and 7:00 PM every day
| `0 0/30 8-10 * * *` | 8:00, 8:30, 9:00, 9:30, 10:00 and 10:30 every day
| `0 0 9-17 * * MON-FRI`| on the hour nine-to-five weekdays
| `0 0 0 25 DEC ?` | every Christmas Day at midnight
| `0 0 0 L * *` | last day of the month at midnight
| `0 0 0 L-3 * *` | third-to-last day of the month at midnight
| `0 0 0 * * 5L` | last Friday of the month at midnight
| `0 0 0 * * THUL` | last Thursday of the month at midnight
| `0 0 0 1W * *` | first weekday of the month at midnight
| `0 0 0 LW * *` | last weekday of the month at midnight
| `0 0 0 ? * 5#2` | the second Friday in the month at midnight
| `0 0 0 ? * MON#1` | the first Monday in the month at midnight
|===
=== Macros
Expressions such as `0 0 * * * *` are hard for humans to parse and are, therefore, hard to fix in case of bugs.
To improve readability, Spring supports the following macros, which represent commonly used sequences.
You can use these macros instead of the six-digit value, thus: `@Scheduled(cron = "@hourly")`.
|===
|Macro | Meaning
| `@yearly` (or `@annually`) | once a year (`0 0 0 1 1 *`)
| `@monthly` | once a month (`0 0 0 1 * *`)
| `@weekly` | once a week (`0 0 0 * * 0`)
| `@daily` (or `@midnight`) | once a day (`0 0 0 * * *`), or
| `@hourly` | once an hour, (`0 0 * * * *`)
|===
[[scheduling-quartz]]
== Using the Quartz Scheduler
Quartz uses `Trigger`, `Job`, and `JobDetail` objects to realize scheduling of all kinds
of jobs. For the basic concepts behind Quartz, see
https://www.quartz-scheduler.org/[]. For convenience purposes, Spring offers a couple of
classes that simplify using Quartz within Spring-based applications.
[[scheduling-quartz-jobdetail]]
=== Using the `JobDetailFactoryBean`
Quartz `JobDetail` objects contain all the information needed to run a job. Spring provides a
`JobDetailFactoryBean`, which provides bean-style properties for XML configuration purposes.
Consider the following example:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="example.ExampleJob"/>
<property name="jobDataAsMap">
<map>
<entry key="timeout" value="5"/>
</map>
</property>
</bean>
----
The job detail configuration has all the information it needs to run the job (`ExampleJob`).
The timeout is specified in the job data map. The job data map is available through the
`JobExecutionContext` (passed to you at execution time), but the `JobDetail` also gets
its properties from the job data mapped to properties of the job instance. So, in the following example,
the `ExampleJob` contains a bean property named `timeout`, and the `JobDetail`
has it applied automatically:
[source,java,indent=0]
[subs="verbatim"]
----
package example;
public class ExampleJob extends QuartzJobBean {
private int timeout;
/**
* Setter called after the ExampleJob is instantiated
* with the value from the JobDetailFactoryBean (5)
*/
public void setTimeout(int timeout) {
this.timeout = timeout;
}
protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {
// do the actual work
}
}
----
All additional properties from the job data map are available to you as well.
NOTE: By using the `name` and `group` properties, you can modify the name and the group
of the job, respectively. By default, the name of the job matches the bean name
of the `JobDetailFactoryBean` (`exampleJob` in the preceding example above).
[[scheduling-quartz-method-invoking-job]]
=== Using the `MethodInvokingJobDetailFactoryBean`
Often you merely need to invoke a method on a specific object. By using the
`MethodInvokingJobDetailFactoryBean`, you can do exactly this, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="exampleBusinessObject"/>
<property name="targetMethod" value="doIt"/>
</bean>
----
The preceding example results in the `doIt` method being called on the
`exampleBusinessObject` method, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
public class ExampleBusinessObject {
// properties and collaborators
public void doIt() {
// do the actual work
}
}
----
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="exampleBusinessObject" class="examples.ExampleBusinessObject"/>
----
By using the `MethodInvokingJobDetailFactoryBean`, you need not create one-line jobs
that merely invoke a method. You need only create the actual business object and
wire up the detail object.
By default, Quartz Jobs are stateless, resulting in the possibility of jobs interfering
with each other. If you specify two triggers for the same `JobDetail`, it is
possible that, before the first job has finished, the second one starts. If
`JobDetail` classes implement the `Stateful` interface, this does not happen. The second
job does not start before the first one has finished. To make jobs resulting from the
`MethodInvokingJobDetailFactoryBean` be non-concurrent, set the `concurrent` flag to
`false`, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="exampleBusinessObject"/>
<property name="targetMethod" value="doIt"/>
<property name="concurrent" value="false"/>
</bean>
----
NOTE: By default, jobs will run in a concurrent fashion.
[[scheduling-quartz-cron]]
=== Wiring up Jobs by Using Triggers and `SchedulerFactoryBean`
We have created job details and jobs. We have also reviewed the convenience bean that lets
you invoke a method on a specific object. Of course, we still need to schedule the
jobs themselves. This is done by using triggers and a `SchedulerFactoryBean`. Several
triggers are available within Quartz, and Spring offers two Quartz `FactoryBean`
implementations with convenient defaults: `CronTriggerFactoryBean` and
`SimpleTriggerFactoryBean`.
Triggers need to be scheduled. Spring offers a `SchedulerFactoryBean` that exposes
triggers to be set as properties. `SchedulerFactoryBean` schedules the actual jobs with
those triggers.
The following listing uses both a `SimpleTriggerFactoryBean` and a `CronTriggerFactoryBean`:
[source,xml,indent=0]
[subs="verbatim"]
----
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<!-- see the example of method invoking job above -->
<property name="jobDetail" ref="jobDetail"/>
<!-- 10 seconds -->
<property name="startDelay" value="10000"/>
<!-- repeat every 50 seconds -->
<property name="repeatInterval" value="50000"/>
</bean>
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="exampleJob"/>
<!-- run every morning at 6 AM -->
<property name="cronExpression" value="0 0 6 * * ?"/>
</bean>
----
The preceding example sets up two triggers, one running every 50 seconds with a starting delay of 10
seconds and one running every morning at 6 AM. To finalize everything, we need to set up the
`SchedulerFactoryBean`, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger"/>
<ref bean="simpleTrigger"/>
</list>
</property>
</bean>
----
More properties are available for the `SchedulerFactoryBean`, such as the calendars used by the
job details, properties to customize Quartz with, and a Spring-provided JDBC DataSource. See
the {api-spring-framework}/scheduling/quartz/SchedulerFactoryBean.html[`SchedulerFactoryBean`]
javadoc for more information.
NOTE: `SchedulerFactoryBean` also recognizes a `quartz.properties` file in the classpath,
based on Quartz property keys, as with regular Quartz configuration. Please note that many
`SchedulerFactoryBean` settings interact with common Quartz settings in the properties file;
it is therefore not recommended to specify values at both levels. For example, do not set
an "org.quartz.jobStore.class" property if you mean to rely on a Spring-provided DataSource,
or specify an `org.springframework.scheduling.quartz.LocalDataSourceJobStore` variant which
is a full-fledged replacement for the standard `org.quartz.impl.jdbcjobstore.JobStoreTX`.
@@ -1,12 +0,0 @@
[[languages]]
= Language Support
include::attributes.adoc[]
include::page-layout.adoc[]
include::languages/kotlin.adoc[leveloffset=+1]
include::languages/groovy.adoc[leveloffset=+1]
include::languages/dynamic-languages.adoc[leveloffset=+1]
@@ -1,859 +0,0 @@
[[dynamic-language]]
= Dynamic Language Support
Spring provides comprehensive support for using classes and objects that have been
defined by using a dynamic language (such as Groovy) with Spring. This support lets
you write any number of classes in a supported dynamic language and have the Spring
container transparently instantiate, configure, and dependency inject the resulting
objects.
Spring's scripting support primarily targets Groovy and BeanShell. Beyond those
specifically supported languages, the JSR-223 scripting mechanism is supported
for integration with any JSR-223 capable language provider (as of Spring 4.2),
e.g. JRuby.
You can find fully working examples of where this dynamic language support can be
immediately useful in <<dynamic-language-scenarios>>.
[[dynamic-language-a-first-example]]
== A First Example
The bulk of this chapter is concerned with describing the dynamic language support in
detail. Before diving into all of the ins and outs of the dynamic language support,
we look at a quick example of a bean defined in a dynamic language. The dynamic
language for this first bean is Groovy. (The basis of this example was taken from the
Spring test suite. If you want to see equivalent examples in any of the other
supported languages, take a look at the source code).
The next example shows the `Messenger` interface, which the Groovy bean is going to
implement. Note that this interface is defined in plain Java. Dependent objects that
are injected with a reference to the `Messenger` do not know that the underlying
implementation is a Groovy script. The following listing shows the `Messenger` interface:
[source,java,indent=0,subs="verbatim,quotes"]
----
package org.springframework.scripting;
public interface Messenger {
String getMessage();
}
----
The following example defines a class that has a dependency on the `Messenger` interface:
[source,java,indent=0,subs="verbatim,quotes"]
----
package org.springframework.scripting;
public class DefaultBookingService implements BookingService {
private Messenger messenger;
public void setMessenger(Messenger messenger) {
this.messenger = messenger;
}
public void processBooking() {
// use the injected Messenger object...
}
}
----
The following example implements the `Messenger` interface in Groovy:
[source,groovy,indent=0,subs="verbatim,quotes"]
----
// from the file 'Messenger.groovy'
package org.springframework.scripting.groovy;
// import the Messenger interface (written in Java) that is to be implemented
import org.springframework.scripting.Messenger
// define the implementation in Groovy
class GroovyMessenger implements Messenger {
String message
}
----
[NOTE]
====
To use the custom dynamic language tags to define dynamic-language-backed beans, you
need to have the XML Schema preamble at the top of your Spring XML configuration file.
You also need to use a Spring `ApplicationContext` implementation as your IoC
container. Using the dynamic-language-backed beans with a plain `BeanFactory`
implementation is supported, but you have to manage the plumbing of the Spring internals
to do so.
For more information on schema-based configuration, see <<xsd-schemas-lang,
XML Schema-based Configuration>>.
====
Finally, the following example shows the bean definitions that effect the injection of the
Groovy-defined `Messenger` implementation into an instance of the
`DefaultBookingService` class:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?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:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/lang https://www.springframework.org/schema/lang/spring-lang.xsd">
<!-- this is the bean definition for the Groovy-backed Messenger implementation -->
<lang:groovy id="messenger" script-source="classpath:Messenger.groovy">
<lang:property name="message" value="I Can Do The Frug" />
</lang:groovy>
<!-- an otherwise normal bean that will be injected by the Groovy-backed Messenger -->
<bean id="bookingService" class="x.y.DefaultBookingService">
<property name="messenger" ref="messenger" />
</bean>
</beans>
----
The `bookingService` bean (a `DefaultBookingService`) can now use its private `messenger`
member variable as normal, because the `Messenger` instance that was injected into it is
a `Messenger` instance. There is nothing special going on here -- just plain Java and
plain Groovy.
Hopefully, the preceding XML snippet is self-explanatory, but do not worry unduly if it is not.
Keep reading for the in-depth detail on the whys and wherefores of the preceding configuration.
[[dynamic-language-beans]]
== Defining Beans that Are Backed by Dynamic Languages
This section describes exactly how you define Spring-managed beans in any of the
supported dynamic languages.
Note that this chapter does not attempt to explain the syntax and idioms of the supported
dynamic languages. For example, if you want to use Groovy to write certain of the classes
in your application, we assume that you already know Groovy. If you need further details
about the dynamic languages themselves, see <<dynamic-language-resources>> at the end of
this chapter.
[[dynamic-language-beans-concepts]]
=== Common Concepts
The steps involved in using dynamic-language-backed beans are as follows:
. Write the test for the dynamic language source code (naturally).
. Then write the dynamic language source code itself.
. Define your dynamic-language-backed beans by using the appropriate `<lang:language/>`
element in the XML configuration (you can define such beans programmatically by
using the Spring API, although you will have to consult the source code for
directions on how to do this, as this chapter does not cover this type of advanced configuration).
Note that this is an iterative step. You need at least one bean definition for each dynamic
language source file (although multiple bean definitions can reference the same source file).
The first two steps (testing and writing your dynamic language source files) are beyond
the scope of this chapter. See the language specification and reference manual
for your chosen dynamic language and crack on with developing your dynamic language
source files. You first want to read the rest of this chapter, though, as
Spring's dynamic language support does make some (small) assumptions about the contents
of your dynamic language source files.
[[dynamic-language-beans-concepts-xml-language-element]]
==== The <lang:language/> element
The final step in the list in the <<dynamic-language-beans-concepts, preceding section>>
involves defining dynamic-language-backed bean definitions, one for each bean that you
want to configure (this is no different from normal JavaBean configuration). However,
instead of specifying the fully qualified class name of the class that is to be
instantiated and configured by the container, you can use the `<lang:language/>`
element to define the dynamic language-backed bean.
Each of the supported languages has a corresponding `<lang:language/>` element:
* `<lang:groovy/>` (Groovy)
* `<lang:bsh/>` (BeanShell)
* `<lang:std/>` (JSR-223, e.g. with JRuby)
The exact attributes and child elements that are available for configuration depends on
exactly which language the bean has been defined in (the language-specific sections
later in this chapter detail this).
[[dynamic-language-refreshable-beans]]
==== Refreshable Beans
One of the (and perhaps the single) most compelling value adds of the dynamic language
support in Spring is the "`refreshable bean`" feature.
A refreshable bean is a dynamic-language-backed bean. With a small amount of
configuration, a dynamic-language-backed bean can monitor changes in its underlying
source file resource and then reload itself when the dynamic language source file is
changed (for example, when you edit and save changes to the file on the file system).
This lets you deploy any number of dynamic language source files as part of an
application, configure the Spring container to create beans backed by dynamic
language source files (using the mechanisms described in this chapter), and (later,
as requirements change or some other external factor comes into play) edit a dynamic
language source file and have any change they make be reflected in the bean that is
backed by the changed dynamic language source file. There is no need to shut down a
running application (or redeploy in the case of a web application). The
dynamic-language-backed bean so amended picks up the new state and logic from the
changed dynamic language source file.
NOTE: This feature is off by default.
Now we can take a look at an example to see how easy it is to start using refreshable
beans. To turn on the refreshable beans feature, you have to specify exactly one
additional attribute on the `<lang:language/>` element of your bean definition. So,
if we stick with <<dynamic-language-a-first-example, the example>> from earlier in
this chapter, the following example shows what we would change in the Spring XML
configuration to effect refreshable beans:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<beans>
<!-- this bean is now 'refreshable' due to the presence of the 'refresh-check-delay' attribute -->
<lang:groovy id="messenger"
refresh-check-delay="5000" <!-- switches refreshing on with 5 seconds between checks -->
script-source="classpath:Messenger.groovy">
<lang:property name="message" value="I Can Do The Frug" />
</lang:groovy>
<bean id="bookingService" class="x.y.DefaultBookingService">
<property name="messenger" ref="messenger" />
</bean>
</beans>
----
That really is all you have to do. The `refresh-check-delay` attribute defined on the
`messenger` bean definition is the number of milliseconds after which the bean is
refreshed with any changes made to the underlying dynamic language source file.
You can turn off the refresh behavior by assigning a negative value to the
`refresh-check-delay` attribute. Remember that, by default, the refresh behavior is
disabled. If you do not want the refresh behavior, do not define the attribute.
If we then run the following application, we can exercise the refreshable feature.
(Please excuse the "`jumping-through-hoops-to-pause-the-execution`" shenanigans
in this next slice of code.) The `System.in.read()` call is only there so that the
execution of the program pauses while you (the developer in this scenario) go off
and edit the underlying dynamic language source file so that the refresh triggers
on the dynamic-language-backed bean when the program resumes execution.
The following listing shows this sample application:
[source,java,indent=0,subs="verbatim,quotes"]
----
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;
public final class Boot {
public static void main(final String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
Messenger messenger = (Messenger) ctx.getBean("messenger");
System.out.println(messenger.getMessage());
// pause execution while I go off and make changes to the source file...
System.in.read();
System.out.println(messenger.getMessage());
}
}
----
Assume then, for the purposes of this example, that all calls to the `getMessage()`
method of `Messenger` implementations have to be changed such that the message is
surrounded by quotation marks. The following listing shows the changes that you
(the developer) should make to the `Messenger.groovy` source file when the
execution of the program is paused:
[source,groovy,indent=0,subs="verbatim,quotes"]
----
package org.springframework.scripting
class GroovyMessenger implements Messenger {
private String message = "Bingo"
public String getMessage() {
// change the implementation to surround the message in quotes
return "'" + this.message + "'"
}
public void setMessage(String message) {
this.message = message
}
}
----
When the program runs, the output before the input pause will be `I Can Do The Frug`.
After the change to the source file is made and saved and the program resumes execution,
the result of calling the `getMessage()` method on the dynamic-language-backed
`Messenger` implementation is `'I Can Do The Frug'` (notice the inclusion of the
additional quotation marks).
Changes to a script do not trigger a refresh if the changes occur within the window of
the `refresh-check-delay` value. Changes to the script are not actually picked up until
a method is called on the dynamic-language-backed bean. It is only when a method is
called on a dynamic-language-backed bean that it checks to see if its underlying script
source has changed. Any exceptions that relate to refreshing the script (such as
encountering a compilation error or finding that the script file has been deleted)
results in a fatal exception being propagated to the calling code.
The refreshable bean behavior described earlier does not apply to dynamic language
source files defined with the `<lang:inline-script/>` element notation (see
<<dynamic-language-beans-inline>>). Additionally, it applies only to beans where
changes to the underlying source file can actually be detected (for example, by code
that checks the last modified date of a dynamic language source file that exists on the
file system).
[[dynamic-language-beans-inline]]
==== Inline Dynamic Language Source Files
The dynamic language support can also cater to dynamic language source files that are
embedded directly in Spring bean definitions. More specifically, the
`<lang:inline-script/>` element lets you define dynamic language source immediately
inside a Spring configuration file. An example might clarify how the inline script
feature works:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<lang:groovy id="messenger">
<lang:inline-script>
package org.springframework.scripting.groovy;
import org.springframework.scripting.Messenger
class GroovyMessenger implements Messenger {
String message
}
</lang:inline-script>
<lang:property name="message" value="I Can Do The Frug" />
</lang:groovy>
----
If we put to one side the issues surrounding whether it is good practice to define
dynamic language source inside a Spring configuration file, the `<lang:inline-script/>`
element can be useful in some scenarios. For instance, we might want to quickly add a
Spring `Validator` implementation to a Spring MVC `Controller`. This is but a moment's
work using inline source. (See <<dynamic-language-scenarios-validators>> for such an
example.)
[[dynamic-language-beans-ctor-injection]]
==== Understanding Constructor Injection in the Context of Dynamic-language-backed Beans
There is one very important thing to be aware of with regard to Spring's dynamic
language support. Namely, you can not (currently) supply constructor arguments
to dynamic-language-backed beans (and, hence, constructor-injection is not available for
dynamic-language-backed beans). In the interests of making this special handling of
constructors and properties 100% clear, the following mixture of code and configuration
does not work:
.An approach that cannot work
[source,groovy,indent=0,subs="verbatim,quotes"]
----
// from the file 'Messenger.groovy'
package org.springframework.scripting.groovy;
import org.springframework.scripting.Messenger
class GroovyMessenger implements Messenger {
GroovyMessenger() {}
// this constructor is not available for Constructor Injection
GroovyMessenger(String message) {
this.message = message;
}
String message
String anotherMessage
}
----
[source,xml,indent=0,subs="verbatim,quotes"]
----
<lang:groovy id="badMessenger"
script-source="classpath:Messenger.groovy">
<!-- this next constructor argument will not be injected into the GroovyMessenger -->
<!-- in fact, this isn't even allowed according to the schema -->
<constructor-arg value="This will not work" />
<!-- only property values are injected into the dynamic-language-backed object -->
<lang:property name="anotherMessage" value="Passed straight through to the dynamic-language-backed object" />
</lang>
----
In practice this limitation is not as significant as it first appears, since setter
injection is the injection style favored by the overwhelming majority of developers
(we leave the discussion as to whether that is a good thing to another day).
[[dynamic-language-beans-groovy]]
=== Groovy Beans
This section describes how to use beans defined in Groovy in Spring.
The Groovy homepage includes the following description:
"`Groovy is an agile dynamic language for the Java 2 Platform that has many of the
features that people like so much in languages like Python, Ruby and Smalltalk, making
them available to Java developers using a Java-like syntax.`"
If you have read this chapter straight from the top, you have already
<<dynamic-language-a-first-example, seen an example>> of a Groovy-dynamic-language-backed
bean. Now consider another example (again using an example from the Spring test suite):
[source,java,indent=0,subs="verbatim,quotes"]
----
package org.springframework.scripting;
public interface Calculator {
int add(int x, int y);
}
----
The following example implements the `Calculator` interface in Groovy:
[source,groovy,indent=0,subs="verbatim,quotes"]
----
// from the file 'calculator.groovy'
package org.springframework.scripting.groovy
class GroovyCalculator implements Calculator {
int add(int x, int y) {
x + y
}
}
----
The following bean definition uses the calculator defined in Groovy:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- from the file 'beans.xml' -->
<beans>
<lang:groovy id="calculator" script-source="classpath:calculator.groovy"/>
</beans>
----
Finally, the following small application exercises the preceding configuration:
[source,java,indent=0,subs="verbatim,quotes"]
----
package org.springframework.scripting;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
Calculator calc = ctx.getBean("calculator", Calculator.class);
System.out.println(calc.add(2, 8));
}
}
----
The resulting output from running the above program is (unsurprisingly) `10`.
(For more interesting examples, see the dynamic language showcase project for a more
complex example or see the examples <<dynamic-language-scenarios>> later in this chapter).
You must not define more than one class per Groovy source file. While this is perfectly
legal in Groovy, it is (arguably) a bad practice. In the interests of a consistent
approach, you should (in the opinion of the Spring team) respect the standard Java
conventions of one (public) class per source file.
[[dynamic-language-beans-groovy-customizer]]
==== Customizing Groovy Objects by Using a Callback
The `GroovyObjectCustomizer` interface is a callback that lets you hook additional
creation logic into the process of creating a Groovy-backed bean. For example,
implementations of this interface could invoke any required initialization methods,
set some default property values, or specify a custom `MetaClass`. The following listing
shows the `GroovyObjectCustomizer` interface definition:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface GroovyObjectCustomizer {
void customize(GroovyObject goo);
}
----
The Spring Framework instantiates an instance of your Groovy-backed bean and then
passes the created `GroovyObject` to the specified `GroovyObjectCustomizer` (if one
has been defined). You can do whatever you like with the supplied `GroovyObject`
reference. We expect that most people want to set a custom `MetaClass` with this
callback, and the following example shows how to do so:
[source,java,indent=0,subs="verbatim,quotes"]
----
public final class SimpleMethodTracingCustomizer implements GroovyObjectCustomizer {
public void customize(GroovyObject goo) {
DelegatingMetaClass metaClass = new DelegatingMetaClass(goo.getMetaClass()) {
public Object invokeMethod(Object object, String methodName, Object[] arguments) {
System.out.println("Invoking '" + methodName + "'.");
return super.invokeMethod(object, methodName, arguments);
}
};
metaClass.initialize();
goo.setMetaClass(metaClass);
}
}
----
A full discussion of meta-programming in Groovy is beyond the scope of the Spring
reference manual. See the relevant section of the Groovy reference manual or do a
search online. Plenty of articles address this topic. Actually, making use of a
`GroovyObjectCustomizer` is easy if you use the Spring namespace support, as the
following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- define the GroovyObjectCustomizer just like any other bean -->
<bean id="tracingCustomizer" class="example.SimpleMethodTracingCustomizer"/>
<!-- ... and plug it into the desired Groovy bean via the 'customizer-ref' attribute -->
<lang:groovy id="calculator"
script-source="classpath:org/springframework/scripting/groovy/Calculator.groovy"
customizer-ref="tracingCustomizer"/>
----
If you do not use the Spring namespace support, you can still use the
`GroovyObjectCustomizer` functionality, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="calculator" class="org.springframework.scripting.groovy.GroovyScriptFactory">
<constructor-arg value="classpath:org/springframework/scripting/groovy/Calculator.groovy"/>
<!-- define the GroovyObjectCustomizer (as an inner bean) -->
<constructor-arg>
<bean id="tracingCustomizer" class="example.SimpleMethodTracingCustomizer"/>
</constructor-arg>
</bean>
<bean class="org.springframework.scripting.support.ScriptFactoryPostProcessor"/>
----
NOTE: You may also specify a Groovy `CompilationCustomizer` (such as an `ImportCustomizer`)
or even a full Groovy `CompilerConfiguration` object in the same place as Spring's
`GroovyObjectCustomizer`. Furthermore, you may set a common `GroovyClassLoader` with custom
configuration for your beans at the `ConfigurableApplicationContext.setClassLoader` level;
this also leads to shared `GroovyClassLoader` usage and is therefore recommendable in case of
a large number of scripted beans (avoiding an isolated `GroovyClassLoader` instance per bean).
[[dynamic-language-beans-bsh]]
=== BeanShell Beans
This section describes how to use BeanShell beans in Spring.
The https://beanshell.github.io/intro.html[BeanShell homepage] includes the following
description:
----
BeanShell is a small, free, embeddable Java source interpreter with dynamic language
features, written in Java. BeanShell dynamically runs standard Java syntax and
extends it with common scripting conveniences such as loose types, commands, and method
closures like those in Perl and JavaScript.
----
In contrast to Groovy, BeanShell-backed bean definitions require some (small) additional
configuration. The implementation of the BeanShell dynamic language support in Spring is
interesting, because Spring creates a JDK dynamic proxy that implements all of the
interfaces that are specified in the `script-interfaces` attribute value of the
`<lang:bsh>` element (this is why you must supply at least one interface in the value
of the attribute, and, consequently, program to interfaces when you use BeanShell-backed
beans). This means that every method call on a BeanShell-backed object goes through the
JDK dynamic proxy invocation mechanism.
Now we can show a fully working example of using a BeanShell-based bean that implements
the `Messenger` interface that was defined earlier in this chapter. We again show the
definition of the `Messenger` interface:
[source,java,indent=0,subs="verbatim,quotes"]
----
package org.springframework.scripting;
public interface Messenger {
String getMessage();
}
----
The following example shows the BeanShell "`implementation`" (we use the term loosely here)
of the `Messenger` interface:
[source,java,indent=0,subs="verbatim,quotes"]
----
String message;
String getMessage() {
return message;
}
void setMessage(String aMessage) {
message = aMessage;
}
----
The following example shows the Spring XML that defines an "`instance`" of the above
"`class`" (again, we use these terms very loosely here):
[source,xml,indent=0,subs="verbatim,quotes"]
----
<lang:bsh id="messageService" script-source="classpath:BshMessenger.bsh"
script-interfaces="org.springframework.scripting.Messenger">
<lang:property name="message" value="Hello World!" />
</lang:bsh>
----
See <<dynamic-language-scenarios>> for some scenarios where you might want to use
BeanShell-based beans.
[[dynamic-language-scenarios]]
== Scenarios
The possible scenarios where defining Spring managed beans in a scripting language would
be beneficial are many and varied. This section describes two possible use cases for the
dynamic language support in Spring.
[[dynamic-language-scenarios-controllers]]
=== Scripted Spring MVC Controllers
One group of classes that can benefit from using dynamic-language-backed beans is that
of Spring MVC controllers. In pure Spring MVC applications, the navigational flow
through a web application is, to a large extent, determined by code encapsulated within
your Spring MVC controllers. As the navigational flow and other presentation layer logic
of a web application needs to be updated to respond to support issues or changing
business requirements, it may well be easier to effect any such required changes by
editing one or more dynamic language source files and seeing those changes being
immediately reflected in the state of a running application.
Remember that, in the lightweight architectural model espoused by projects such as
Spring, you typically aim to have a really thin presentation layer, with all
the meaty business logic of an application being contained in the domain and service
layer classes. Developing Spring MVC controllers as dynamic-language-backed beans lets
you change presentation layer logic by editing and saving text files. Any
changes to such dynamic language source files is (depending on the configuration)
automatically reflected in the beans that are backed by dynamic language source files.
NOTE: To effect this automatic "`pickup`" of any changes to dynamic-language-backed
beans, you have to enable the "`refreshable beans`" functionality. See
<<dynamic-language-refreshable-beans>> for a full treatment of this feature.
The following example shows an `org.springframework.web.servlet.mvc.Controller` implemented
by using the Groovy dynamic language:
[source,groovy,indent=0,subs="verbatim,quotes"]
----
// from the file '/WEB-INF/groovy/FortuneController.groovy'
package org.springframework.showcase.fortune.web
import org.springframework.showcase.fortune.service.FortuneService
import org.springframework.showcase.fortune.domain.Fortune
import org.springframework.web.servlet.ModelAndView
import org.springframework.web.servlet.mvc.Controller
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
class FortuneController implements Controller {
@Property FortuneService fortuneService
ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse httpServletResponse) {
return new ModelAndView("tell", "fortune", this.fortuneService.tellFortune())
}
}
----
[source,xml,indent=0,subs="verbatim,quotes"]
----
<lang:groovy id="fortune"
refresh-check-delay="3000"
script-source="/WEB-INF/groovy/FortuneController.groovy">
<lang:property name="fortuneService" ref="fortuneService"/>
</lang:groovy>
----
[[dynamic-language-scenarios-validators]]
=== Scripted Validators
Another area of application development with Spring that may benefit from the
flexibility afforded by dynamic-language-backed beans is that of validation. It can
be easier to express complex validation logic by using a loosely typed dynamic language
(that may also have support for inline regular expressions) as opposed to regular Java.
Again, developing validators as dynamic-language-backed beans lets you change
validation logic by editing and saving a simple text file. Any such changes is
(depending on the configuration) automatically reflected in the execution of a
running application and would not require the restart of an application.
NOTE: To effect the automatic "`pickup`" of any changes to dynamic-language-backed
beans, you have to enable the 'refreshable beans' feature. See
<<dynamic-language-refreshable-beans>> for a full and detailed treatment of this feature.
The following example shows a Spring `org.springframework.validation.Validator`
implemented by using the Groovy dynamic language (see <<core.adoc#validator,
Validation using Springs Validator interface>> for a discussion of the
`Validator` interface):
[source,groovy,indent=0,subs="verbatim,quotes"]
----
import org.springframework.validation.Validator
import org.springframework.validation.Errors
import org.springframework.beans.TestBean
class TestBeanValidator implements Validator {
boolean supports(Class clazz) {
return TestBean.class.isAssignableFrom(clazz)
}
void validate(Object bean, Errors errors) {
if(bean.name?.trim()?.size() > 0) {
return
}
errors.reject("whitespace", "Cannot be composed wholly of whitespace.")
}
}
----
[[dynamic-language-final-notes]]
== Additional Details
This last section contains some additional details related to the dynamic language support.
[[dynamic-language-final-notes-aop]]
=== AOP -- Advising Scripted Beans
You can use the Spring AOP framework to advise scripted beans. The Spring AOP
framework actually is unaware that a bean that is being advised might be a scripted
bean, so all of the AOP use cases and functionality that you use (or aim to use)
work with scripted beans. When you advise scripted beans, you cannot use class-based
proxies. You must use <<core.adoc#aop-proxying, interface-based proxies>>.
You are not limited to advising scripted beans. You can also write aspects themselves
in a supported dynamic language and use such beans to advise other Spring beans.
This really would be an advanced use of the dynamic language support though.
[[dynamic-language-final-notes-scopes]]
=== Scoping
In case it is not immediately obvious, scripted beans can be scoped in the same way as
any other bean. The `scope` attribute on the various `<lang:language/>` elements lets
you control the scope of the underlying scripted bean, as it does with a regular
bean. (The default scope is <<core.adoc#beans-factory-scopes-singleton, singleton>>,
as it is with "`regular`" beans.)
The following example uses the `scope` attribute to define a Groovy bean scoped as
a <<core.adoc#beans-factory-scopes-prototype, prototype>>:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?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:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/lang https://www.springframework.org/schema/lang/spring-lang.xsd">
<lang:groovy id="messenger" script-source="classpath:Messenger.groovy" scope="prototype">
<lang:property name="message" value="I Can Do The RoboCop" />
</lang:groovy>
<bean id="bookingService" class="x.y.DefaultBookingService">
<property name="messenger" ref="messenger" />
</bean>
</beans>
----
See <<core.adoc#beans-factory-scopes, Bean Scopes>> in <<core.adoc#beans, The IoC Container>>
for a full discussion of the scoping support in the Spring Framework.
[[xsd-schemas-lang]]
=== The `lang` XML schema
The `lang` elements in Spring XML configuration deal with exposing objects that have been
written in a dynamic language (such as Groovy or BeanShell) as beans in the Spring container.
These elements (and the dynamic language support) are comprehensively covered in
<<dynamic-language, Dynamic Language Support>>. See that section
for full details on this support and the `lang` elements.
To use the elements in the `lang` schema, you need to have the following preamble at the
top of your Spring XML configuration file. The text in the following snippet references
the correct schema so that the tags in the `lang` namespace are available to you:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?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:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/lang https://www.springframework.org/schema/lang/spring-lang.xsd">
<!-- bean definitions here -->
</beans>
----
[[dynamic-language-resources]]
== Further Resources
The following links go to further resources about the various dynamic languages referenced
in this chapter:
* The https://www.groovy-lang.org/[Groovy] homepage
* The https://beanshell.github.io/intro.html[BeanShell] homepage
* The https://www.jruby.org[JRuby] homepage

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