Compare commits

..

363 Commits

Author SHA1 Message Date
Sam Brannen a636420239 Manual URL Cleanup
See gh-22517
2019-03-12 17:44:26 +01:00
Sam Brannen e68a7ae776 Merge branch 'spring-operator-polish-urls-build-4.0.x' into 4.0.x 2019-03-11 22:40:02 +01:00
Sam Brannen 0dedba016c Manual URL Cleanup
Closes gh-22517
2019-03-11 20:34:33 +01:00
Spring Operator 82ee0bef93 URL Cleanup
This commit updates URLs to prefer the https protocol. Redirects are not followed to avoid accidentally expanding intentionally shortened URLs (i.e. if using a URL shortener).

# HTTP URLs that Could Not Be Fixed
These URLs were unable to be fixed. Please review them to see if they can be manually resolved.

* http://aopalliance.sourceforge.net/doc/ (200) migrated to:
  http://aopalliance.sourceforge.net/doc/ ([https](https://aopalliance.sourceforge.net/doc/) result AnnotatedConnectException).

# Fixed URLs

## Fixed But Review Recommended
These URLs were fixed, but the https status was not OK. However, the https status was the same as the http request or http redirected to an https URL, so they were migrated. Your review is recommended.

* http://jackson.codehaus.org/1.9.4/javadoc/ (UnknownHostException) migrated to:
  https://jackson.codehaus.org/1.9.4/javadoc/ ([https](https://jackson.codehaus.org/1.9.4/javadoc/) result UnknownHostException).
* http://jira.codehaus.org/browse/JIBX-465 (UnknownHostException) migrated to:
  https://jira.codehaus.org/browse/JIBX-465 ([https](https://jira.codehaus.org/browse/JIBX-465) result UnknownHostException).
* http://quartz-scheduler.org/api/2.2.0/ (301) migrated to:
  https://www.quartz-scheduler.org/api/2.2.0/ ([https](https://quartz-scheduler.org/api/2.2.0/) result 404).

## Fixed Success
These URLs were fixed successfully.

* http://commons.apache.org/proper/commons-codec/apidocs/ migrated to:
  https://commons.apache.org/proper/commons-codec/apidocs/ ([https](https://commons.apache.org/proper/commons-codec/apidocs/) result 200).
* http://commons.apache.org/proper/commons-dbcp/apidocs/ migrated to:
  https://commons.apache.org/proper/commons-dbcp/apidocs/ ([https](https://commons.apache.org/proper/commons-dbcp/apidocs/) result 200).
* http://commons.apache.org/proper/commons-lang/javadocs/api-2.5/ migrated to:
  https://commons.apache.org/proper/commons-lang/javadocs/api-2.5/ ([https](https://commons.apache.org/proper/commons-lang/javadocs/api-2.5/) result 200).
* http://docs.jboss.org/jbossas/javadoc/4.0.5/connector/ migrated to:
  https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/ ([https](https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/) result 200).
* http://docs.jboss.org/jbossas/javadoc/7.1.2.Final/ migrated to:
  https://docs.jboss.org/jbossas/javadoc/7.1.2.Final/ ([https](https://docs.jboss.org/jbossas/javadoc/7.1.2.Final/) result 200).
* http://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/ migrated to:
  https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/ ([https](https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/) result 200).
* http://docs.oracle.com/javaee/7/api/ migrated to:
  https://docs.oracle.com/javaee/7/api/ ([https](https://docs.oracle.com/javaee/7/api/) result 200).
* http://docs.oracle.com/javase/7/docs/api/ migrated to:
  https://docs.oracle.com/javase/7/docs/api/ ([https](https://docs.oracle.com/javase/7/docs/api/) result 200).
* http://portals.apache.org/pluto/portlet-2.0-apidocs/ migrated to:
  https://portals.apache.org/pluto/portlet-2.0-apidocs/ ([https](https://portals.apache.org/pluto/portlet-2.0-apidocs/) result 200).
* http://tiles.apache.org/framework/apidocs/ migrated to:
  https://tiles.apache.org/framework/apidocs/ ([https](https://tiles.apache.org/framework/apidocs/) result 200).
* http://tiles.apache.org/tiles-request/apidocs/ migrated to:
  https://tiles.apache.org/tiles-request/apidocs/ ([https](https://tiles.apache.org/tiles-request/apidocs/) result 200).
* http://www.apache.org/licenses/LICENSE-2.0.txt migrated to:
  https://www.apache.org/licenses/LICENSE-2.0.txt ([https](https://www.apache.org/licenses/LICENSE-2.0.txt) result 200).
* http://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/ migrated to:
  https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/ ([https](https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/) result 200).
* http://ehcache.org/apidocs/ (301) migrated to:
  https://www.ehcache.org/apidocs/ ([https](https://ehcache.org/apidocs/) result 200).
* http://static.springframework.org/spring-framework/docs (301) migrated to:
  https://docs.spring.io/spring-framework/docs ([https](https://static.springframework.org/spring-framework/docs) result 301).
* http://fasterxml.github.com/jackson-core/javadoc/2.3.0/ migrated to:
  https://fasterxml.github.com/jackson-core/javadoc/2.3.0/ ([https](https://fasterxml.github.com/jackson-core/javadoc/2.3.0/) result 301).
* http://fasterxml.github.com/jackson-databind/javadoc/2.3.0/ migrated to:
  https://fasterxml.github.com/jackson-databind/javadoc/2.3.0/ ([https](https://fasterxml.github.com/jackson-databind/javadoc/2.3.0/) result 301).
* http://glassfish.java.net/nonav/docs/v3/api/ migrated to:
  https://glassfish.java.net/nonav/docs/v3/api/ ([https](https://glassfish.java.net/nonav/docs/v3/api/) result 301).
* http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs migrated to:
  https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs ([https](https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs) result 301).
* http://pic.dhe.ibm.com/infocenter/wasinfo/v7r0/topic/com.ibm.websphere.javadoc.doc/web/apidocs/ migrated to:
  https://pic.dhe.ibm.com/infocenter/wasinfo/v7r0/topic/com.ibm.websphere.javadoc.doc/web/apidocs/ ([https](https://pic.dhe.ibm.com/infocenter/wasinfo/v7r0/topic/com.ibm.websphere.javadoc.doc/web/apidocs/) result 301).
* http://projects.spring.io/spring-framework migrated to:
  https://projects.spring.io/spring-framework ([https](https://projects.spring.io/spring-framework) result 301).
* http://springframework.org/schema migrated to:
  https://springframework.org/schema ([https](https://springframework.org/schema) result 301).
* http://spring.io/tools/sts/all migrated to:
  https://spring.io/tools/sts/all ([https](https://spring.io/tools/sts/all) result 302).
2019-03-05 21:55:52 -06:00
Rossen Stoyanchev b8a133d2f4 Remove section on spring-framework-contrib group 2016-04-08 15:44:51 -04:00
Sam Brannen f2e5e5aa86 Eclipse project deps come after third-party libs
This commit is a backport of SPR-11836.

Prior to this commit, the ordering of classpath entries in the
generated Eclipse .classpath files did not align with the intended
dependency management configured in the Gradle build. Specifically,
project dependencies often came before dependencies on third-party
libraries required for the given project, causing the project not to
compile in Eclipse.

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

Issue: SPR-12947, SPR-11836
2015-04-24 21:39:57 +02:00
Spring Buildmaster b6083c7f77 Next Development Version 2014-12-30 07:16:48 -08:00
Juergen Hoeller 8575533081 Latest dependency updates (SLF4J 1.7.9, JRuby 1.7.17, Reactor 1.1.5) 2014-12-30 13:23:46 +01:00
Juergen Hoeller 5528c8a93b Polishing 2014-12-30 13:11:46 +01:00
Juergen Hoeller c9912f0d2d Polishing
Issue: SPR-12502
(cherry picked from commit 26845f6)
2014-12-30 10:09:00 +01:00
Juergen Hoeller 8e14a5d640 Prevent NPE in AbstractApplicationEventMulticaster's non-caching code path
Issue: SPR-12545
(cherry picked from commit 1cefeb2)
2014-12-30 10:08:42 +01:00
Juergen Hoeller 230aa07fbb Polishing 2014-12-29 20:15:54 +01:00
Juergen Hoeller b9aa471def TimeZone and ZoneId as supported arguments for MVC handler methods
Issue: SPR-12575
(cherry picked from commit 3791e7f)
2014-12-29 19:03:15 +01:00
Juergen Hoeller 39fbfbd343 Doc: base-packages can be comma/semicolon/space/tab/linefeed-separated
Issue: SPR-12523
(cherry picked from commit 6f2de28)
2014-12-29 19:02:24 +01:00
Juergen Hoeller e9d24d5336 Polishing
(cherry picked from commit 86b8112)
2014-12-29 18:59:17 +01:00
Juergen Hoeller 4a27a98702 Spring's JMX support can rely on native MXBean detection on Java 6+
Issue: SPR-12574
(cherry picked from commit 0919a15)
2014-12-29 18:59:07 +01:00
Juergen Hoeller 929c596b55 AnnotationJmxAttributeSource uses AnnotationUtils for consistent meta-annotation handling and diagnostics
Issue: SPR-12572
(cherry picked from commit d4a5059)
2014-12-29 18:59:00 +01:00
Juergen Hoeller 9362d0d547 PayloadArgumentResolver does not insist on configured Validator anymore
Issue: SPR-12567
(cherry picked from commit ed0e2f4)
2014-12-29 18:58:50 +01:00
Juergen Hoeller 37713ae9dd HandlerExecutionChain prevents re-adding the interceptors array to the list (and declares varargs now)
Issue: SPR-12566
(cherry picked from commit 6b3023c)
2014-12-29 18:58:41 +01:00
Juergen Hoeller e118086bd1 Annotation post-processors clear old InjectionMetadata registrations on refresh
Issue: SPR-12526
(cherry picked from commit 809ee0d)
2014-12-29 18:56:44 +01:00
Stephane Nicoll 0c7fa9e786 Fix scope inheritance documentation
Issue: SPR-12570
2014-12-29 10:55:01 +01:00
Juergen Hoeller dc15070877 Consistent bridge method handling in annotation post-processors
Issue: SPR-12495
2014-12-22 16:42:47 +01:00
Juergen Hoeller 61a6bc0139 AbstractApplicationEventMulticaster populates ListenerRetriever cache in fully synchronized fashion
Issue: SPR-12545
(cherry picked from commit 2b0ada9)
2014-12-22 16:39:25 +01:00
Juergen Hoeller c087e51b80 AbstractFallbackTransactionAttributeSource's DefaultCacheKey takes targetClass into account (again)
Issue: SPR-12536
(cherry picked from commit 4efe6a8)
2014-12-22 16:26:25 +01:00
Rossen Stoyanchev 9cb1569e67 Synchronize message sending
Issue: SPR-12516
(backport for commit b796c1)
2014-12-08 12:02:19 -05:00
Rossen Stoyanchev ac5c361688 Check the user of a SockJS request
Issue: SPR-12497
(backport of commit dc5b5c)
2014-12-08 11:22:45 -05:00
Juergen Hoeller 328ba7b6d4 Polishing 2014-12-07 23:24:38 +01:00
Juergen Hoeller d97add075a Consistent bridge method handling in annotation post-processors
Issue: SPR-12495
(cherry picked from commit 03d4e1b)
2014-12-07 23:24:02 +01:00
Juergen Hoeller c8ff562d1f DefaultSingletonBeanRegistry's isDependent defensively checks for circular recursion
Issue: SPR-10787
(cherry picked from commit 15d3b88)
2014-12-07 22:21:55 +01:00
Juergen Hoeller 1d6978afd5 Revised ControllerAdvice basePackages handling (based on package names instead of java.lang.Package)
Issue: SPR-12506
(cherry picked from commit 4013fe0)
2014-12-07 22:20:32 +01:00
Juergen Hoeller 4660e61cf9 Consistent resolution of Class methods and static methods
Issue: SPR-12502
(cherry picked from commit 9ef0bdc)
2014-12-07 22:13:13 +01:00
Juergen Hoeller dfc79721cb AnnotationAttributesReadingVisitor defensively handles meta-annotation retrieval failure
Issue: SPR-12493
(cherry picked from commit 5018889)
2014-12-07 22:13:08 +01:00
Juergen Hoeller 90ae073854 Relaxed final declarations and protected doInvoke methods
Issue: SPR-12484
(cherry picked from commit 2496d68)
2014-12-07 22:13:03 +01:00
Juergen Hoeller 50c578c5a0 Polishing 2014-11-28 20:59:35 +01:00
Juergen Hoeller c89d8ec382 Polishing
Issue: SPR-12476
(cherry picked from commit 3b15849)
2014-11-27 20:56:41 +01:00
Juergen Hoeller 7e07f3d083 Polishing 2014-11-27 18:28:16 +01:00
Juergen Hoeller e039185fec Revised SqlRowSet javadoc
Issue: SPR-12476
(cherry picked from commit a6a837b)
2014-11-27 18:01:19 +01:00
Juergen Hoeller b45f1aa26f ExtendedBeanInfo ignores invalid bean properties (analogous to the JavaBeans Introspector)
Issue: SPR-12434
(cherry picked from commit eacd4a1)
2014-11-27 01:48:33 +01:00
Juergen Hoeller 054464709f Latest dependency updates (Tomcat 8.0.15, JRuby 1.7.16.1) 2014-11-26 16:07:21 +01:00
Juergen Hoeller 5ac868036f Clarified getAllAnnotationAttributes behavior
Issue: SPR-12473
2014-11-26 15:35:24 +01:00
Juergen Hoeller 58bea23f1d NotReadablePropertyException accepts root cause (analogous to NotWritablePropertyException)
Issue: SPR-12467
(cherry picked from commit feb91e8)
2014-11-26 12:11:40 +01:00
Juergen Hoeller 98569045cb Tracking ASM 5.0.4 development: Fix for ASM issue 317545
Issue: SPR-12470
(cherry picked from commit ab5856b)
2014-11-26 12:11:13 +01:00
Stephane Nicoll c2ffbf28c5 Fix typo 2014-11-24 21:01:53 +01:00
Stephane Nicoll a5e2afc980 Fix documentation formatting
Using the "quotes" substitution group by default leads to side effect
when the "*" character is used. This is especially true for AOP pointcut
or for MVC mappings.

Plain verbatim might work most of the time unless you intend to highlight
a piece of code or a comment.

Issue: SPR-12456
(cherry picked from commit 0d00b674f4)
2014-11-24 21:01:53 +01:00
Juergen Hoeller 5be50c8e5e Polishing
Issue: SPR-12462
2014-11-24 17:41:43 +01:00
Brian Clozel 1214624265 Fix location checks for servlet 3 resources
SPR-12354 applied new checks to make sure that served static resources
are under authorized locations.

Prior to this change, serving static resources from Servlet 3 locations
such as "/webjars/" would not work since those locations can be within
one of the JARs on path. In that case, the checkLocation method would
return false and disallow serving that static resource.

This change fixes this issue by making sure to call the
`ServletContextResource.getPath()` method for servlet context resources.

Note that there's a known workaround for this issue, which is using a
classpath scheme as location, such as:
"classpath:/META-INF/resources/webjars/" instead of "/webjars".

Issue: SPR-12432
(cherry picked from commit 161d3e3)
2014-11-24 17:41:11 +01:00
Brian Clozel 493e8463fa Use https for maven repositories
(cherry picked from commit 44ff143)
2014-11-23 00:07:51 +01:00
Juergen Hoeller 6d3a0d5735 Polishing 2014-11-23 00:05:27 +01:00
Juergen Hoeller 5f58cd3b79 Polishing
(cherry picked from commit 2675ce7)
2014-11-22 22:47:24 +01:00
Juergen Hoeller 8854c33bf7 Rearranged WebAsyncTask constructor interdependencies and assertions
Issue: SPR-12457
(cherry picked from commit aadb93f)
2014-11-22 22:45:24 +01:00
Juergen Hoeller f093a5f749 RequestMappingHandlerAdapter allows for overriding handleInternal
Issue: SPR-12460
(cherry picked from commit ece2c90)
2014-11-22 22:45:19 +01:00
Juergen Hoeller 1f45dd297d Unit test for JavaBeans introspection against FreeMarker Configuration class
Issue: SPR-12448
(cherry picked from commit 282aded)
2014-11-22 22:45:12 +01:00
Juergen Hoeller 21b8ce5284 MethodParameter generally uses volatile variables where applicable now (as well as a local copy of the parameterNameDiscoverer field)
Issue: SPR-12453
(cherry picked from commit 7fcadaa)
2014-11-22 22:44:38 +01:00
Juergen Hoeller 2490d1b38d MutablePropertySources uses an internal CopyOnWriteArrayList for defensiveness against concurrent modifications
Issue: SPR-12428
(cherry picked from commit 1ef06cc)
2014-11-22 22:44:30 +01:00
Spring Buildmaster ffa4736895 Next Development Version 2014-11-10 22:50:00 -08:00
Rossen Stoyanchev 9beae9ae42 Apply extra checks to static resource handling
- remove leading '/' and control chars
- improve url and relative path checks
- account for URL encoding
- add isResourceUnderLocation final verification

Issue: SPR-12354
2014-11-11 06:31:57 +01:00
Juergen Hoeller e42e2330b3 Polishing 2014-11-11 03:21:43 +01:00
Sebastien Deleuze b331d65019 Check STOMP headers against ending backslash
Issue: SPR-12418
(cherry picked from commit 1803348)
2014-11-11 03:02:25 +01:00
Juergen Hoeller 1823ce1fad ShallowEtagHeaderFilter skips "Cache-Control" header check on Servlet 2.5
Issue: SPR-12414
(cherry picked from commit bf9295b)
2014-11-06 23:19:33 +01:00
Rossen Stoyanchev 2d0a677117 Improve exception message
Issue: SPR-12230
(cherry picked from commit 7f4bf41)
2014-11-06 16:15:04 +01:00
Sam Brannen a09bc9b1c1 Log context cache statistics in the TCF
Prior to this commit, finding out how many application contexts had
been loaded within a test suite required the use of reflection and a
bit of hacking.

This commit addresses this issue by logging ContextCache statistics
whenever an application context is loaded by the Spring TestContext
Framework (TCF).

The log output can be enabled by setting the
"org.springframework.test.context.cache" logging category to DEBUG.

Issue: SPR-12409
(cherry picked from commit da04362)
2014-11-06 16:13:37 +01:00
Stephane Nicoll 260b451d5f Add documentation for the keep-alive flag
This commit updates the documentation of the task:executor element to
reference the keep-alive flag.

Issue: SPR-12407
(cherry picked from commit 6534d00)
2014-11-06 15:28:38 +01:00
Juergen Hoeller acefd8381d Polishing 2014-11-02 11:38:35 +01:00
Juergen Hoeller 7fa3e65d04 Test for cyclic type declaration handling in TypeDescriptor
Issue: SPR-9735
2014-11-02 11:38:28 +01:00
Juergen Hoeller 329ba2a913 Removed outdated TimerTaskExecutor reference (and other minor doc updates)
Issue: SPR-12389
2014-11-01 13:02:36 +01:00
Juergen Hoeller f691618967 Polishing 2014-11-01 13:00:54 +01:00
Juergen Hoeller fde0713a94 Upgrade to Hibernate 4.2.16 (and AspectJ 1.8.3) 2014-11-01 13:00:39 +01:00
Juergen Hoeller 4729483804 HibernateJpaSessionFactoryBean is compatible with Hibernate 4.3 as well now
Issue: SPR-12401
(cherry picked from commit bf5739c)
2014-11-01 12:59:15 +01:00
Juergen Hoeller d3ec48f490 PersistenceAnnotationBeanPostProcessor correctly detects JPA 2.1 synchronization attribute
Issue: SPR-12396
(cherry picked from commit a181b40)
2014-11-01 12:58:38 +01:00
Juergen Hoeller 2d874d724b SimpleMetadataReaderFactory is capable of resolving inner class names with dot syntax now (analogous to ClassUtils.forName)
Issue: SPR-12390
(cherry picked from commit 725ad0d)
2014-11-01 12:58:08 +01:00
Juergen Hoeller 91940f212e Normalized AnnotationAttributesReadingVisitor class layout and improved diagnostics through delegating to AnnotationUtils
Issue: SPR-12387
(cherry picked from commit b70c5d1)
2014-11-01 12:57:10 +01:00
Juergen Hoeller de8645b30c Test for <util:map> with LinkedCaseInsensitiveMap and specified key/value types
Issue: SPR-10994
(cherry picked from commit 9243869)
2014-11-01 12:56:24 +01:00
Juergen Hoeller c8b8dc5f97 TilesConfigurer defensively expects null from getResources in case of no resources found
Also includes order preservation for resource results with Tiles 2 as well as retrieval failure logging with Tiles 3.

Issue: SPR-12362
(cherry picked from commit cb86036)
2014-11-01 12:55:53 +01:00
Juergen Hoeller aa82da8507 Set ResponseStatusExceptionResolver.messageSource in the MVC Java config
Issue: SPR-12380
(cherry picked from commit 17b9bde)
2014-11-01 12:55:03 +01:00
Juergen Hoeller 16a4fe70f2 Polishing
(cherry picked from commit 2956049)
2014-10-22 17:14:23 +02:00
Juergen Hoeller 6117311906 QualifierAnnotationAutowireCandidateResolver calls BeanFactory.getType defensively
Issue: SPR-12191
(cherry picked from commit 73ce3b5)
2014-10-22 17:14:02 +02:00
Juergen Hoeller 6e5711a7fb Revised test for ImportBeanDefinitionRegistrar double scan
Issue: SPR-12334
2014-10-22 02:16:39 +02:00
Juergen Hoeller 15c8987cc4 Polishing 2014-10-22 01:10:01 +02:00
Juergen Hoeller 0e907764a4 Test for ImportBeanDefinitionRegistrar double scan
Issue: SPR-12334
(cherry picked from commit 38030ef)
2014-10-22 01:09:47 +02:00
Juergen Hoeller a305bf7ad1 Latest dependency updates (Hibernate 4.2.15, Tiles 3.0.5) 2014-10-22 01:09:31 +02:00
Juergen Hoeller e16f21c281 AnnotationUtils consistently logs introspection failures via lazily initialized logger
Issue: SPR-12325
Issue: SPR-12329
2014-10-22 01:08:41 +02:00
Juergen Hoeller 7507560e75 Reimplemented ServerEndpointExporter to avoid BeanPostProcessor role
Issue: SPR-12340
(cherry picked from commit 10328f1)
2014-10-22 01:05:19 +02:00
Juergen Hoeller a1c0905a7c Consistently accept empty Content-Type header and empty character encoding
Issue: SPR-12173
(cherry picked from commit d501137)
2014-10-22 01:04:22 +02:00
Juergen Hoeller 6f9d7da59e Consistent use of LinkedHashSet for interfaces (since interface order may matter in subtle cases)
(cherry picked from commit 5a63117)
2014-10-07 17:26:48 +02:00
Juergen Hoeller 47ed4d634b Explicitly detect (and log) private @Scheduled methods on CGLIB proxies
Issue: SPR-12308
(cherry picked from commit 01724d3)
2014-10-07 17:26:35 +02:00
Juergen Hoeller da2c30cd0c TextMessage.toString() does not throw StringIndexOutOfBoundsException for payload with multibyte characters
Issue: SPR-12307
(cherry picked from commit da14aee)
2014-10-07 17:15:23 +02:00
Juergen Hoeller a7eeedaf6a Fix outdated references to JobDetailBean
Issue: SPR-12306
(cherry picked from commit aaf69eb)
2014-10-07 17:13:53 +02:00
Juergen Hoeller 578c3dd853 Polishing
(cherry picked from commit d6be433)
2014-10-07 00:53:01 +02:00
Juergen Hoeller 9758bc7833 Consistent reset of resource holders on doBegin failure
Issue: SPR-12280
(cherry picked from commit e58b33a)
2014-10-07 00:06:49 +02:00
Juergen Hoeller c672678423 Provider declaration for @Value method argument works again
Issue: SPR-12297
(cherry picked from commit 70fec47)
2014-10-07 00:02:03 +02:00
Juergen Hoeller ac4103d1b9 Further locking optimizations for the retrieval of non-singleton beans
Issue: SPR-12250
(cherry picked from commit 9d83281)
2014-10-07 00:01:40 +02:00
Juergen Hoeller d23b033f61 Polishing 2014-10-01 01:20:01 +02:00
Juergen Hoeller c74bad6e07 Remove outdated references to ContextLoaderServlet
Issue: SPR-7725
(cherry picked from commit 69998e3)
2014-10-01 01:14:57 +02:00
Juergen Hoeller f41e0bc796 Latest dependency updates (POI 3.10.1, JRuby 1.7.16) 2014-09-30 20:27:19 +02:00
Stephane Nicoll c7b6b72e73 Optimize ResolvableType cache
Prior to this commit, the ResolvableType static cache was holding a lot
of duplicates for simple types. We are using too much metadata to compute
the key when the class has no generic information. so setFoo(String foo)
and setBar(String bar) would result in two entries in the cache because
the TypeProvider is different. On a very simple application 65% of the
entries in the cache were duplicate.

When the type is a Class with no generic information, the ResolvableType
instance is a simple wrapper around it so we might just as well not cache
it at all as the cost of finding it back from the cache is higher than
creating that simple wrapper.

This commit adds an explicit check; if the type is a simple Class we just
return a "resolved" ResolvableType instance for it. On a few test cases,
this reduces the size of the cache by 85%

Issue: SPR-12275
(cherry picked from commit 6f1acdd)
2014-09-30 20:22:51 +02:00
Anders D. Johnson 1df8133237 Fix condition vs. conditional typo in documenation
Issue: SPR-12273
(cherry picked from commit bc0e59f)
2014-09-30 09:53:21 +02:00
Juergen Hoeller 43d40187d9 Polishing 2014-09-29 23:40:32 +02:00
Juergen Hoeller b91cab1fde Polishing 2014-09-26 22:18:15 +02:00
Juergen Hoeller d46c3fc7bf Deprecated BeanDefinitionDocumentReader's setEnvironment method in favor of access via XmlReaderContext
Issue: SPR-12248
2014-09-26 22:17:58 +02:00
Brian Clozel 3267e5aa33 Update default SockJS CDN location
This commit updates the default location of the SockJS' client library.
The previous location is being retired by the project maintainers.

The new default location is backed by several CDN providers:
* https://cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js

See sockjs/sockjs-client#198

Issue: SPR-12254
(cherry picked from commit a6e1c53)
2014-09-26 22:17:04 +02:00
Juergen Hoeller 251970a2f9 Tracking ASM 5.0.4 development: Fix for ASM issue 317539
Issue: SPR-12255
(cherry picked from commit e8486e2)
2014-09-26 21:47:41 +02:00
Juergen Hoeller 901346eb7d WebAsyncManager avoids concurrentResult.toString() and builds correct DeferredResultProcessingInterceptor keys
Issue: SPR-12253
(cherry picked from commit bf99d6a)
2014-09-26 21:47:30 +02:00
Juergen Hoeller 1ffefcb548 LiveBeansView escapes double quotes in resource descriptions
Issue: SPR-12252
(cherry picked from commit 92f7121)
2014-09-26 21:47:21 +02:00
Juergen Hoeller 0c680d6d03 Defensively use setRemoveOnCancelPolicy for JDK 6 compatibility
Issue: SPR-12238
(cherry picked from commit e003d21)
2014-09-26 21:47:01 +02:00
Juergen Hoeller b87329cda7 Remove outdated references to ContextLoaderServlet
Issue: SPR-7725
(cherry picked from commit 94e26fd)
2014-09-26 21:40:28 +02:00
Juergen Hoeller f21c8c37fb Avoid use of fragile Swing classes in tests (for compatibility with JDK 8u40)
Issue: SPR-12235
(cherry picked from commit a80495b)
2014-09-26 21:40:09 +02:00
Juergen Hoeller 50e50d0c18 Polishing
(cherry picked from commit 16325c2)
2014-09-20 00:51:19 +02:00
Juergen Hoeller 205e681295 Backported tests for property source ordering
Issue: SPR-12198
(cherry picked from commit 90b93ff)
2014-09-19 00:20:00 +02:00
Juergen Hoeller 29abca5399 Explicit exclusion of bridge methods in annotation post-processors (for Java 8 compatibility)
Issue: SPR-12187
(cherry picked from commit f4219ca)
2014-09-19 00:11:44 +02:00
Juergen Hoeller 60d5ff8dd5 Polishing
(cherry picked from commit a833889)
2014-09-17 22:10:08 +02:00
Juergen Hoeller d2e8b7e6ee ScheduledAnnotationBeanPostProcessor avoids needless re-scanning of non-annotated classes
Issue: SPR-12189
(cherry picked from commit 58b22ce)
2014-09-17 22:09:43 +02:00
Juergen Hoeller 7d55715213 Polishing 2014-09-17 13:53:12 +02:00
Juergen Hoeller d07230cf6a BeanDefinitionParserDelegate does not silently ignore 1.x 'singleton' attribute
Issue: SPR-12167
(cherry picked from commit 0cf472b)
2014-09-17 13:36:54 +02:00
Juergen Hoeller bf881e4544 Log warning for single optional constructor when no default constructor to fall back to
Issue: SPR-12161
(cherry picked from commit 80cec01)
2014-09-17 13:35:12 +02:00
Juergen Hoeller 36a1d6242e Latest dependency updates (Reactor 1.1.4) 2014-09-17 02:39:16 +02:00
Juergen Hoeller 10d4888408 Polishing 2014-09-17 02:37:32 +02:00
Juergen Hoeller f8b729aa5f Polishing 2014-09-17 01:51:23 +02:00
Juergen Hoeller b39e66b897 GenericTypeAwarePropertyDescriptor implements equals/hashCode for proper lookups on IBM JVM 6
Issue: SPR-12185
2014-09-17 01:42:30 +02:00
Juergen Hoeller 64849a46b4 Client request implementations enforce RFC 6265 (cookies in a single header)
Issue: SPR-12196
(cherry picked from commit 26a93b6)
2014-09-17 00:44:30 +02:00
Juergen Hoeller afb0342924 Accept empty Content-Type header
Issue: SPR-12173
(cherry picked from commit 70412a9)
2014-09-17 00:37:38 +02:00
Sam Brannen bea9f3ff76 Make SocketUtils a concrete class
Per the Javadoc for the SocketUtils() constructor, SocketUtils can be
instantiated as a Spring Bean in XML configuration files; however,
SocketUtils is currently abstract which prevents such usage.

This commit removes the 'abstract' declaration thereby allowing
SocketUtils to be instantiated as a Spring bean.

Issue: SPR-12169
(cherry picked from commit 559e81b)
2014-09-17 00:36:21 +02:00
Spring Buildmaster 31094f3ef6 Next Development Version 2014-09-04 01:18:19 -07:00
Juergen Hoeller d765698ac3 Polishing 2014-09-04 03:06:37 +02:00
Juergen Hoeller d081a4530c Polishing 2014-09-04 02:37:36 +02:00
Juergen Hoeller 97bd0ccfec Polishing 2014-09-04 01:43:15 +02:00
Juergen Hoeller 8543a5548e CachingConnectionFactory proceeds to physicalClose in case of logicalClose exceptions
Issue: SPR-12148
(cherry picked from commit 82f8b43)
2014-09-04 00:58:50 +02:00
Juergen Hoeller d2ef6dcb8d Polishing 2014-09-03 13:49:37 +02:00
Juergen Hoeller bff2bf2cdb AbstractAutowireCapableBeanFactory avoids early FactoryBean instantiation on currently created configuration bean
Issue: SPR-12141
(cherry picked from commit 4432c41)
2014-09-03 13:48:47 +02:00
Juergen Hoeller 72d62a7113 Cache.get(key, type) explicitly defines an IllegalStateException in case of a type mismatch
Issue: SPR-12145
(cherry picked from commit 2f93759)
2014-09-03 13:48:19 +02:00
Stephane Nicoll e1a01d4c3c Improve configuration changes in GuavaCacheManager
Prior to this commit, setting the parameters used to build the caches
was fragile in static mode as the caches were created right when the
setCacheNames setter was called.

This commit provides a better handling of such arguments and also
provide a way to restore the dynamic mode if necessary.

Issue: SPR-12120
(cherry picked from commit d47c543)
2014-09-01 14:08:14 +02:00
Sebastien Deleuze b7726ff48c Improve ResolvableType.hashCode() for better performance
Prior to this commit, when there was a lot of entries in the
ResolvableType.cache HashMap, getting a simple value could
take a lot of time due to a lot of calls to ResolvableType.equals().
ResolvableType.equals() used this.type, getSource(),
this.variableResolver.getSource() and this.componentType, but
ResolvableType.hashCode() used only this.type.

With this commit, ResolvableType.hashCode() now uses the same
fields than ResolvableType.equals().

Performance on the spring-resolvabletype-benchmark project:
 - 8000 us before this commit
 - 120 us with this commit

Issue: SPR-12122
(cherry picked from commit 7ea69fb)
2014-09-01 14:07:43 +02:00
Sam Brannen a52ff46fea Include ALL source artifacts in published sources jars
This commit updates the Gradle build to ensure that *all* source
artifacts are including in published 'sources' jars.

To achieve this, we are now assembling 'sources' jars from
'sourceSets.main.allSource' and not explicitly including or excluding
anything by default.

Issue: SPR-12085
(cherry picked from commit 07629a3c7c)
2014-08-26 16:51:26 +02:00
Juergen Hoeller eb3509a37c Polishing 2014-08-23 02:02:38 +02:00
Juergen Hoeller 93ad7f8def Polishing 2014-08-23 01:28:10 +02:00
Juergen Hoeller 54ba5c5e7b Polishing 2014-08-22 23:39:37 +02:00
Juergen Hoeller 249c688e9b DispatcherServlet's checkMultipart detects wrapped MultipartRequest as well
Issue: SPR-12114
(cherry picked from commit 786fd92)
2014-08-22 23:18:39 +02:00
Juergen Hoeller e324c2ab4a Polishing 2014-08-22 00:14:10 +02:00
Juergen Hoeller 39a23660fa PropertySource implementations perform conversion to String arrays via StringUtils (getting rid of EMPTY_NAMES_ARRAY)
(cherry picked from commit b73c531)
2014-08-22 00:09:53 +02:00
Juergen Hoeller 25971d9927 Consistent attribute documentation/formatting in spring-context and spring-mvc schemas
(cherry picked from commit b5763fe)
2014-08-22 00:04:12 +02:00
Juergen Hoeller 379e5abd83 ServerEndpointExporter can initialize itself based on a late-provided ServletContext as well (for Boot)
Also allows for direct setting of a ServerContainer and for custom triggering of endpoint registration.

Issue: SPR-12109
(cherry picked from commit 11805b6)
2014-08-22 00:02:14 +02:00
Juergen Hoeller 8fb7b694dd PersistenceAnnotationBeanPostProcessor defensively handles BeanDefinition access for extended EntityManagers
Issue: SPR-8834
(cherry picked from commit 592e344)
2014-08-22 00:01:39 +02:00
Juergen Hoeller 21d0ce5af8 DefaultListableBeanFactory defensively handles BeanDefinition access in getBean(Class)
Issue: SPR-10542
(cherry picked from commit 9d3d6d5)
2014-08-22 00:01:16 +02:00
Juergen Hoeller fb452fad7c Upgrade to Apache HttpComponents HttpClient 4.3.5 and HttpAsyncClient 4.0.2
Issue: SPR-12100
(cherry picked from commit 5cd1e6a)
2014-08-19 21:41:57 +02:00
Juergen Hoeller 2dd2b7175f ShallowEtagHeaderFilter supports Servlet 3.1's setContentLengthLong as well
Issue: SPR-12097
(cherry picked from commit 0c32d66)
2014-08-19 21:39:43 +02:00
Juergen Hoeller f418e6e981 Polishing 2014-08-18 22:24:43 +02:00
Juergen Hoeller 5e2b9b6f07 Latest dependency updates (AspectJ 1.8.2, Jackson 2.3.4) 2014-08-18 22:19:33 +02:00
Stephane Nicoll a95a3ae4ec Exclude infra modules from public distribution
Prior to this commit, spring-build-src and spring-framework-bom
internal artifacts were published as part of the distribution bundle.

This commit excludes those projects so that those unnecessary artifacts
are no longer shipped.

Issue: SPR-12087
2014-08-18 12:20:24 +02:00
Juergen Hoeller 0c0d23d0f4 Polishing 2014-08-15 00:58:05 +02:00
Juergen Hoeller a31ebb6c1e Polishing 2014-08-15 00:07:25 +02:00
Juergen Hoeller 42c090efa4 Polishing
Issue: SPR-12079
(cherry picked from commit 92bd240)
2014-08-13 15:28:36 +02:00
Juergen Hoeller 035a9b91d2 BeanNameViewResolver ignores non-View beans
Issue: SPR-12079
(cherry picked from commit 626a5fe)
2014-08-13 15:28:09 +02:00
Juergen Hoeller a7492fa55b Polishing 2014-08-12 22:24:50 +02:00
Juergen Hoeller ecf23ff6cd Remove outdated IDE, JIRA and Tomcat references
Issue: SPR-7521
(cherry picked from commit f1f1c4c)
2014-08-12 22:11:49 +02:00
Juergen Hoeller 4129f00a02 Polishing
(cherry picked from commit 4db258b)
2014-08-11 22:35:26 +02:00
Juergen Hoeller e2d71a8ccd Polishing 2014-08-11 15:45:54 +02:00
Juergen Hoeller 1034b0dae2 Tiles 3 TilesViewResolver allows for specifying custom TilesView subclasses as well
Issue: SPR-12075
(cherry picked from commit 7c57424)
2014-08-11 15:45:14 +02:00
Juergen Hoeller c08ded769a Polishing (includes varargs for selected String array setters) 2014-08-09 00:19:56 +02:00
Juergen Hoeller d61353db51 AnnotatedElementUtils adapts post-processed values to AnnotationAttributes as well
Issue: SPR-12065
(cherry picked from commit ef51d4d)
2014-08-09 00:19:28 +02:00
Rossen Stoyanchev 96efafef97 Fix documentation issue for user destinations.
Issue: SPR-11992
2014-07-31 17:15:52 -04:00
Rossen Stoyanchev fbf24a927d Update Javadoc (cherry-picked from 162734)
Issue: SPR-12028
2014-07-29 18:07:36 -04:00
Juergen Hoeller f2eea9d911 Polishing
(cherry picked from commit 8f484d3)
2014-07-29 11:50:50 +02:00
Juergen Hoeller cac0683dab Rearranged statement whitespace in Velocity macros
(cherry picked from commit 7bc9660)
2014-07-29 11:47:12 +02:00
Juergen Hoeller 9ad6618207 Polishing 2014-07-28 22:47:22 +02:00
Juergen Hoeller 84a4433898 AbstractTypeHierarchyTraversingFilter leniently ignores non-loadable super classes and interfaces
Issue: SPR-12042
(cherry picked from commit a3163ce)
2014-07-28 22:27:37 +02:00
Juergen Hoeller 44fac40a2f Latest dependency updates (Hibernate Validator 4.3.2, propdeps plugin 0.0.7) 2014-07-28 22:27:28 +02:00
Juergen Hoeller 6e95b2613e Polishing 2014-07-24 18:35:49 +02:00
Arjen Poutsma fca72f6b65 StaxStreamXMLReader ignores significant whitespace
The StaxStreamXMLReader no longer handles all whitespace as ignorable
whitespace.

Issue: SPR-12000
(cherry picked from commit d6950d8)
2014-07-24 17:57:14 +02:00
Juergen Hoeller 0dca31c39b Typo fixes and cleanup of outdated Java 5 references
Issue: SPR-12020
(cherry picked from commit d4fe732)
2014-07-24 17:56:35 +02:00
Juergen Hoeller d45f743248 TypeDescriptor's getResolvableType() and getSource() are @since 4.0
(cherry picked from commit 0ce90f1)
2014-07-24 17:55:39 +02:00
Juergen Hoeller acbd61b87a ConcurrentMapCacheManager recreates caches in case of setAllowNullValues change
Issue: SPR-12026
(cherry picked from commit 7848412)
2014-07-24 17:54:22 +02:00
Juergen Hoeller dcb83c2daf Autowiring rejects self references to a factory method on the same bean as well
Issue: SPR-12018
(cherry picked from commit 496492b)
2014-07-24 17:53:21 +02:00
Juergen Hoeller 5342d92db7 ResourceHttpMessageConverter does not call contentLength() on InputStreamResource
Issue: SPR-12017
(cherry picked from commit f0bcb77)
2014-07-24 17:50:31 +02:00
Juergen Hoeller 7af1e002bd AbstractMessageListenerContainer calls "Session.recover()" in case of rollback attempt on non-transacted Session
Issue: SPR-12015
(cherry picked from commit c082220)
2014-07-24 17:49:34 +02:00
Juergen Hoeller abd5f18e85 AbstractApplicationContext logs initialization exception before trying to cancel refresh attempt
Issue: SPR-12010
(cherry picked from commit 5e2fbe7)
2014-07-24 17:48:08 +02:00
Juergen Hoeller 940ae0c528 ClassUtils.isCacheSafe defensively catches SecurityException (for Google App Engine compatibility)
Issue: SPR-12002
(cherry picked from commit 48fea0b)
2014-07-24 17:46:42 +02:00
Juergen Hoeller 5f77dce1ee SelectedValueComparator defensively handles null values in exhaustiveCompare
Issue: SPR-12001
(cherry picked from commit 980f971)
2014-07-24 17:46:04 +02:00
Juergen Hoeller 0ef17a2f2c NestedIOException can use IOException constructor with cause argument on JDK 6+
(cherry picked from commit c18b6bf)
2014-07-24 17:44:57 +02:00
Juergen Hoeller 452260316e DefaultListableBeanFactory's registerBeanDefinition only calls resetBeanDefinition in case of pre-existing bean definition or pre-existing singleton instance
Issue: SPR-8318
(cherry picked from commit a4968b9)
2014-07-24 17:43:59 +02:00
Stephane Nicoll c0815407bb Revert ea16ce0
The latest build preview of jdk 1.8.0_20 does not exhibit the problem
anymore.

Issue: SPR-11973
2014-07-23 19:43:09 +02:00
Juergen Hoeller 845528a6cb Polishing 2014-07-10 18:18:44 +02:00
Juergen Hoeller ed88155f55 Reverted test for 1.8 bytecode parsing - 4.0.x tests are still on target 1.7
Issue: SPR-11979
2014-07-10 17:45:08 +02:00
Brian Clozel a3a76d8c15 Fix UriComponentsBuilder.fromUriString parsing error
This commit fixes cases where part of the URI was mistaken for the
userinfo when:
* the URI did not contain any path
* the query string contained the "@"

Issue: SPR-11970
(cherry picked from commit a4484bb)
2014-07-10 16:46:33 +02:00
Juergen Hoeller 10a039000b Test ASM5's bytecode parsing of INVOKESPECIAL/STATIC on interfaces
Issue: SPR-11979
(cherry picked from commit 29f6f3d)
2014-07-10 16:44:59 +02:00
Spring Buildmaster 7de42efaf2 Next development version 2014-07-07 20:49:34 -07:00
Phillip Webb 7dcc01e69b Disable processing javac warnings
Set `-Xlint:-processing` to disable annotation processing warnings.
Required when building on JDK 1.8.0_20-b05 due to JDK-8039469.

Issue: SPR-11973
(cherry picked from commit ea16ce0)
2014-07-07 19:22:07 -07:00
Juergen Hoeller adb73d5f35 Remaining polishing 2014-07-08 00:21:17 +02:00
Juergen Hoeller 7eee7d2e30 Polishing
Issue: SPR-8045
2014-07-08 00:04:19 +02:00
Juergen Hoeller d72ee809b7 Polishing
(cherry picked from commit b559f15)
2014-07-07 21:51:01 +02:00
Juergen Hoeller 31a251e44a Polishing 2014-07-07 20:53:26 +02:00
Brian Clozel ce6adfbe35 Remove Jackson dependency from TransportHandler hierarchy
Prior to this change, AbstractHttpReceivingTransportHandler had a direct
dependency on a Jacckson Exception (checking that exception in a catch
clause). This can cause issues for applications that don't have that
dependency.

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

Issue: SPR-11963
(cherry picked from commit e549103)
2014-07-07 20:39:29 +02:00
Juergen Hoeller 60d3a7f300 RestTemplate accepts getMessageConverters() List on setMessageConverters again
Issue: SPR-11962
(cherry picked from commit 1222ca3)
2014-07-07 20:37:35 +02:00
Juergen Hoeller 6dbb465585 TypedValue implements equals/hashCode based on value and TypeDescriptor
Issue: SPR-11960
(cherry picked from commit a8848cb)
2014-07-07 20:36:53 +02:00
Juergen Hoeller 47de943926 Upgraded to AspectJ 1.8.1 (also declared as recommended user version) and Tomcat 8.0.9 (first stable version)
Issue: SPR-11957
2014-07-05 11:14:34 +02:00
Juergen Hoeller 0164f8691a CachedSessionInvocationHandler includes durable flag in ConsumerCacheKey
Issue: SPR-11956
(cherry picked from commit 6fd0fc7)
2014-07-05 08:56:10 +02:00
Juergen Hoeller 077c624b73 ConstructorResolver's exception message on null factory-bean hints at potential BeanPostProcessor involvement
Issue: SPR-11951
(cherry picked from commit 6c41cc3)
2014-07-04 22:32:10 +02:00
Juergen Hoeller dae81fd5e6 CachedSessionInvocationHandler properly caches createSharedConsumer results
Issue: SPR-11956
(cherry picked from commit 7a7641b)
2014-07-04 22:31:38 +02:00
Juergen Hoeller 837f27851f Jms2MessageProducerInvocationHandler specifically delegates all CompletionListener-based send methods
Issue: SPR-11950
(cherry picked from commit b6a7957)
2014-07-04 16:13:14 +02:00
Juergen Hoeller c3b992c5c4 Jms2MessageProducerInvocationHandler properly delegates to CachedMessageProducer.this
Issue: SPR-11949
(cherry picked from commit 134e5a2)
2014-07-04 00:27:25 +02:00
Juergen Hoeller f8ab4f6980 Reference documentation backports
Issue: SPR-11944
Issue: SPR-11945
2014-07-03 22:40:02 +02:00
Juergen Hoeller 77406eded8 Refined DefaultJpaDialect's InvalidIsolationLevelException message
Issue: SPR-8325
(cherry picked from commit af13310)
2014-07-03 22:34:14 +02:00
Rossen Stoyanchev 977c5ca439 Synchronize request init in AbstractHttpSockJsSession
Although unlikely in practice (but not impossible), the SockJS
integration tests write a message while the request is initializing.
This change adds synchronization around request intiailization
for the SockJS HTTP sesion.

This is a backport of:
https://github.com/spring-projects/spring-framework/commit/59e02e63c427659e701cc371d210faf1a85c1d42

Issue: SPR-11916
2014-07-02 18:28:48 -04:00
Juergen Hoeller ede2150544 Polishing 2014-07-02 17:32:43 +02:00
Juergen Hoeller 07edf11e21 Defensively check for pre-resolved FactoryBean.getObject() results in circular reference scenarios
Issue: SPR-11937
(cherry picked from commit d870b38)
2014-07-02 00:23:32 +02:00
Juergen Hoeller f27b9498be CachedIntrospectionResults uses SoftReference instead of WeakReference for non-cache-safe entries
Issue: SPR-11867
2014-07-02 00:22:24 +02:00
Rossen Stoyanchev e9ecaf6f4a Fix issue with StompSubProtocolHandler initialization
This change ensures that StompSubProtocolHandler is injected with an
ApplicationEventPublisher for both the Java and XML config.

Backport for:
https://github.com/spring-projects/spring-framework/commit/0dddb6f3e19377cb397f0396e85734df2c19c2b6
https://github.com/spring-projects/spring-framework/commit/4372dac002be4dd9be9568fafb442cde5b6d1ada

Issue: SPR-11825
2014-07-01 14:59:51 -04:00
Rossen Stoyanchev 5092414842 Improve SubscriptionRegistry subscription removal
This a backport of:
https://github.com/spring-projects/spring-framework/commit/ce20abde51ac52dde2f318e28004af80cca886fe
https://github.com/spring-projects/spring-framework/commit/083d415fc4d54ce19f7939b3d837f1f1eda5732a

Issue: SPR-11931, SPR-11930
2014-07-01 14:35:22 -04:00
Rossen Stoyanchev 6438209557 Remove JDK 1.7 guards 2014-07-01 14:33:27 -04:00
Juergen Hoeller 7396210d40 Package javadoc cleanup for web.servlet.mvc, removing outdated references
Issue: SPR-11935
(cherry picked from commit ba522e5)
2014-07-01 18:12:42 +02:00
Juergen Hoeller b9935e9fe3 BeanFactoryAnnotationUtils consistently applies bean name fallback when no BeanDefinition present
Issue: SPR-11915
(cherry picked from commit f8b6114)
2014-07-01 18:12:23 +02:00
Juergen Hoeller 86ea3059b6 Polishing 2014-07-01 15:11:36 +02:00
Juergen Hoeller 4384c1ae50 Upgrade to Reactor 1.1.3 2014-07-01 15:10:09 +02:00
Juergen Hoeller 347b7313b8 Dependency updates (as appropriate for the 4.0.x line) 2014-07-01 12:31:47 +02:00
Juergen Hoeller 3fb05017a1 Reference documentation overhaul (backported from master)
Issue: SPR-11853
Issue: SPR-11906
2014-07-01 12:31:16 +02:00
Juergen Hoeller 48f753f368 Polishing (backported from master) 2014-07-01 12:12:42 +02:00
Juergen Hoeller c16032b98a Consistent declaration of private static final logger variables
Issue: SPR-11905
(cherry picked from commit 18131bf)
2014-07-01 12:09:29 +02:00
Juergen Hoeller c32d5592c7 AbstractAutowireCapableBeanFactory's createBean/autowireBean/configureBean use CacheUtils.isCacheSafe to re-enable PropertyDescriptor caching
Issue: SPR-11875
(cherry picked from commit 974bd43)
2014-07-01 12:07:26 +02:00
Juergen Hoeller 5f695a47db Cache Class.getDeclaredMethods() results and avoid pattern matching in isCglibRenamedMethod as far as possible
Issue: SPR-11882
Issue: SPR-11894
2014-07-01 12:06:34 +02:00
Juergen Hoeller 74c878ee0e AnnotationUtils defensively catches and logs unexpected exceptions from retrieval attempts (proceeding like the annotation wasn't there)
Issue: SPR-11874
(cherry picked from commit 2c0c081)
2014-07-01 12:05:38 +02:00
Juergen Hoeller 178d58cc75 ConfigurationClassParser falls back to ASM-based resolution of member classes in case of NoClassDefFoundError
Issue: SPR-11844
(cherry picked from commit 6aa9c40)
2014-07-01 12:02:27 +02:00
Juergen Hoeller 7b38b41bf5 ConfigurationClassPostProcessor skips pre-processed bean definitions now (avoiding side effects in case of multiple CCPPs)
Issue: SPR-11858
(cherry picked from commit b625512)
2014-07-01 12:01:50 +02:00
Juergen Hoeller 6403479902 TypeDescriptor's nested type traversal leniently returns null in case of unresolvable nested type
Issue: SPR-11898
(cherry picked from commit d663580)
2014-07-01 12:00:20 +02:00
Juergen Hoeller 5af1a691bc DefaultAdvisorChainFactory never passes null into ClassFilter, enabling async advisor to work without target class as well
Issue: SPR-11910
(cherry picked from commit a9b650f)
2014-07-01 11:59:43 +02:00
Juergen Hoeller 9f967b1d81 SharedEntityManagerCreator immediately throws TransactionRequiredException on persist, merge, remove etc (as required by JPA spec)
Issue: SPR-11923
(cherry picked from commit 045d735)
2014-07-01 11:59:11 +02:00
Juergen Hoeller e50507206f DefaultSubscriptionRegistry's removeSubscriptionInternal defensively handles non-existing destinations
Issue: SPR-11832
(cherry picked from commit 5a8e470)
2014-07-01 11:58:08 +02:00
Juergen Hoeller c73ac075c4 UriComponentBuilder allows for multiple independent build() calls on same builder instance
Issue: SPR-11885
(cherry picked from commit d239016)
2014-07-01 11:57:45 +02:00
Juergen Hoeller 0bf0901b8f SelectTag consistently checks specified 'multiple' attribute now, never falling back to forceMultiple in case of user-provided value
Issue: SPR-11903
(cherry picked from commit 779ca99)
2014-07-01 11:57:15 +02:00
Juergen Hoeller 7d94b5e853 FrameworkPortlet properly initializes StandardPortletEnvironment's property sources and exposes local PortletRequestAttributes in case of pre-bound ServletRequestAttributes as well
Issue: SPR-11816
Issue: SPR-11295
2014-07-01 11:55:19 +02:00
Juergen Hoeller 8ddbbc2e67 PathMatchingResourcePatternResolver's findPathMatchingResources needs to check for VFS before checking isJarResource
ResourceUtils isFileURL also detects "vfsfile" as a file system protocol (again).

Issue: SPR-11887
2014-07-01 11:54:16 +02:00
Juergen Hoeller 0a34f86333 MBeanExportConfiguration's SpecificPlatform properly calls afterPropertiesSet
Also makes the nested SpecificPlatform enum public for Spring Boot to reuse it.

Issue: SPR-11877
(cherry picked from commit 6f68b03)
2014-07-01 11:52:53 +02:00
Juergen Hoeller a0c86ae455 WebSphereUowTransactionManager exposes SmartTransactionObject
Issue: SPR-11876
(cherry picked from commit 03f3412)
2014-07-01 11:51:33 +02:00
Juergen Hoeller 0761ee99ae Workaround for WFLY-3474 NullPointerException
Prior to this commit, the ServletResponseHttpHeaders.get method
would throw an NPE when used under Wildfly 8.0.0.Final and 8.1.0.Final.
This can be traced to WFLY-3474, which throws an NPE when calling
HttpServletResponse.getHeaders("foo") and that header has not
been defined prior to that.
This would cause NPE being thrown by AbstractSockJsService when
checking for CORS HTTP headers in the server HTTP response.

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

Issue: SPR-11919
(cherry picked from commit 24cdefb)
2014-07-01 11:51:01 +02:00
Juergen Hoeller 9054f4fb58 HttpHeaders fails getAllow if set to EmptyCollection
Prior to this commit, calls to getAllow would fail is setAllow was set
to an EmptyCollection right before.

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

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

Issue: SPR-11917
(cherry picked from commit 9919a98)
2014-07-01 11:46:53 +02:00
Rossen Stoyanchev 41e78d0f50 Fix failing test 2014-06-29 22:08:25 -04:00
Rossen Stoyanchev 526d5ba253 Fix warnings from sniff task on master 2014-06-29 17:44:43 -04:00
Rossen Stoyanchev 40c203ca71 Minor fix in STOMP broker relay
This is a backport for a change made as part of:
https://github.com/spring-projects/spring-framework/commit/113fd1180a14656a89d6d6639d259c577d70fbc5

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

This is a backport of:
https://github.com/spring-projects/spring-framework/commit/3af488a701834f61b89cd84040605a6252962e9c

Issue: SPR-11884
2014-06-29 16:58:13 -04:00
Rossen Stoyanchev 618771d59d Add check for unused WebSocket sessions
Sessions connected to a STOMP endpoint are expected to receive some
client messages. Having received none after successfully connecting
could be an indication of proxy or network issue. This change adds
periodic checks to see if we have not received any messages on a
session which is an indication the session isn't going anywhere
most likely due to a proxy issue (or unreliable network) and close
those sessions.

This is a backport for commit:
https://github.com/spring-projects/spring-framework/commit/a3fa9c979777e554efad0df429041767f05dfdb8

Issue: SPR-11884
2014-06-29 16:58:13 -04:00
Rossen Stoyanchev 87077d3fad Add support for setting removeOnCancelPolicy
This change introduces removeOnCancelPolicy on ThreadPoolTaskScheduler
and ScheduledExecutorFactoryBean and sets it to true for SockJS.
This ensures that cancelled tasks are removed immediately to avoid
the "unbounded retention of cancelled tasks" that is mentioned in
the Javadoc ScheduledThreadPoolExecutor:

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

This is a backport for:
https://github.com/spring-projects/spring-framework/commit/7441f2301281049cff7224bf650646490388080e

Issue: SPR-11918
2014-06-29 16:57:58 -04:00
Rossen Stoyanchev c0c3618906 Fix concurrency issues in SockJS session impls
Cherry-picked from:
https://github.com/spring-projects/spring-framework/commit/fcf6ae83285890f2e8e7de77dcc6e633129978e8

Issue: SPR-11916
2014-06-29 15:50:55 -04:00
Spring Buildmaster 0405bb401a Next development version 2014-05-20 06:41:20 -07:00
Juergen Hoeller e3e71ba92a Servlet 3 multipart request implements getParameterMap defensively as well
Issue: SPR-11074
(cherry picked from commit c7d1c49)
2014-05-20 11:11:56 +02:00
Juergen Hoeller d4e1b9eb4c Polishing
(cherry picked from commit 6fef8b9)
2014-05-20 11:11:33 +02:00
Rossen Stoyanchev 44ee51a6c9 Disable URL resolution in DTD declarations
Issue: SPR-11768
2014-05-19 22:23:51 -04:00
Juergen Hoeller ba6d187b5c Consistent MvcUriComponentsBuilder assertion handling
(cherry picked from commit 61b47ba)
2014-05-20 01:51:33 +02:00
Juergen Hoeller 54636b3f7c Further UriComponentsBuilder javadoc revision 2014-05-20 01:13:18 +02:00
Juergen Hoeller 545c28fd4e Minor UriComponentsBuilder javadoc revision 2014-05-20 00:47:03 +02:00
Juergen Hoeller 95a7bfd327 Consistent use of IllegalStateException instead of InternalError for UnsupportedEncodingException cause
(cherry picked from commit d9b39ad)
2014-05-20 00:42:48 +02:00
Juergen Hoeller ccebbf74ec Latest patches from ASM trunk
(cherry picked from commit cfc720d)
2014-05-19 22:59:39 +02:00
Juergen Hoeller c350080f99 StringUtils.parseLocaleString parses variant correctly when variant contains country code
This commit also includes a JUnit 4 style revision of StringUtilsTests and ObjectUtilsTests.

Issue: SPR-11806
(cherry picked from commit 295a6ae)
2014-05-19 22:46:47 +02:00
Juergen Hoeller 6b9738fd68 Building against latest javax.el 2.2.x version
(cherry picked from commit f73a8ba)
2014-05-19 20:46:42 +02:00
Juergen Hoeller 1120680c65 Consistently log Class.getName() instead of Class.toString(), avoiding double class term in log message
Issue: SPR-11804
(cherry picked from commit 2619955)
2014-05-19 20:46:14 +02:00
Juergen Hoeller a2ef2c9d3e Refined check for NoClassDefFoundError in getTestExecutionListeners()
Issue: SPR-11804
(cherry picked from commit 41ed228)
2014-05-19 17:28:37 +02:00
Juergen Hoeller 1bbc032071 Backported proper updateAccessedAttributes test
Issue: SPR-11738
(cherry picked from commit 6188550)
2014-05-19 16:42:42 +02:00
Juergen Hoeller c70a81a805 Javadoc fixes 2014-05-19 00:08:46 +02:00
Juergen Hoeller 3c709a763d Servlet 3 multipart request implements getParameterNames defensively (for WebLogic 12 compatibility)
This commit also includes lazy resolution support for StandardServletMultipartResolver, along the lines of existing lazy mode in CommonsMultipartResolver.

Issue: SPR-11074
Issue: SPR-11730
(cherry picked from commit 82336c3)
2014-05-18 20:27:44 +02:00
Juergen Hoeller 7d78c65187 Properly evaluate @Conditional in case of multiple imports for same config class (fixing regression in Spring Boot)
Issue: SPR-11788
2014-05-18 02:00:11 +02:00
Rossen Stoyanchev 748167bfa3 Improve StringUtils.cleanPath
Issue: SPR-11793
2014-05-16 11:27:55 -04:00
Juergen Hoeller 3e70013b55 JmsResourceHolder checks for nested DataSource transactions as well (for Oracle AQ compatibility)
Issue: SPR-11791
(cherry picked from commit 5faacd5)
2014-05-16 15:36:16 +02:00
Juergen Hoeller ea9ad4ee9b Properly evaluate @Conditional in case of multiple imports for same config class
Issue: SPR-11788
(cherry picked from commit 52f44b3)
2014-05-16 15:35:56 +02:00
Rossen Stoyanchev 482eff81e1 Await TcpClient shutdown in STOMP relay 2014-05-15 21:46:44 -04:00
Juergen Hoeller c706cbbed4 Latest possible dependency updates for spring-orm
(cherry picked from commit 0d22719)
2014-05-15 17:35:32 +02:00
Juergen Hoeller 7bc38acbf6 Polishing 2014-05-15 15:23:11 +02:00
Juergen Hoeller 297e4eec36 Keep unused MemorySafeUndeclaredThrowableStrategy around in deprecated form for the 4.0.x line
Issue: SPR-11782
2014-05-15 15:05:00 +02:00
Juergen Hoeller 235b65d103 Polishing
(cherry picked from commit b0f0d2f)
2014-05-15 14:43:02 +02:00
Juergen Hoeller 8a28645dd6 ResultSetWrappingSqlRowSet preserves first matching column per name (as defined in ResultSet's javadoc)
Issue: SPR-11786
(cherry picked from commit 0728e32)
2014-05-15 14:40:43 +02:00
Juergen Hoeller f94ded830d Consistent use of ClassUtils.forName instead of class.getClassLoader().loadClass
Issue: SPR-11780
(cherry picked from commit 551950c)
2014-05-15 14:40:15 +02:00
Juergen Hoeller ad8f17b6af Test compatibility with JasperReports 5.5.2 2014-05-12 23:35:17 +02:00
Juergen Hoeller 261520cea0 Undertow-related polishing
(cherry picked from commit 2750ab6)
2014-05-12 23:32:12 +02:00
Juergen Hoeller 00aa40b8da Consistent EhCache version between 4.1 and 4.0.5 2014-05-12 23:27:16 +02:00
Juergen Hoeller ac9b64e528 Consistent declaration of JSP, JSTL and JavaMail API dependencies; updated several dependencies to latest maintenance releases (Tiles 3.0.4, Jetty 9.1.5) 2014-05-12 21:50:13 +02:00
Juergen Hoeller 1e7fade89e JasperReports-related polishing
(cherry picked from commit 782d10c)
2014-05-12 20:50:24 +02:00
Stephane Nicoll 3c58be05a5 Fix typo
Issue: SPR-11571
(cherry picked from commit 6551aa6)
2014-05-08 16:38:08 +02:00
Juergen Hoeller ef290ff95c Additional tests for configuration class importing via ASM
Issue: SPR-11647
(cherry picked from commit 8c9116f)
2014-05-08 16:31:07 +02:00
Juergen Hoeller ae45794451 IdToEntityConverter defensively handles access to getDeclaredMethods
Issue: SPR-11758
(cherry picked from commit 381ccde)
2014-05-08 16:30:45 +02:00
Juergen Hoeller fccec210b4 GenericTypeResolver returns null for entirely unresolvable type arguments only
Issue: SPR-11763
(cherry picked from commit bea34ea)
2014-05-08 16:28:53 +02:00
Sebastien Deleuze 96da77ef75 Avoid ConcurrentModificationException
Removal of cached destination is now moved outside the for loop
that removes subscriptions to avoid ConcurrentModificationException.

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

Issue: SPR-11755
2014-05-06 20:22:05 -04:00
Juergen Hoeller c9417d0610 Fixed invalid code tag in javadoc
(cherry picked from commit a139670)
2014-05-01 10:55:55 +02:00
Spring Buildmaster 085e52fc31 Next development version 2014-04-30 15:41:11 -07:00
Juergen Hoeller 8f175046c8 spring-orm-hibernate4 requires aopalliance dependency now (after propdeps upgrade) 2014-04-30 23:19:07 +02:00
Juergen Hoeller c1dbd9f150 Upgraded to propdeps plugin 0.0.6
(cherry picked from commit a842b93)
2014-04-30 22:58:56 +02:00
Juergen Hoeller 42a35ef3be AnnotationMetadataReadingVisitor passes metaAnnotationMap into getMergedAnnotationAttributes algorithm, for finding out about applicable overrides
Issue: SPR-11649
(cherry picked from commit 842a8a8)
2014-04-30 22:57:09 +02:00
Rossen Stoyanchev 21fd68150a Log ignored CONNECT messages at error level
Issue: SPR-11673
2014-04-30 11:12:32 -04:00
Rossen Stoyanchev d20259be1d Log STOMP ERROR frames at error level
Issue: SPR-11673
2014-04-30 11:01:18 -04:00
Rossen Stoyanchev 6ca921021c Update log level
Issue: SPR-11673
2014-04-30 10:28:27 -04:00
Rossen Stoyanchev eab9d24c31 Fix Javadoc typo 2014-04-30 10:07:51 -04:00
Juergen Hoeller 15749c2a9d Upgraded to Gradle 1.12 and updated several dependencies to latest maintenance releases
(cherry picked from commit e84f61b)
2014-04-30 14:32:12 +02:00
Juergen Hoeller 93b0f0b126 JRubyScriptUtils defensively handles null Nodes in findClassNode (fails against JRuby 1.7.12 otherwise)
Issue: SPR-11747
(cherry picked from commit adb616b)
2014-04-30 14:32:07 +02:00
Juergen Hoeller 27b2ed1614 Spring Framework build uses Reactor 1.1 GA now, getting rid of last remaining pre-GA dependency 2014-04-30 11:41:57 +02:00
Rossen Stoyanchev 790d62bcb1 Simplify and improve STOMP broker relay int tests 2014-04-29 23:01:40 -04:00
Rossen Stoyanchev 563476f57c Split destination cache into access vs update
Issue: SPR-11657
2014-04-29 22:16:49 -04:00
Rossen Stoyanchev 79de45b805 Add cache limit to DefaultSessionRegistry and polish
Issue: SPR-11657
2014-04-29 18:17:40 -04:00
Sebastien Deleuze 63a868dafb Fix DefaultSubscriptionRegistry when using pattern destinations
DestinationCache is now used for both plain and pattern
destinations. It stores internally the subscriptions map for each
cached destination. Subscriptions are initially created when there
is no cache for the requested destination, and are updated when
subscriptions change.

Issue: SPR-11657
2014-04-29 18:17:30 -04:00
Juergen Hoeller 1afdd9bd75 Polishing
(cherry picked from commit 02aca9c)
2014-04-30 00:10:58 +02:00
Juergen Hoeller d0c839f0eb Polishing 2014-04-29 16:21:06 +02:00
Juergen Hoeller 744727b8aa AbstractAdvisingBeanPostProcessor uses target class check for existing proxy but checks against actual exposed object otherwise (catching introductions)
Issue: SPR-11725
(cherry picked from commit a0658c5)
2014-04-29 16:01:44 +02:00
Rossen Stoyanchev 9fbb7394e2 Polish STOMP documentation 2014-04-28 22:35:51 -04:00
Sebastien Deleuze 80cb0c4e5b Include more guidance about DispatcherServlet context
Also update <mvc:annotation-driven /> comment in XSD.

Issue: SPR-11703
2014-04-28 22:10:52 -04:00
Rossen Stoyanchev 5c4804f4a4 Document STOMP-related ApplicationContext events
Issue: SPR-11672
2014-04-28 21:59:09 -04:00
Juergen Hoeller a679f3e7a3 Polishing
(cherry picked from commit be5f2a8)
2014-04-28 23:52:23 +02:00
Juergen Hoeller bd5b3749c5 Revised @PropertySource parsing for consistent PropertySource naming, avoiding accidental overriding by name
Issue: SPR-11637
(cherry picked from commit ab24dda)
2014-04-28 23:47:33 +02:00
Juergen Hoeller 89398b0811 AspectJExpressionPointcut defensively handles fallback expression parsing
Issue: SPR-9335
(cherry picked from commit ce4912b)
2014-04-28 23:47:25 +02:00
Juergen Hoeller a87c9b8c5c Test for base package specified as config location
Issue: SPR-11647
(cherry picked from commit 8e6e6c2)
2014-04-28 23:47:14 +02:00
Juergen Hoeller d0f9853769 Test for overriding singleton @Bean method with scoped proxy in configuration subclass
Issue: SPR-10744
(cherry picked from commit 299776f)
2014-04-28 23:46:51 +02:00
Juergen Hoeller bed739ce92 GenericBeanDefinition's toString() takes parent definition into account
Issue: SPR-9671
(cherry picked from commit 0ef29b2)
2014-04-28 23:46:40 +02:00
Rossen Stoyanchev cb712afa97 Improve handling of disconnects in STOMP broker relay
This is a backport of:
https://github.com/spring-projects/spring-framework/commit/990f5bb720ac172fa659a59339df06065d31c0bf

Issue: SPR-11655
2014-04-28 10:08:00 -04:00
Juergen Hoeller c97c246940 Polishing 2014-04-28 00:29:04 +02:00
Juergen Hoeller 4196e6c96f AbstractMarshaller avoids SAXSource workaround when processExternalEntities=true
Issue: SPR-11737
(cherry picked from commit 93d13cf)
2014-04-28 00:27:04 +02:00
Juergen Hoeller 59cef3ce81 General defensiveness about the bootstrap ClassLoader (i.e. null ClassLoader)
Issue: SPR-11721
2014-04-28 00:26:18 +02:00
Juergen Hoeller c8ea80d7ed Configuration class processing explicitly skips java.* classes since we'll never find @Bean annotations there anyway
Issue: SPR-11718
(cherry picked from commit 470e9c8)
2014-04-28 00:17:19 +02:00
Juergen Hoeller 656fc52ff9 @Bean processing explicitly ignores bridge methods (for method overrides with return type narrowing on JDK 8)
Issue: SPR-11718
(cherry picked from commit dc93406)
2014-04-28 00:16:38 +02:00
Rossen Stoyanchev a4d6f8473e Upgrade to reactor 1.1 RC1 2014-04-25 12:32:36 -04:00
Rossen Stoyanchev 443fb8e4ee Fix issue with subscribe destination
The original fix for SPR-11423:
https://github.com/spring-projects/spring-framework/commit/32e5f57e647022d9ea82c03670936bf31f8467de

was insufficient when using an external broker since the original
destination header has to be in the "native headers" map (i.e. with
STOMP headers) in order to be included in messages broadcast by
the broker.
2014-04-25 12:17:08 -04:00
Juergen Hoeller 95b1cb7173 AspectJ 1.8 final used in build, plus TestNG preparations for optional -target 1.8 usage
Issue: SPR-11212
Issue: SPR-11699
2014-04-24 11:09:39 +02:00
Juergen Hoeller 51fb485181 Polishing
(cherry picked from commit 9353332)
2014-04-24 10:32:38 +02:00
Juergen Hoeller f16e6c4105 General defensiveness about the bootstrap ClassLoader (i.e. null ClassLoader)
Issue: SPR-11721
(cherry picked from commit c05ab3e)
2014-04-24 10:31:48 +02:00
Juergen Hoeller 4dbc0c296c Polishing
(cherry picked from commit ec7d80b)
2014-04-24 10:31:39 +02:00
Juergen Hoeller d71d38bfaf ServletServerHttpResponse specifically checks for Servlet 3.0's HttpServletResponse.getHeader(String) method
Issue: SPR-11712
(cherry picked from commit 21f9ca0)
2014-04-24 10:31:29 +02:00
Juergen Hoeller eb28259371 SpringProperties falls back to ClassLoader.getSystemResource when loaded in bootstrap ClassLoader
Issue: SPR-11721
(cherry picked from commit 9cb5f48)
2014-04-24 10:31:20 +02:00
Juergen Hoeller 7ae6a60f43 checkNotModified leniently handles IE-10-style If-Modified-Since values and silently proceeds if header value cannot be parsed at all
Issue: SPR-11727
(cherry picked from commit 794e859)
2014-04-24 10:30:49 +02:00
Juergen Hoeller d3c9a31e9a DispatcherServlet skips multipart resolution if error page exception attribute indicates MultipartException as root cause
Issue: SPR-11716
(cherry picked from commit 5b47816)
2014-04-24 10:30:12 +02:00
Juergen Hoeller 5aa126600f Final patches from ASM 5.0.2 release
Issue: SPR-11212
(cherry picked from commit fb66567)
2014-04-24 10:29:50 +02:00
Juergen Hoeller 2f15f72fcc ShallowEtagHeaderFilter writes body early on sendError/sendRedirect and interprets setContentLength/setBufferSize as a hint for capacity increase
Issue: SPR-11705
Issue: SPR-11717
(cherry picked from commit 3f392e3)
2014-04-24 10:28:42 +02:00
Juergen Hoeller d5aacdefd9 Polishing
(cherry picked from commit 700c3b2)
2014-04-24 10:27:43 +02:00
Juergen Hoeller e379e77b96 considerInterfaces should work without considerInherited as well
Issue: SPR-11719
(cherry picked from commit 5ab7076)
2014-04-24 10:27:15 +02:00
Juergen Hoeller 9c45755890 AnnotationTypeFilter prevents ASM-based loading of java.* interfaces as well
Issue: SPR-11719
(cherry picked from commit 945335d)
2014-04-24 10:26:42 +02:00
Juergen Hoeller 5962fc2590 EQ/NE do not fail in case of a Comparable mismatch and apply a specific CharSequence equality check
Issue: SPR-11708
(cherry picked from commit 66bae99)
2014-04-24 10:26:08 +02:00
Rossen Stoyanchev 1b59b4eb26 Add Reactor 11 support
This change adds support for Reactor 1.1 in spring-messaging in
addition to Reactor 1.0.1 -- whichever is present on the classpath is
used.

Note also the module name change:
reactor-tcp:1.0.1 -> reactor-net:1.1.0

Issue: SPR-11636
2014-04-23 23:35:49 -04:00
HejiaHo f6b89c2ca9 Fix ConcurrentReferenceHashMap max constraints
Update calculateShift to respect the maximum upper bound as well as the
minimum value.

Issue: SPR-11720

(cherry picked from commit 16410cad)
2014-04-22 15:03:23 +01:00
Sam Brannen 82c32db5a1 Don't mutate annotation metadata when merging attrs
Prior to this commit, invoking the getMergedAnnotationAttributes()
method in AnnotationReadingVisitorUtils resulted in mutation of the
internal state of the ASM-based annotation metadata supplied to the
method.

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

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

Issue: SPR-11710
(cherry picked from commit e1720d89fc)
2014-04-21 12:50:27 -04:00
Juergen Hoeller 52f580149f Polishing 2014-04-17 22:42:33 +02:00
Juergen Hoeller 271fe4f554 Polishing
(cherry picked from commit 48c977a)
2014-04-17 22:14:31 +02:00
Juergen Hoeller 02e8198283 MessageConversionException offers constructor without cause argument now, plus related polishing
Issue: SPR-11653
(cherry picked from commit 2888775)
2014-04-17 22:14:01 +02:00
Juergen Hoeller 090ab4bb6d XStreamMarshaller supports custom NameCoder strategy
Issue: SPR-11702
(cherry picked from commit f5cce14)
2014-04-17 21:39:34 +02:00
Juergen Hoeller 18ef1d4f76 WebSocket intro correctly points to annotations section
Issue: SPR-11667
(cherry picked from commit 53162e7)
2014-04-17 21:39:02 +02:00
Juergen Hoeller d9cb44527c Backported tests for package-visible methods with CGLIB proxies
Issue: SPR-11618
(cherry picked from commit 90309ab)
2014-04-17 21:38:01 +02:00
Juergen Hoeller 8e44521ec4 Latest ASM patches (fix for bug 317151 from ASM HEAD; ASM's 1.8 bytecode processing needs to be lenient with CGLIB 3.1)
Issue: SPR-11212
(cherry picked from commit 637ad20)
2014-04-17 21:34:57 +02:00
Juergen Hoeller d28c6d8bdb Consistent documentation for both 'close' and 'shutdown' as inferred destroy methods
Issue: SPR-11680
(cherry picked from commit 14f9dbf)
2014-04-17 21:32:09 +02:00
Juergen Hoeller c9432ce499 JDBC 3.0 related javadoc overhaul
Issue: SPR-11600
(cherry picked from commit 3fef358)
2014-04-17 21:31:45 +02:00
Stephane Nicoll 2d8e0c8f87 Fix expression cache
Prior to this commit, only the java.lang.reflect.Method was used to
identify an annotated method. As a result, if different annotations
were placed on different methods (method overriding, interface
implementation) only the first one (cached) was used.

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

Issue: SPR-11692
(cherry picked from commit df34bab)
2014-04-15 09:29:56 +02:00
Sam Brannen 03ae8eeb95 Make EOF_STATEMENT_SEPARATOR XML friendly
Changed the value of ScriptUtils.EOF_STATEMENT_SEPARATOR from
"<<< END OF SCRIPT >>>" to "^^^ END OF SCRIPT ^^^" so that the angle
brackets do not have to be escaped in XML configuration files.

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

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

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

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

Issue: SPR-11687
(cherry picked from commit cc0ae3a881)
2014-04-11 18:25:06 +02:00
Sam Brannen d5ce30f360 Deprecate custom/default CtxLdr class name in TCF
Once the new bootstrap strategy for the TestContext framework (TCF) is
introduced in Spring Framework 4.1, a TestContextBootstrapper will
assume full responsibility for determining what ContextLoader to use as
the default. Consequently, the previous support for supplying the class
name for a custom, default ContextLoader will no longer be applicable.

This commit therefore officially deprecates support for custom, default
ContextLoader class names via the following mechanisms:

 - The TestContextManager(Class, String) constructor

 - The getDefaultContextLoaderClassName(Class) method in
   SpringJUnit4ClassRunner

Issue: SPR-11682
2014-04-10 17:54:14 +02:00
Sam Brannen 428aee5b72 Deprecate getDefTELClasses in TestContextMgr
Since a dedicated bootstrap strategy will be introduced for the
TestContext framework in Spring Framework 4.1, this commit officially
deprecates TestContextManager.getDefaultTestExecutionListenerClasses()
as a forewarning to developers or frameworks that may be using this
obscure feature.

Issue: SPR-11679
2014-04-09 22:20:43 +02:00
Juergen Hoeller f7a17added AbstractMarshaller uses general exception message in extracted buildDocument() method
Issue: SPR-11635
2014-04-09 20:54:41 +02:00
Juergen Hoeller 196f629a20 JBoss "vfszip" resources need to be treated as jar URLs
Issue: SPR-11676
(cherry picked from commit be6b54f)
2014-04-09 20:51:23 +02:00
Juergen Hoeller 7135e729e2 Dropped JCache RC repo (now unused); minor dependency upgrades
Issue: SPR-11640
(cherry picked from commit 9e1631d)
2014-04-09 19:00:44 +02:00
Juergen Hoeller 21874627d2 Polishing
Issue: SPR-11637
(cherry picked from commit 0041e24)
2014-04-09 19:00:23 +02:00
Juergen Hoeller 3ab88b857b AbstractMarshaller pre-implements getDefaultEncoding() method
Issue: SPR-11635
(cherry picked from commit d4b0ae9)
2014-04-09 19:00:08 +02:00
Juergen Hoeller 294545d07a XStreamMarshaller lazily creates fallback XppDriver
Issue: SPR-11635
(cherry picked from commit 8f28a93)
2014-04-09 18:59:47 +02:00
Juergen Hoeller 38cd79047a SelectTag correctly detects multiple="true" again
Issue: SPR-11678
(cherry picked from commit 8d8766d)
2014-04-09 18:59:37 +02:00
Stephane Nicoll 8a779c32d5 Update to JCache 1.0.0 final
Issue: SPR-11640
(cherry picked from commit 0bdece7)
2014-04-08 15:20:57 +02:00
Sam Brannen 7ba31dee45 Use unconstrained quotes in reference manual
Prior to this commit, there were numerous places in the reference
manual, where we see output similar to the following:

    BeanDefinition`s with the `ApplicationContext

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

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

Issue: SPR-11650
(cherry picked from commit 0eba1f818f)
2014-04-04 22:05:43 +02:00
Brian Clozel 1c45d7573c Fix @SubscribeMapping MESSAGE response destination
Prior to this commit, @SubscribeMapping mapped methods (backed with
@SendTo* annotations, or not) would send MESSAGEs with the wrong
destination. Instead of using the original SUBSCRIBE destination, it
would use the lookup path computed from the configured prefixes in the
application.

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

Issue: SPR-11648
2014-04-04 12:45:19 +02:00
Juergen Hoeller f014bbb5cf Revised ByteArrayOutputStream handling in MarshallingView and co
Issue: SPR-11646
(cherry picked from commit 8006696)
2014-04-02 21:29:37 +02:00
Juergen Hoeller d1030b798d ResourceHttpRequestHandler explicitly closes a Resource's InputStream
Issue: SPR-11644
(cherry picked from commit 3a96f16)
2014-04-02 21:29:05 +02:00
Juergen Hoeller d628025f13 Polishing
Issue: SPR-11262
2014-04-02 21:28:42 +02:00
Rossen Stoyanchev 30ea1a3475 Fix issue with default executor for message channels
This change ensures correct default settings for message channel's
created through the websocket message-broker XML namespace even
when the channels are customized for other reasons -- e.g. to add
a channel interceptor.

Issue: SPR-11623
2014-04-01 14:16:10 -04:00
Rossen Stoyanchev 7b014eaa55 Improve access to WebSocketSession fields
Ensure the Standard- and the JettyWebSocketSession can return the
Principal and accepted WebSocket sub-protocol even after the
session is closed.

Issue: SPR-11621
2014-04-01 14:16:10 -04:00
Rossen Stoyanchev e21c47d4ce Optimize performance in StompEncoder/Decoder escaping
Issue: SPR-11643
2014-04-01 14:16:10 -04:00
Rossen Stoyanchev 632ae6a4df Improve shutdown responsiveness of SubProtocolWSH
Proactively notify all active WebSocket sessions when a shutdown is
progress. Sessions then can ignore further attempts to send messages
and also stop stop trying to flush messages right away.
2014-04-01 14:16:10 -04:00
Sam Brannen 989f6194b6 Support classes AND locations in @ContextConfiguration
Prior to this commit, the Spring TestContext Framework did not support
the declaration of both 'locations' and 'classes' within
@ContextConfiguration at the same time.

This commit addresses this in the following manner:

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

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

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

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

 - The Javadoc for @ContextConfiguration has been updated accordingly.

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

Issue: SPR-11634
(cherry picked from commit 1f017c4acb)
2014-04-01 19:54:04 +02:00
Sam Brannen 17bf5b991f Update Javadoc for supported JBoss VFS version
(cherry picked from commit 34eb4dba3a)
2014-03-30 16:02:27 +02:00
Sam Brannen cd9d7cfe4f Reinject Servlet mocks between TestNG test methods
Prior to this commit, if multiple test methods were executed in a
subclass of AbstractTestNGSpringContextTests annotated with
@WebAppConfiguration, then injected Servlet API mocks would only
reference the mocks created for the first test method. Subsequent test
methods could therefore never reference the current mocks, and there
was a discrepancy between the state of the injected mocks and the mock
set in the RequestContextHolder.

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

Issue: SPR-11626
(cherry picked from commit c38600762d)
2014-03-29 21:46:58 +01:00
10802 changed files with 463279 additions and 926608 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
+9 -22
View File
@@ -1,42 +1,31 @@
# 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
activemq-data/
/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
@@ -47,6 +36,4 @@ spring-*/src/main/java/META-INF/MANIFEST.MF
out
test-output
atlassian-ide-plugin.xml
# VS Code
.vscode/
.gradletasknamecache
-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/]
+51
View File
@@ -0,0 +1,51 @@
= How to contribute to the reference
The Spring Framework reference now uses http://asciidoctor.org/[asciidoctor]. This
document describes how to contribute documentation updates.
== Building with Gradle
You can build the documentation using gradle using the `reference` task. For example, from
the project root execute the following command:
./gradlew reference
the output will be available at `spring-framework/build/reference/htmlsingle/index.html`
== Live editing
One of the nice features about using asciidoctor is the support for live editing.
You will find a Guardfile already present at `spring-framework/src/asciidoc/Guardfile`.
Make sure first to follow the setup instructions within the
http://asciidoctor.org/docs/editing-asciidoc-with-live-preview/[Editing AsciiDoc with Live Preview]
document. Once you have done that, there are additional gems to install to make it work
(assuming that you are using http://livereload.com/[LiveReload]):
gem install guard-rspec guard-livereload
When running `guard start` within the `src/asciidoc/` folder, any changes to the
`src/asciidoc/index.adoc` file will automatically be written at
`src/asciidoc/build/index.html`.
== Troubleshooting
* If you are using LiveReload, make sure to select _Allow access to file URLs_ in the
LiveEdit plugin options of your browser.
* The icon used to enable _LiveReload_ can be a bit confusing. The dot is empty when it is
disabled and full when the plugin is active. Make sure to enable it on the tab
displaying the `index.html` file.
* Ensure you are _not_ running guard start at all as two instances could not run at the
same time. To exit a current session in a clean way, just type e in the shell.
== Documentation notes
Some notes on documentation
* Documentation is wrapped at 90 chars, ensure that you manually wrap your edits
* Tabs are used for indentation, do not use spaces
* Follow the existing style when inserting `source` blocks
* http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/[Asciidoctor Quick Reference]
* http://asciidoctor.org/docs/user-manual/[Asciidoctor Manual]
* http://asciidoctor.org/docs/asciidoc-writers-guide/[Asciidoctor Writers Guide]
+274 -102
View File
@@ -1,135 +1,307 @@
# 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
## Take Your First Steps
* [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)
### Understand the basics
### 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 you
do not find something similar, please create a new JIRA issue before submitting
a pull request unless the change is truly trivial -- for example: 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.
### Sign the Contributor License Agreement
Once you're ready, create an issue on [GitHub](https://github.com/spring-projects/spring-framework/issues).
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!
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.
Note that emailing/postal mailing a signed copy is _not_ necessary. Submission
of the web form is all that is required.
#### Issue Lifecycle
Once you've completed the web form, simply add the following in a comment on
your pull request:
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.
I have signed and agree to the terms of the SpringSource Individual
Contributor License Agreement.
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.
You do not need to include your token/id. Please add the above statement to all
future pull requests as well, simply so that the Spring Framework team knows
immediately that this process is complete.
#### Submit a Pull Request
## Create a Branch
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.
### Branch from `master`
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.
Master currently represents work toward Spring Framework 4.0. Please submit
all pull requests there, even bug fixes and minor improvements. Backports to
`3.2.x` will be considered on a case-by-case basis.
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.
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.
### Use short branch names
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.
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.
1. If there is a prior issue, reference the GitHub issue number in the description of the
pull request.
## Use Spring Framework Code Style
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.
The complete [Spring Framework Code Style][] reference is available on the wiki, but
here's a quick summary:
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.
### Mind the whitespace
#### Participate in Reviews
Please carefully follow the whitespace and formatting conventions already
present in the framework.
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.
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
### Build from Source
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.
### Add Apache license header to all new classes
### Source Code Style
```java
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.
package ...;
```
### Reference Docs
### Update Apache license header in modified files as necessary
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.
Always check the date range in the license header. For example, if you've
modified a file in 2014 whose header still reads:
When making changes locally, execute `./gradlew :framework-docs:asciidoctor` and then browse the result under
`framework-docs/build/docs/ref-docs/html5/index.html`.
```java
* Copyright 2002-2011 the original author or authors.
```
Asciidoctor also supports live editing. For more details see
[AsciiDoc Tooling](https://docs.asciidoctor.org/asciidoctor/latest/tooling/).
Then be sure to update it to 2014 accordingly:
```java
* Copyright 2002-2014 the original author or authors.
```
### Use @since tags for newly-added public API types and methods
For example:
```java
/**
* ...
*
* @author First Last
* @since 4.0
* @see ...
*/
```
## Prepare Your Commit
### Submit JUnit test cases for all behavior changes
Search the codebase to find related tests and add additional `@Test` methods
as appropriate. It is also acceptable to submit test cases on a per JIRA issue
basis, for example:
```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 --autosquash`, `git add --patch`, and other tools
to "squash" multiple commits into a single atomic commit. In addition to the man
pages for git, there are many resources online to help you understand how these
tools work. The [Rewriting History section of Pro Git][] provides a good overview.
### 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 via the account admin area in GitHub (useful for
fork-and-edit cases); _globally_ on your machine 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 with a capitalized verb, e.g. "Add, Prune, Fix,
Introduce, Avoid, etc."
1. Do not end the subject line with a period
1. Restrict 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/spring-projects/spring-framework/commit/08e2669b84ec0faa2f7904441fe39ac70b65b078
- https://github.com/spring-projects/spring-framework/commit/1d9d3e6ff79ce9f0eca03b02cd1df705925575da
- https://github.com/spring-projects/spring-framework/commit/8e0b1c3a5f957af3049cfa0438317177e16d6de6
- https://github.com/spring-projects/spring-framework/commit/b787a68f2050df179f7036b209aa741230a02477
## Run the Final Checklist
### 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. In other words, 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.spring.io/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
[Spring Framework Code Style]: https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Code-Style
[Rewriting History section of Pro Git]: http://git-scm.com/book/en/Git-Tools-Rewriting-History
[Commit Guidelines section of Pro Git]: http://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines
[building from source]: https://github.com/spring-projects/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.
+85 -22
View File
@@ -1,38 +1,101 @@
# <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 [Spring 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.
## 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.
## Build from Source
### Prerequisites
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.
[Git][] and [OpenJDK 8 early access build 100 or later][JDK8 build]
## Continuous Integration Builds
Be sure that your `JAVA_HOME` environment variable points to the `jdk1.8.0` folder
extracted from the JDK download.
Information regarding CI builds can be found in the [Spring Framework Concourse pipeline](ci/README.adoc) documentation.
### Check out sources
`git clone git@github.com:spring-projects/spring-framework.git`
## Stay in Touch
### Import sources into your IDE
Run `./import-into-eclipse.sh` or read `import-into-idea.md` as appropriate.
> **Note:** Per the prerequisites above, ensure that you have JDK 8 configured properly in your IDE.
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).
### Install all spring-\* jars into your local Maven cache
`./gradlew install`
### Compile and test; build all jars, distribution zips, and docs
`./gradlew build`
... and discover more commands with `./gradlew tasks`. See also the [Gradle
build and release FAQ][].
## Contributing
[Pull requests][] are welcome; see the [contributor guidelines][] for details.
## Staying in Touch
Follow [@SpringCentral][] as well as [@SpringFramework][] and its [team members][]
on Twitter. In-depth articles can be found at [The Spring 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/spring-projects/spring-integration
[Spring Batch]: https://github.com/spring-projects/spring-batch
[family of projects]: http://spring.io/projects
[Spring organization]: https://github.com/spring-projects
[downloading Spring artifacts]: https://github.com/spring-projects/spring-framework/wiki/Downloading-Spring-artifacts
[building a distribution with dependencies]: https://github.com/spring-projects/spring-framework/wiki/Building-a-distribution-with-dependencies
[Javadoc]: http://docs.spring.io/spring-framework/docs/current/javadoc-api/
[reference docs]: http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/
[Spring forums]: http://forum.spring.io/
[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://spring.io/services
[Spring Framework JIRA]: https://jira.spring.io/browse/SPR
[the lifecycle of an issue]: https://github.com/spring-projects/spring-framework/wiki/The-Lifecycle-of-an-Issue
[spring-framework-issues]: https://github.com/spring-projects/spring-framework-issues#readme
[readme]: https://github.com/spring-projects/spring-framework-issues#readme
[Gradle]: http://gradle.org
[`./gradlew`]: http://vimeo.com/34436402
[Git]: http://help.github.com/set-up-git-redirect
[JDK8 build]: http://jdk8.java.net/download.html
[Gradle build and release FAQ]: https://github.com/spring-projects/spring-framework/wiki/Gradle-build-and-release-FAQ
[Pull requests]: http://help.github.com/send-pull-requests
[contributor guidelines]: https://github.com/spring-projects/spring-framework/blob/master/CONTRIBUTING.md
[@SpringFramework]: https://twitter.com/springframework
[@SpringCentral]: https://twitter.com/springcentral
[team members]: https://twitter.com/springframework/team/members
[The Spring Blog]: http://spring.io/blog/
[news feed]: http://spring.io/blog/category/news
[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.
+1193 -139
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,157 @@
/*
* 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.plugins.ide.eclipse.EclipsePlugin
import org.gradle.plugins.ide.idea.IdeaPlugin
import org.gradle.api.invocation.*
/**
* Gradle plugin that allows projects to merged together. Primarily developed to
* allow Spring to support multiple incompatible versions of third-party
* dependencies (for example Hibernate v3 and v4).
* <p>
* The 'merge' extension should be used to define how projects are merged, for example:
* <pre class="code">
* 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,55 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.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]

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