Compare commits

...

197 Commits

Author SHA1 Message Date
Spring Buildmaster 5cde41b8b5 Release version 5.0.3.RELEASE 2018-01-23 09:42:20 +00:00
Rossen Stoyanchev 4b861aeae6 Reference docs: update async request content
Issue: SPR-16203
2018-01-22 22:42:59 -05:00
Rossen Stoyanchev aee8a9c97b Upgrade to Bismuth-SR5
Issue: SPR-16385
2018-01-22 19:57:38 -05:00
Juergen Hoeller 91e6274b7d Upgrade to Apache HttpClient 4.5.5 and Tomcat 8.5.27 2018-01-22 22:11:00 +01:00
Juergen Hoeller 61579ffe7b Polishing 2018-01-22 21:34:44 +01:00
Arjen Poutsma b47491695e Support array of scalar values in Jackson2Tokenizer
Issue: SPR-16407
2018-01-22 17:08:08 +01:00
Juergen Hoeller 9d0e62ef68 Javadoc format and related polishing 2018-01-22 11:43:21 +01:00
Juergen Hoeller 3dd6c19c6d Upgrade to Netty 4.1.20 2018-01-22 11:43:06 +01:00
Arjen Poutsma 0befc60c8f Renamed DataBufferUtils/DataBufferFactory.compose to join
Issue: SPR-16365
2018-01-22 10:51:07 +01:00
sdeleuze 3f3141cdda Add additional test for SPR-16210
Issue: SPR-16210
2018-01-22 10:29:21 +01:00
Arjen Poutsma 646fcc5c2f Support Custom Headers for Multipart Async Data
This commit makes sure there is no custom Content-Disposition header
before setting one automatically.

This commit also adds a headers(Consumer<HttpHeaders>) method, so that
one can user the nicer methods of HttpHeaders, as opposed to basic
strings.

Issue: SPR-16376
2018-01-20 15:18:20 +01:00
Juergen Hoeller b2ce98e881 Upgrade to CGLIB 3.2.6
Issue: SPR-16267
2018-01-20 14:29:56 +01:00
Juergen Hoeller 395c9bd723 Consistently call HttpURLConnection.setFixedLengthStreamingMode(long)
Issue: SPR-16404
2018-01-20 14:29:40 +01:00
andrm 142f1ab42f Removed unnecessary cast to int
Since Java7 HttpURLConnection offers setFixedLengthStreamingMode method with long parameter which should be prefered over version with int argument, therefore casting ContentLength to int is no longer needed. Moreover it makes impossible to stream payload larger than Integer.MAX_VALUE
2018-01-20 14:11:15 +01:00
Stephane Nicoll 0ca4cd1cf8 Merge pull request #1647 from izeye:polish-20180120
* pr/1647:
  Polish InterceptorRegistration
2018-01-20 10:15:53 +01:00
Johnny Lim 36d42bcda4 Polish InterceptorRegistration
Closes gh-1647
2018-01-20 10:15:12 +01:00
Rossen Stoyanchev 283811b16b Ensure filename is written
Now that SPR-16307 makes it possible to write a part with a Publisher
we need to ensure we get the filename correctly when writing with
Publisher<Resource>.

Issue: SPR-16376
2018-01-19 22:12:53 -05:00
Rossen Stoyanchev aea6bb6357 Update STOMP docs on using dot as separator
Issue: SPR-16275
2018-01-19 21:51:31 -05:00
Rossen Stoyanchev 0fb31c5e36 Refine "." separator support for STOMP messaging
After this commit DefaultUserDestinationResolves no longer looks at
whether AntPathMatcher is configured with "." as separator and rather
expects to be explicitly told whether to keep the leading slash in
translated destinations which actually depends on what the message
broker supports (e.g. RabbitMQ "/", Artemis ".") or how it is
configured (simple broker could be either way).

There is also a minor improvement in SimpMessagingTemplate to ensure
user destinations are correctly formed based on what the
DefaultUserDestinationResolver expects. When using "." as separtor it
allows sending messages to "queue.q1" rather than "/queue.q1".

Issue: SPR-16275
2018-01-19 21:02:43 -05:00
Rossen Stoyanchev 17f9b61249 Polish MessageBrokerConfigurationTests 2018-01-19 21:02:43 -05:00
Rossen Stoyanchev afd248da8a MultipartHttpMessageWriter consumes source once only
The previous fix #09f1f7 did not actually address the issue but only
moved it further down, so instead of the subscribe(), it was consuming
it inside the MultipartHttpMessageWriter#write method which returned
this.body.then(), and then again for the actual request body writing.

In this commit MultipartHttpMessageWriter#write returns Mono.empty()
since we don't actually want to write the part content from there, but
only want to access it as soon as it is availabele, for writing to
the request body.

Issue: SPR-16402
2018-01-19 20:55:22 -05:00
Juergen Hoeller 572c668726 Polishing 2018-01-19 21:30:37 +01:00
Arjen Poutsma 5520e730f1 DataBufferUtils.read should not take input stream/channel as parameter
Fixed by creating `Callable`-based variants, as explained in the JIRA
issue.

Issue: SPR-16403
2018-01-19 17:38:14 +01:00
Rossen Stoyanchev 09f1f727a7 Remove explicit subscribe in MultipartHttpMessageWriter
Issue: SPR-16402
2018-01-19 10:41:08 -05:00
Juergen Hoeller 40127bd9ad Polishing 2018-01-19 12:30:10 +01:00
Juergen Hoeller 4c7414833b ResolvableType returns clone for cached state with original local source
Issue: SPR-16210
2018-01-19 12:30:03 +01:00
Rossen Stoyanchev 357fb48d13 Update docs on stomp.js library
Issue: SPR-15624
2018-01-18 13:09:40 -05:00
Rossen Stoyanchev 513461d4f1 Update STOMP overview
Issue: SPR-15624
2018-01-18 13:04:43 -05:00
Arjen Poutsma c53c8bfc5a Set 304 status on ServerResponse when ETag/LastModified match
This commit checks the Etag/LastModified headers on the incoming
request, and sets a 304 Not Modified status with no body when they
match, by delegating to ServerWebExchange.checkNotModified.

Issue: SPR-16348
2018-01-18 11:34:38 +01:00
Arjen Poutsma c211e3998b Polishing 2018-01-18 11:34:38 +01:00
Arjen Poutsma 61d330f805 Javadoc 2018-01-18 11:34:38 +01:00
Juergen Hoeller ea5f8f5a7c Consistently handle NoClassDefFoundError in BeanUtils and related places
Issue: SPR-16369
2018-01-17 20:24:57 +01:00
Rossen Stoyanchev 0c289283ff Separate from expectations from response creation
This commit separates the creation of a response so that it is executed
after the synchronized block inside which requests need to be matched
and validated (for order and count).

This allows a ResponseCreator to be slow or block if it has to.

Issue: SPR-16319
2018-01-17 13:01:40 -05:00
Rossen Stoyanchev 7ab4d0ca08 Polish MockRestServiceServer code 2018-01-17 13:01:40 -05:00
Juergen Hoeller 214576673a Proper NoClassDefFoundError check against BeanUtils.instantiateClass
Issue: SPR-16369
2018-01-17 18:29:05 +01:00
Juergen Hoeller 32b4279929 Upgrade to Kotlin 1.2.20 (and Undertow 1.4.22) 2018-01-17 18:28:26 +01:00
Stephane Nicoll 817254f2eb Merge pull request #1646 from JanStureNielsen:patch-2
* pr/1646:
  Fix doc typo
2018-01-17 18:07:04 +01:00
Jan Nielsen fed81c6f83 Fix doc typo
Closes gh-1646
2018-01-17 18:06:17 +01:00
Stephane Nicoll dfe4e01f51 Merge pull request #1645 from JanStureNielsen:patch-1
* pr/1645:
  Fix doc typo
2018-01-17 18:06:04 +01:00
Jan Nielsen df9761cb3f Fix doc typo
Closes gh-1645
2018-01-17 18:05:39 +01:00
Rossen Stoyanchev 12baafd4f0 WebFlux docs: security + consistent order for WebFlux config 2018-01-16 23:23:26 -05:00
Rossen Stoyanchev 1148b61dfa WebMVC Docs: remove old code-by-convention content
We cover this more succinctly from other places, e.g. in Model Methods
by referring to the Javadoc of Contentions, or pointing to the
RequestToViewNameTranslator.
2018-01-16 23:23:09 -05:00
Rossen Stoyanchev bb7152d6e7 WebFlux docs: matrix variables
Issue: SPR-16040
2018-01-16 22:53:40 -05:00
Rossen Stoyanchev 61fc321554 WebFlux docs: Request|ResponseBody + Http|ResponseEntity
Issue: SPR-16040
2018-01-16 22:10:04 -05:00
Rossen Stoyanchev 4f2e54fc2d Document multipart data support for WebFlux
Issue: SPR-16040
2018-01-16 16:53:19 -05:00
Rossen Stoyanchev b97fa4a5ee Update docs on multipart requests + table of parameters
Issue: SPR-16040
2018-01-16 11:33:27 -05:00
Juergen Hoeller 65a167f7fd JmsListenerAnnotationBeanPostProcessor properly deals with nested proxies
Issue: SPR-16196
2018-01-16 14:39:31 +01:00
Juergen Hoeller 0270808b3c Upgrade to EclipseLink 2.7.1 2018-01-16 14:39:14 +01:00
Timo Meinen 823a16ce17 Log SQL parameters in EclipseLink when using showSql
As of EclipseLink 2.4.0 - Juno this is not sufficient to log
SQL parameter binding. Additionally,
eclipselink.logging.parameters must be enabled.

Issue: SPR-16383
2018-01-16 14:20:49 +01:00
Sam Brannen 7b4ec72378 Upgrade to JUnit Jupiter 5.0.3
Issue: SPR-16384
2018-01-16 13:17:32 +01:00
Rossen Stoyanchev 437c33ba42 WebFlux @ModelAttribute coverage in reference
Issue: SPR-16040
2018-01-15 17:47:27 -05:00
sdeleuze 43d3abdfd5 Fix SockJs CorsConfiguration for forbidden origins
After this commit, AbstractSockJsService uses the configured allowed
origins when generating the CorsConfiguration instead of "*".

As a consequence, forbidden origin requests still result in a 403
response but now with no CORS headers in order to improve consistency
between the status code and the headers.

Issue: SPR-16304
2018-01-15 18:07:24 +01:00
Stephane Nicoll 3ae776b1fa Fix BOM link
Issue: SPR-16317
2018-01-15 17:12:19 +01:00
Rossen Stoyanchev 61352fe85b Fix compile error 2018-01-15 10:28:12 -05:00
Rossen Stoyanchev 6375cc55a3 Merge pull request #1642 from EnvOut/master 2018-01-15 10:01:54 -05:00
Rossen Stoyanchev dd09c08cdf Replace use of AbstractWebSocketMessageBrokerConfigurer 2018-01-15 10:01:14 -05:00
Sergey d6591a6329 Default methods in WebSocketMessageBrokerConfigurer
+ deprecate AbstractWebSocketMessageBrokerConfigurer
2018-01-15 09:54:55 -05:00
Stephane Nicoll 42dfa40151 Make sure endpoint settings override default factory settings
Issue: SPR-16338
2018-01-15 15:42:50 +01:00
Stephane Nicoll 89b81b0080 Merge pull request #1636 from izeye:exception-message
* pr/1636:
  Fix an exception message in ScheduledAnnotationBeanPostProcessor
2018-01-15 15:19:16 +01:00
Johnny Lim d31767eab8 Fix an exception message in ScheduledAnnotationBeanPostProcessor
Closes gh-1636
2018-01-15 15:19:02 +01:00
Juergen Hoeller b160f93495 CacheProxyFactoryBean exposes all relevant CacheInterceptor callbacks
Issue: SPR-16295
2018-01-14 23:48:21 +01:00
Juergen Hoeller 44fd2d96cc Restore original separator set (but keep new isParameterSeparator impl)
Issue: SPR-16340
2018-01-14 17:57:32 +01:00
Juergen Hoeller 9623cdec48 Functional response builders allow for non-standard HTTP status codes
Issue: SPR-16366
2018-01-14 17:45:58 +01:00
Philippe Marschall 91e39d558f Use ArrayList instead of LinkedList for known size
Spring JDBC unlike other modules uses LinkedList instead of ArrayList
in several places. There is a large body of evidence suggesting that on
contemporary hardware ArrayList is both faster and has less overhead
than even in degenerate cases of empty lists [3] or unknown size.

There are two places in Spring JDBC where the size of the list is known
in advance and an ArrayList of the correct final size can be created

 [1] https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8011200
 [2] http://cliffc.org/blog/2017/11/05/modern-hardware-performance-cache-lines/
 [3] https://bugs.openjdk.java.net/browse/JDK-8011200

Issue: SPR-16378
2018-01-14 17:45:32 +01:00
Sam Brannen c88f11f958 Avoid deprecation warnings on JDK 9 in spring-test 2018-01-13 16:45:04 +01:00
Rossen Stoyanchev 1dbcd66091 More sections in WebFlux docs @Controller argument
Issue: SPR-16040
2018-01-12 16:48:06 -05:00
Juergen Hoeller 4c9ed0d87e Polishing 2018-01-12 18:24:00 +01:00
Juergen Hoeller 06e6386dc9 CollectionUtils.lastElement for common Set/List extraction
Issue: SPR-16374
2018-01-12 18:23:52 +01:00
Juergen Hoeller 13a8f90e08 ScheduledAnnotationBeanPostProcessor properly deals with nested proxies
Issue: SPR-16196
2018-01-12 16:48:17 +01:00
Juergen Hoeller a15975d94d Log non-loadable TestExecutionListener classes at debug level only
Issue: SPR-16369
2018-01-12 16:47:46 +01:00
Juergen Hoeller 93f645800b Test for non-equality after URI normalization
Issue: SPR-16364
2018-01-12 16:46:06 +01:00
Juergen Hoeller 347c2daa2c Test for multiple interfaces at BridgeMethodResolver level
Issue: SPR-16288
2018-01-12 16:45:38 +01:00
Arjen Poutsma e39bf87b07 Add reference docs for DataBufferUtils.compose
Issue: SPR-16365
2018-01-12 11:38:50 +01:00
Arjen Poutsma 67e7c784e8 Use DataBufferUtils.compose and remove writeAggregator
Use DataBufferUtils.compose instead of writeAggregator to combine
multiple data buffers into one, as the write aggregator would not work
when the initial data buffer did not have enough capacity to contain
all subsequent buffers.

Removed writeAggregator, as it is no longer needed.

Issue: SPR-16365
2018-01-12 10:36:09 +01:00
Arjen Poutsma 384a399fd2 Add DataBufferUtils.compose
Added a utility method that composes data buffers into a single buffer.
Depending on the `DataBuffer` implementation, the returned buffer may be
a single buffer containing all data of the provided buffers, or it may
be a true composite that contains references to the buffers.

Issue: SPR-16365
2018-01-12 10:36:09 +01:00
Rossen Stoyanchev c60313de3f Refine UriTemplate match pattern
The match/matches methods of UriTemplate use a regex with (.*) in place
of URI variables, which work fine except in the end where such a
pattern can match greedily more than one segment.

This commit updates the regex to use ([^/]*) instead since URI
variables are only meant to be used within a single path segment.

Issue: SPR-16169
2018-01-11 16:36:35 -05:00
Rossen Stoyanchev 84b8ceca0f Update Javadoc for AbstractHandlerMapping properties
Clarify what PathPatternParser is used for which is CORS checks in the
very least. Some sub-classes will also use it for request mapping but
not all (e.g. RouterFunctionMapping). Hence the need to be more
explicit.
2018-01-11 12:41:04 -05:00
Rossen Stoyanchev 72e3c43375 Refine @EnableWebFlux docs for functional endpoints
@EnableWebFlux bootstraps both annotated controllers and functional
endpoints, so we need to be more explicit about which parts of the
configuration apply to which.

Issue: SPR-16360
2018-01-11 12:25:42 -05:00
Juergen Hoeller 8f6d3feaa0 Tests for non-required MultipartFile parameters
Issue: SPR-16329
2018-01-11 10:22:59 +01:00
Juergen Hoeller 121f9e3734 BridgeMethodResolver properly resolves all declared interfaces
Issue: SPR-16288
2018-01-11 10:18:27 +01:00
Rossen Stoyanchev ea73ec5c41 ResourceUrlProvider handles sanitizes double slashes
Issue: SPR-16296
2018-01-10 17:48:43 -05:00
Rossen Stoyanchev cdf2ab9737 Expand docs on WebFlux.fn + @EnableWebFlux
Issue: SPR-16360
2018-01-10 16:28:34 -05:00
sdeleuze 19a1477228 Fix Kotlin bean w/ default + secondary ctors handling
This commit polishes SPR-16022 fix in order to handle correctly
the case when primary and default constructors are the same when
a secondary constructor is defined.

Issue: SPR-16289
2018-01-10 18:29:33 +01:00
Arjen Poutsma 5adaa1030c Javadoc 2018-01-10 17:54:51 +01:00
Arjen Poutsma f7bf6460aa Reverted signature change on fromMultipartData
Reverted back from `MultiValueMap<String, Object>` to
`MultiValueMap<String, ?>`
2018-01-10 17:54:51 +01:00
Rossen Stoyanchev b36af8a11f Uncomment test after Reactor Netty fix
Issue: SPR-16246
2018-01-10 10:47:36 -05:00
Arjen Poutsma 8a129ef3da Use write aggregator from DataBufferUtils
This commit uses the newly introduced write aggregator from
DataBufferUtils throughout WebFlux.

Issue: SPR-16351
2018-01-10 11:15:47 +01:00
Arjen Poutsma 69ccba30e9 Introduce write aggregator to DataBufferUtils
As a consequence of dropping CompositeByteBuf (see prior commit),
DataBuffers fluxes that are aggregated with Flux.reduce(BiFunction) are
now required to be released, as the composite no longer holds a
reference to subsequent data buffers.

For this purpose, DataBufferUtils now has a writeAggregator that can be
used with Flux.reduce, and that released the subsequent buffers
properly.

Issue: SPR-16351
2018-01-10 11:14:02 +01:00
Arjen Poutsma e6893da971 Remove use of CompositeByteBuf in NettyDataBuffer
Prior to this commit, NettyDataBuffer had a optimization in
write(ByteBuf...), where it used a CompositeByteBuf to hold the original
and the parameter buffer.
Unfortunately, this procedure has nasty consequences when splicing
buffers (see https://stackoverflow.com/a/48111196/839733).

As of this commit, NettyDataBuffer stopped using CompositeByteBuf, and
 simply does ByteBuf.write().

Issue: SPR-16351
2018-01-10 11:05:41 +01:00
Rossen Stoyanchev 5ed0cf9027 Fix init issues in AbstractReactiveWebInitializer
Issue: SPR-16290
2018-01-09 17:32:40 -05:00
Rossen Stoyanchev 537f79f680 Merge pull request #1609 from jeff/SPR-16245 2018-01-09 16:40:46 -05:00
Rossen Stoyanchev 018469286d Polish 2018-01-09 16:39:40 -05:00
Jeff Nelson 30e40210a8 Extract protected method in AbstractRequestLoggingFilter
Issue: SPR-16245
2018-01-09 16:38:21 -05:00
Juergen Hoeller c8bdb3c602 Polishing 2018-01-09 22:20:22 +01:00
Juergen Hoeller e43439c6c3 SqlParameterSource interface exposes parameter names
Includes default implementations of getSqlType and getTypeName.

Issue: SPR-16361
2018-01-09 22:19:46 +01:00
Juergen Hoeller b2322e58d9 Support for PostgreSQL array syntax
Includes efficient separator determination.

Issue: SPR-16340
2018-01-09 22:19:05 +01:00
Masahiro Ide d9af4d6599 ResourceBundleMessageSource uses ConcurrentHashMaps instead of synchronization
Issue: https://jira.spring.io/browse/SPR-16235
2018-01-09 22:12:03 +01:00
Stephane Nicoll f72318a0ae Merge pull request #1640 from making:patch-2
* pr/1640:
  Fix source code in doc
2018-01-09 19:17:24 +01:00
Toshiaki Maki 50b46c9e35 Fix source code in doc
Closes gh-1640
2018-01-09 19:17:09 +01:00
sdeleuze 9c7de232b8 Polishing
Optimize same origin check when the request is an instance of
ServletServerHttpRequest and when there is no forwarded headers.

This commit also optimizes the getPort methods and ForwardedHeaderFilter
forwarded headers checks.

Issue: SPR-16262
2018-01-09 14:33:54 +01:00
sdeleuze c326e44488 Refine forwarded protocol support
This commit refines forwarded protocol support in order to support
proxies that only set "X-Forwarded-Proto" header and not
"X-Forwarded-Port" by performing a reset of the port in such case.

"Forwarded" header support has been updated accordingly since it
also supports similar use case, as described in SPR-15504.

Issue: SPR-16262
2018-01-09 14:33:54 +01:00
Rossen Stoyanchev 30c0616384 Improve error message
Issue: SPR-16214
2018-01-08 21:30:41 -05:00
Rossen Stoyanchev 74b5c371ba Merge pull request #1634 from lin/patch-1 2018-01-08 17:36:24 -05:00
Rossen Stoyanchev 846636f2b9 Polish 2018-01-08 17:35:30 -05:00
Aleksey Voronenkov cb8ad46564 Overloaded methods in InterceptorRegistration
Issue: SPR-16324
2018-01-08 17:31:06 -05:00
Juergen Hoeller 45828cb934 Check BeanInfoFactory for interface introspection as well
Issue: SPR-16322
2018-01-08 18:05:02 +01:00
Rossen Stoyanchev 446e7ed25c Links from @RequestMapping to reference docs
Issue: SPR-16266
2018-01-08 09:29:52 -05:00
Juergen Hoeller 73cad470e5 Upgrade to HtmlUnit 2.29 and Selenium 3.8.1 2018-01-08 13:41:36 +01:00
Juergen Hoeller 4adc820714 Warning instead of error for non-present type filter class
Issue: SPR-16356
2018-01-08 13:41:23 +01:00
Stephane Nicoll 0d151b0212 Fix SpEL syntax
Issue: SPR-16315
2018-01-08 08:46:20 +01:00
Juergen Hoeller 72590dbb21 CachingConnectionFactory does not cache producer for temporary queue/topic
Issue: SPR-16353
2018-01-08 00:26:29 +01:00
Juergen Hoeller 7a55d932f9 Build against OpenPDF 1.0.5 instead of outdated iText 2.1.7
Issue: SPR-16352
2018-01-08 00:26:10 +01:00
Skelotron b9e03cb06b SPR-16316 — fix transaction timeout value overflow 2018-01-07 23:48:24 +01:00
Juergen Hoeller 4a57e26d76 Polishing 2018-01-07 23:33:35 +01:00
Juergen Hoeller b6191f6057 Direct reference to JPA 2.1 SynchronizationType enum
Issue: SPR-13481
2018-01-07 23:30:05 +01:00
Juergen Hoeller d187cbce73 LoadTimeWeaver detection differentiates between WebSphere and Liberty
Issue SPR-16248
2018-01-07 23:29:16 +01:00
Juergen Hoeller 6a1fe0b1d0 FunctionReference's method field is volatile
Issue: SPR-16255
2018-01-07 23:23:45 +01:00
Juergen Hoeller 0a06bce3a6 Relaxed BeanFactory assertion in resolveInterceptorNames
Issue: SPR-16347
2018-01-07 23:23:25 +01:00
Juergen Hoeller 977550f956 Properly handle null bean instance in getLifecycleBeans
Issue: SPR-16343
2018-01-07 23:23:08 +01:00
Juergen Hoeller 18d90ec140 Properly handle null FactoryBean instance
Issue: SPR-16250
2018-01-07 23:22:56 +01:00
Juergen Hoeller d3e0f4dd91 Consider enum subclasses as simple value types as well
Issue: SPR-16278
2018-01-07 23:20:09 +01:00
Juergen Hoeller 9beb97880f Support for static field access on non-public enums
Issue: SPR-16284
2018-01-07 23:19:47 +01:00
Juergen Hoeller 7ad69bfc20 Avoid extra existence check in SimpleMetadataReaderFactory
Issue: SPR-16281
2018-01-07 23:18:52 +01:00
Juergen Hoeller 1a154c3e4c Consistently throw FileNotFoundException even for NIO access
Issue: SPR-16334
2018-01-07 23:18:28 +01:00
Rossen Stoyanchev 37f0e8c6e5 Access to ApplicationContext via ServerWebExchange
Issue: SPR-16298
2018-01-05 15:46:45 -05:00
Rossen Stoyanchev 93a522f336 Fix regression in BodyInserters with multipart data
The issue was introduced very recently with commit #7035ee but never
released.

Issue: SPR-16350
2018-01-05 13:59:14 -05:00
Rossen Stoyanchev 542de82c5f Document Jackson encoder/decoder behavior
Issue: SPR-16260
2018-01-04 22:02:27 -05:00
Rossen Stoyanchev 7bf9b767fd Improve HTTP message reading/writing docs
Cross-reference sections on HttpMessageReader/Writer and
HttpMessageConverter and improve the content.

Issue: SPR-16260
2018-01-04 21:15:04 -05:00
Rossen Stoyanchev 0f1f95e090 Add hasJsonPath/doesNotHaveJsonPath assertion options
Issue: SPR-16339
2018-01-03 20:27:09 -05:00
Juergen Hoeller a49123a72b Upgrade to RxJava 2.1.8 (and Protobuf 3.5.1) 2017-12-31 18:51:22 +01:00
Juergen Hoeller dd4f915234 Upgrade to JCache 1.1 (and Caffeine 2.6.1)
Issue: SPR-13574
2017-12-28 14:08:21 +01:00
sdeleuze d2616b7996 Fix RestOperations extensions parameters nullability
Issue: SPR-16328
2017-12-27 17:54:38 +01:00
sdeleuze deac8e556e Improve Kotlin extensions doc about type erasure
Since type erasure can be fixed only when using
ParameterizedTypeReference based Java methods, RestOperations and
WebFlux API documentation should be updated to specify which extensions
are subject to type erasure, and which are not.

Issue: SPR-16273
2017-12-27 17:49:18 +01:00
Stephane Nicoll 8fc16fc937 Merge pull request #1627 from nkjackzhang:patch-1
* pr/1627:
  Polish Javadoc
2017-12-23 14:20:28 +01:00
nkjackzhang ec6eed6c63 Polish Javadoc
Closes gh-1627
2017-12-23 14:19:56 +01:00
Rossen Stoyanchev 3aee3182ac Restore milestone repo for AspectJ 2017-12-22 15:13:48 -05:00
Rossen Stoyanchev f389f66270 Upgrade to Reactor Bismuth snapshots
This allows us to confirm 0.7.3 fixes and run tests against the latest
ahead of an upgrade to Reactor Netty 0.8 snapshots.

The fix for #231 and #240 are confirmed but unfortunately test case for
2017-12-22 12:50:18 -05:00
Arjen Poutsma 6c3a64578c Move ResolvableType from HttpEntity to PublisherEntity
This commit moves the ResolvableType field from HttpEntity to
PublisherEntity, a new entity type defined in MultipartBodyBuilder.
With this change, the scope of the ResolvableType is limited to
multipart-related code, instead of becoming part of the complete
HttpEntity hierarchy.

Issue: SPR-16307
2017-12-22 13:44:01 +01:00
Brian Clozel 6e587d5c57 Add new WebTestClient header assertions
This commit adds new header assertions for `WebTestClient`.

`doesNotExist` tests that a given header is not present:

    .expectHeader().doesNotExist("Cache-Control");

`contentTypeCompatibleWith` tests for MediaType compatibility:

    .expectHeader().contentTypeCompatibleWith("text/*");

Issue: SPR-16285
2017-12-21 20:34:24 +01:00
Arjen Poutsma 7035ee7ebb Support Publishers for multipart data in BodyInserters
This commit uses the changes in the previous commit to support
Publishers as parts for multipart data.

Issue: SPR-16307
2017-12-21 17:41:01 +01:00
Arjen Poutsma f23612c3a3 Add ResolvableType to HttpEntity for multipart Publishers
This commit adds a ResolvableType field to HttpEntity, in order to
support Publishers as multipart data. Without the type, the
MultipartHttpMessageWriter does not know which delegate writer to use to
write the part.

Issue: SPR-16307
2017-12-21 17:40:27 +01:00
Juergen Hoeller 9d27e86951 Upgrade to Hibernate Validator 6.0.7 2017-12-21 17:00:06 +01:00
Juergen Hoeller 4577a19190 Upgrade to Commons Pool 2.5 (and Jackson Kotlin Module 2.9.3) 2017-12-21 13:52:00 +01:00
sdeleuze 0075f13126 Improve CORS list properties combination logic
This commit allows CorsConfiguration#combine()
to differentiate permit default values set by
CorsConfiguration#applyPermitDefaultValues()
from values configured explicitly by the user.

Those permit default values will be overridden
by any user-provided ones while user-provided values
will be combined in an additive way, including
when "*" is specified.

Documentation has been improved accordingly.

Issue: SPR-15772
2017-12-20 22:38:01 +01:00
Juergen Hoeller 425a999d5e Upgrade to Gradle 4.4.1 (and Netty 4.1.19)
Issue: SPR-16237
2017-12-20 18:10:15 +01:00
Rossen Stoyanchev a8cf275aed MockServerHttpRequest builder supports query params
Issue: SPR-16280
2017-12-19 15:38:12 -05:00
Rossen Stoyanchev 6df1a7874a MockHttpServletResponse uses correct language header
Issue: SPR-16311
2017-12-19 13:56:16 -05:00
sdeleuze 99cbfd32c3 Set default view content-type with WebFlux fn
With this commit, the default content-type defined by the
view (usually "text/html;charset=UTF-8" defined in AbstractView)
is used if any, when none is defined in the response headers.

Issue: SPR-16247
2017-12-19 11:11:40 +01:00
sdeleuze e3e4f37222 Upgrade to Kotlin 1.2.10 2017-12-19 11:03:50 +01:00
Juergen Hoeller 3a51b5a9d2 Upgrade to Hibernate Validator 6.0.6 2017-12-15 15:58:12 +01:00
Arjen Poutsma b8201f48fd Typo in example code in spring reactive reference document
Issue: SPR-16300
2017-12-15 12:03:14 +01:00
Rossen Stoyanchev f4d8c7cc2b Improve decodeToMono support
This commit ensures uniform support for decodeToMono across the various
byte and String related decoders.

Issue: SPR-16253
2017-12-14 16:59:34 -05:00
Arjen Poutsma 8e253a316d Support top-level scalar values in Jackson2Tokenizer
Issue: SPR-16166
2017-12-14 16:44:40 +01:00
sdeleuze 51aa16f85f Remove -Xskip-runtime-version-check Kotlin compiler arg
This compiler argument is not needed anymore since KT-16780
has been fixed.
2017-12-14 15:08:00 +01:00
sdeleuze 8c2e5dcb4a Update Kotlin reference documentation
This commit removes the mention that Spring Boot 2.0 includes
Jackson Kotlin module since it is not the case as of M7, and
add a note about using bean validation annotations with Kotlin.
2017-12-14 14:22:59 +01:00
Juergen Hoeller 05988193b7 Upgrade to Jackson 2.9.3 and Netty 4.1.18 2017-12-13 17:55:50 +01:00
sdeleuze 6a7b197af7 Improve Jackson codecs documentation 2017-12-13 12:07:43 +01:00
sdeleuze 73a81f98d4 Allow interceptors to add existing header values
Additional commit to provide support of requests
that have a body.

Issue: SPR-15066
2017-12-13 10:08:24 +01:00
Rossen Stoyanchev 583201b02c MappingJackson2MessageConverter uses generic type
Issue: SPR-16252
2017-12-12 22:32:33 -05:00
Rossen Stoyanchev f05175586e Refine behavior on error after response committed
If the response is set and we can't change the status through
ServerHttpResponse any more, allow the error signal to propagate and
let the individual server adapters handle it. Ultimately that should
result in closing the connection.

On Servlet containers, we check one last time if the response is
committed (we may not have filled the buffer). If not then save
the exception as a request attribute, dispatch, and re-throw it on the
container thread.

On Undertow access the connection and close it.

On Netty just let the error through to Reactor Netty.

Issue: SPR-16051
2017-12-12 14:51:24 -05:00
Juergen Hoeller 9d421841d5 Merge pull request #1622 from dreis2211/SPR-16293
Improve performance of some string operations

Issue: SPR-16293
2017-12-12 18:36:07 +01:00
Christoph Dreis 260ebeca3a Improve performance of some string operations
Issue: SPR-16293
2017-12-12 17:14:24 +01:00
Rossen Stoyanchev f736b665bd Fix compile issue from previous commit 2017-12-11 16:37:11 -05:00
Rémi Alvergnat 76dcde9e31 Fix Stomp Broker Relay ignoring destination prefixes in some cases
Issue: SPR-16265
2017-12-11 13:25:29 -08:00
sdeleuze 554662ebab Allow interceptors to add existing header values
Provide a fully mutable HttpHeaders to ClientHttpRequestInterceptors
of a RestTemplate when headers are set using HttpEntity. This avoids
UnsupportedOperationException if both HttpEntity and
ClientHttpRequestInterceptor add values for the same HTTP header.

Issue: SPR-15066
2017-12-11 15:01:20 +01:00
sdeleuze ac00d8a215 Use JCenter first to resolve plugins
This is a workaround for Kotlin/dokka#146.
2017-12-11 12:07:17 +01:00
sdeleuze 769ea1b106 Fix Kdoc regression to show only Kotlin API
As of Gradle Kotlin plugin 1.1.60,
project.sourceSets.main.kotlin.srcDirs contains
Java source directories in addition to Kotlin
ones. This commit filter out Java sources to
retain only Kotlin ones.

Issue: SPR-16268
2017-12-11 11:28:39 +01:00
Stephane Nicoll 206a5e02dd Merge pull request #1615 from gro-gg:patch-1
* pr/1615:
  Fix exception message
2017-12-09 15:46:55 -08:00
Philipp Grogg 591429e538 Fix exception message
initialDelayString is parsed into long, not integer.

Closes gh-1615
2017-12-09 15:46:28 -08:00
Juergen Hoeller d198626618 Upgrade to Gradle 4.4 (and Apache HttpClient 4.5.4)
Issue: SPR-16237
2017-12-07 07:28:07 +01:00
Sam Brannen 767ea9db83 Fix minor logic error in SocketUtils 2017-12-06 17:00:29 -08:00
Tony Mitchell 9f36d170be Support identical minPort and maxPort in SocketUtils (#1612)
This commit fixes a bug where an IllegalStateException was
thrown if the minPort and maxPort values supplied to
SocketUtils.findAvailableTcpPort(int, int) were identical.
2017-12-06 16:38:46 -08:00
Sam Brannen f9689dfe48 Polishing 2017-12-06 15:54:02 -08:00
Juergen Hoeller bf8831157d Merge pull request #1614 from dreis2211/SPR-16264
Reduce access on user in SimpleBrokerMessageHandler.handleMessageInternal
2017-12-06 00:40:38 +01:00
Christoph Dreis f4e9fb52a8 Reduce access on user in SimpleBrokerMessageHandler.handleMessageInternal
Issue: SPR-16264
2017-12-05 11:18:02 +01:00
ugultopu 04ab93ceb4 Add missing verb on the note about relays 2017-12-04 11:20:39 -05:00
Juergen Hoeller 0ef117ff11 Upgrade to Tomcat 8.5.24 and Jetty 9.4.8 2017-12-03 16:03:02 +01:00
Rossen Stoyanchev fda08852ba Take content-type from ResponseEntity unconditionally
From #9a894a we began using the content-type from the ResponseEntity
but cross-checking it still against the requested content type.

Arguably there isn't any purpose in cross-checking. The only possible
outcomes are: a) it's compatible or b) it's not, which would result in
406 (NOT_ACCEPTABLE). As we've been given explicitly the media type to
use, it makes little sense to send 406, ignoring the wish to use that
content type.

Issue: SPR-16251
2017-12-02 00:43:43 -05:00
Rossen Stoyanchev 203370a810 Handle absolute URI in Reactor request.uri()
The request URI returned from HttpServerRequest.uri() typically
contains contains an absolute path but could also contain an
absolute URI. This commit adds handling for the latter, effectively
taking only the absolute path portion.

Issue: SPR-16243
2017-12-01 23:34:35 -05:00
Rossen Stoyanchev b9a1168580 Consistently return null if certificates not available
Issue: SPR-16244
2017-12-01 21:38:40 -05:00
Rossen Stoyanchev e99b0038a0 Polish 2017-12-01 21:18:13 -05:00
Rossen Stoyanchev 91d3e4402f Add test case for SPR-16246 2017-12-01 15:40:35 -05:00
Rossen Stoyanchev 44c95a4736 Adjust logging to catch sporadic PERF build failure 2017-12-01 10:11:51 -05:00
sdeleuze 7368f58518 Upgrade to Kotlin 1.2
apiVersion and languageVersion options are set to 1.1
on production code in order to avoid incompatibilities
with Kotlin 1.1 based projects or libraries.

Issue: SPR-16239
2017-11-30 14:50:16 +01:00
Rossen Stoyanchev 63c2c08e01 Update advice on file extensions in URLs
Issue: SPR-16240
2017-11-29 15:09:13 -05:00
Rossen Stoyanchev dd0d270ba2 Improve error handling when response is committed
ResponseStatusExceptionHandler lets the error through if it can't
change the status while HttpWebHandlerAdapter logs a more helpful
message (including status code) but without a full stack trace.

Issue: SPR-16231
2017-11-29 12:39:06 -05:00
Juergen Hoeller 873cb4e58b Remove unnecessary deprecation suppression 2017-11-29 15:44:08 +01:00
Juergen Hoeller b55a2631ba NamedParameterJdbcTemplate provides accessor for classic JdbcTemplate
Issue: SPR-16241
2017-11-29 15:38:21 +01:00
Rossen Stoyanchev 6f73b8b200 Add WebTestClient to Web Servlet section 2017-11-29 08:45:07 -05:00
Sam Brannen 2b68212c20 Fix grammar 2017-11-29 14:44:21 +01:00
Rossen Stoyanchev 3650ecc3bb Polish 2017-11-28 22:16:07 -05:00
Rossen Stoyanchev bec63fbb33 Update CORS chapters
- Add "Processing" section (also replaces Advanced Customizations)
- Add information on out-of-the-box behavior
- Add more deails on @CrossOririn default configuratio
- Add cross-references between Spring MVC and WebFlux
- Polish
2017-11-28 16:29:30 -05:00
Rossen Stoyanchev 375a63a041 Update README.md 2017-11-28 09:28:34 -05:00
Sam Brannen 773cd3241b Polishing 2017-11-28 14:11:21 +01:00
Juergen Hoeller f2e8171bfb Latest dependency updates (RxJava 2.1.7, TestNG 6.13.1, Selenium HtmlUnit Driver 2.28.2) 2017-11-28 12:10:26 +01:00
Rossen Stoyanchev dc3d834026 Refinements related to a committed response
1. setComplete checks the isCommitted flag to avoid an unnecessary
debug message. This method is meant to be safe to call many times.

2. setStatusCode lowers log message to TRACE, since the return value
communicates the outcome it's arguably much less critical.

3. Add comment and test case for ResponseStatusExceptionHandler.
A ResponseStatusException is clearly meant to be handled by this
handler so don't let it pass through even if the respones is
committed.

Issue: SPR-16231
2017-11-27 16:31:49 -05:00
Spring Buildmaster e30f1fbe89 Next Development Version 2017-11-27 10:52:28 +00:00
316 changed files with 11293 additions and 4075 deletions
+3 -2
View File
@@ -22,8 +22,9 @@ wiki page.
## Documentation
The Spring Frameworks maintains
[reference documentation](http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/),
The Spring Frameworks maintains reference documentation
([published](http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/) and
[source](src/docs/asciidoc)),
Github [wiki pages](https://github.com/spring-projects/spring-framework/wiki), and an
[API reference](http://docs.spring.io/spring-framework/docs/current/javadoc-api/).
There are also [guides and tutorials](https://spring.io/guides) across Spring projects.
+23 -17
View File
@@ -14,7 +14,7 @@ buildscript {
plugins {
id "com.gradle.build-scan" version "1.8"
id "io.spring.dependency-management" version "1.0.3.RELEASE" apply false
id "org.jetbrains.kotlin.jvm" version "1.1.61" apply false
id "org.jetbrains.kotlin.jvm" version "1.2.20" apply false
id "org.jetbrains.dokka" version "0.9.15"
id "org.asciidoctor.convert" version "1.5.6"
}
@@ -45,22 +45,22 @@ configure(allprojects) { project ->
ext.freemarkerVersion = "2.3.27-incubating"
ext.groovyVersion = "2.4.13"
ext.hsqldbVersion = "2.4.0"
ext.jackson2Version = "2.9.2"
ext.jettyVersion = "9.4.7.v20170914"
ext.junitJupiterVersion = "5.0.2"
ext.junitPlatformVersion = "1.0.2"
ext.junitVintageVersion = "4.12.2"
ext.kotlinVersion = "1.1.61"
ext.jackson2Version = "2.9.3"
ext.jettyVersion = "9.4.8.v20171121"
ext.junitJupiterVersion = "5.0.3"
ext.junitPlatformVersion = "1.0.3"
ext.junitVintageVersion = "4.12.3"
ext.kotlinVersion = "1.2.20"
ext.log4jVersion = "2.10.0"
ext.nettyVersion = "4.1.17.Final"
ext.reactorVersion = "Bismuth-SR4"
ext.nettyVersion = "4.1.20.Final"
ext.reactorVersion = "Bismuth-SR5"
ext.rxjavaVersion = "1.3.4"
ext.rxjavaAdapterVersion = "1.2.1"
ext.rxjava2Version = "2.1.6"
ext.rxjava2Version = "2.1.8"
ext.slf4jVersion = "1.7.25"
ext.tiles3Version = "3.0.8"
ext.tomcatVersion = "8.5.23"
ext.undertowVersion = "1.4.21.Final"
ext.tomcatVersion = "8.5.27"
ext.undertowVersion = "1.4.22.Final"
ext.gradleScriptDir = "${rootProject.projectDir}/gradle"
@@ -71,13 +71,19 @@ configure(allprojects) { project ->
apply plugin: "kotlin"
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
kotlinOptions.freeCompilerArgs = ["-Xskip-runtime-version-check", "-Xjsr305=strict"]
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs = ["-Xjsr305=strict"]
apiVersion = "1.1"
languageVersion = "1.1"
}
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
kotlinOptions.freeCompilerArgs = ["-Xskip-runtime-version-check", "-Xjsr305=strict"]
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs = ["-Xjsr305=strict"]
}
}
configurations.all {
@@ -268,7 +274,7 @@ configure(rootProject) {
task wrapper(type: Wrapper) {
description = "Generates gradlew[.bat] scripts"
gradleVersion = '4.3.1'
gradleVersion = '4.4.1'
doLast() {
def gradleOpts = "-XX:MaxMetaspaceSize=1024m -Xmx1024m"
+1 -1
View File
@@ -1 +1 @@
version=5.0.2.BUILD-SNAPSHOT
version=5.0.3.RELEASE
+2 -1
View File
@@ -66,7 +66,8 @@ dokka {
outputDirectory = "$buildDir/docs/kdoc"
sourceDirs = files(subprojects.collect { project ->
project.sourceSets.main.kotlin.srcDirs
def kotlinDirs = project.sourceSets.main.kotlin.srcDirs.collect()
kotlinDirs -= project.sourceSets.main.java.srcDirs
})
externalDocumentationLink {
url = new URL("http://docs.spring.io/spring-framework/docs/${version}/javadoc-api/")
Binary file not shown.
+2 -2
View File
@@ -1,5 +1,5 @@
distributionUrl=https\://services.gradle.org/distributions/gradle-4.3.1-bin.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-bin.zip
+8
View File
@@ -9,6 +9,14 @@ pluginManagement {
}
*/
// Workaround for https://github.com/Kotlin/dokka/issues/146
pluginManagement {
repositories {
jcenter()
gradlePluginPortal()
}
}
include "spring-aop"
include "spring-aspects"
include "spring-beans"
+1 -1
View File
@@ -4,6 +4,6 @@ dependencies {
compile(project(":spring-beans"))
compile(project(':spring-core'))
optional("org.aspectj:aspectjweaver:${aspectjVersion}")
optional("org.apache.commons:commons-pool2:2.4.3")
optional("org.apache.commons:commons-pool2:2.5.0")
optional("com.jamonapi:jamon:2.81")
}
@@ -148,7 +148,7 @@ public class AspectMetadata implements Serializable {
}
/**
* Return the aspect class.
* Return the aspect name.
*/
public String getAspectName() {
return this.aspectName;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -544,13 +544,13 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
* @see #setInterceptorNames
*/
private Advisor[] resolveInterceptorNames() {
Assert.state(this.beanFactory != null, "BeanFactory required for resolving interceptor names");
ConfigurableBeanFactory cbf = (this.beanFactory instanceof ConfigurableBeanFactory ?
(ConfigurableBeanFactory) this.beanFactory : null);
BeanFactory bf = this.beanFactory;
ConfigurableBeanFactory cbf = (bf instanceof ConfigurableBeanFactory ? (ConfigurableBeanFactory) bf : null);
List<Advisor> advisors = new ArrayList<>();
for (String beanName : this.interceptorNames) {
if (cbf == null || !cbf.isCurrentlyInCreation(beanName)) {
Object next = this.beanFactory.getBean(beanName);
Assert.state(bf != null, "BeanFactory required for resolving interceptor names");
Object next = bf.getBean(beanName);
advisors.add(this.advisorAdapterRegistry.wrap(next));
}
}
+1 -1
View File
@@ -96,7 +96,7 @@ dependencies {
optional(project(":spring-context-support")) // for JavaMail and JSR-107 support
optional(project(":spring-orm")) // for JPA exception translation support
optional(project(":spring-tx")) // for JPA, @Transactional support
optional("javax.cache:cache-api:1.0.0") // for JCache aspect
optional("javax.cache:cache-api:1.1.0") // for JCache aspect
optional("javax.transaction:javax.transaction-api:1.2") // for @javax.transaction.Transactional support
testCompile(project(":spring-core")) // for CodeStyleAspect
testCompile(project(":spring-test"))
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -98,12 +98,19 @@ public abstract class BeanUtils {
}
/**
* Instantiate a class using its no-arg constructor.
* Instantiate a class using its 'primary' constructor (for Kotlin classes,
* potentially having default arguments declared) or its default constructor
* (for regular Java classes, expecting a standard no-arg setup).
* <p>Note that this method tries to set the constructor accessible
* if given a non-accessible (that is, non-public) constructor.
* @param clazz class to instantiate
* @param clazz the class to instantiate
* @return the new instance
* @throws BeanInstantiationException if the bean cannot be instantiated
* @throws BeanInstantiationException if the bean cannot be instantiated.
* The cause may notably indicate a {@link NoSuchMethodException} if no
* primary/default constructor was found, a {@link NoClassDefFoundError}
* or other {@link LinkageError} in case of an unresolvable class definition
* (e.g. due to a missing dependency at runtime), or an exception thrown
* from the constructor invocation itself.
* @see Constructor#newInstance
*/
public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {
@@ -113,15 +120,15 @@ public abstract class BeanUtils {
}
try {
Constructor<T> ctor = (KotlinDetector.isKotlinType(clazz) ?
KotlinDelegate.findPrimaryConstructor(clazz) : clazz.getDeclaredConstructor());
if (ctor == null) {
throw new BeanInstantiationException(clazz, "No default constructor found");
}
KotlinDelegate.getPrimaryConstructor(clazz) : clazz.getDeclaredConstructor());
return instantiateClass(ctor);
}
catch (NoSuchMethodException ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
catch (LinkageError err) {
throw new BeanInstantiationException(clazz, "Unresolvable class definition", err);
}
}
/**
@@ -561,13 +568,14 @@ public abstract class BeanUtils {
/**
* Check if the given type represents a "simple" value type:
* a primitive, a String or other CharSequence, a Number, a Date,
* a primitive, an enum, a String or other CharSequence, a Number, a Date,
* a URI, a URL, a Locale or a Class.
* @param clazz the type to check
* @return whether the given type represents a "simple" value type
*/
public static boolean isSimpleValueType(Class<?> clazz) {
return (ClassUtils.isPrimitiveOrWrapper(clazz) || clazz.isEnum() ||
return (ClassUtils.isPrimitiveOrWrapper(clazz) ||
Enum.class.isAssignableFrom(clazz) ||
CharSequence.class.isAssignableFrom(clazz) ||
Number.class.isAssignableFrom(clazz) ||
Date.class.isAssignableFrom(clazz) ||
@@ -692,10 +700,26 @@ public abstract class BeanUtils {
private static class KotlinDelegate {
/**
* Return the Java constructor corresponding to the Kotlin primary constructor if any.
* Determine the Java constructor corresponding to the Kotlin primary constructor.
* @param clazz the {@link Class} of the Kotlin class
* @throws NoSuchMethodException if no such constructor found
* @since 5.0.3
* @see #findPrimaryConstructor
* @see Class#getDeclaredConstructor
*/
public static <T> Constructor<T> getPrimaryConstructor(Class<T> clazz) throws NoSuchMethodException {
Constructor<T> ctor = findPrimaryConstructor(clazz);
if (ctor == null) {
throw new NoSuchMethodException();
}
return ctor;
}
/**
* Retrieve the Java constructor corresponding to the Kotlin primary constructor, if any.
* @param clazz the {@link Class} of the Kotlin class
* @see <a href="http://kotlinlang.org/docs/reference/classes.html#constructors">
* http://kotlinlang.org/docs/reference/classes.html#constructors</a>
* http://kotlinlang.org/docs/reference/classes.html#constructors</a>
*/
@Nullable
public static <T> Constructor<T> findPrimaryConstructor(Class<T> clazz) {
@@ -705,8 +729,10 @@ public abstract class BeanUtils {
return null;
}
Constructor<T> constructor = ReflectJvmMapping.getJavaConstructor(primaryCtor);
Assert.notNull(constructor,
() -> "Failed to find Java constructor for Kotlin primary constructor: " + clazz.getName());
if (constructor == null) {
throw new IllegalStateException(
"Failed to find Java constructor for Kotlin primary constructor: " + clazz.getName());
}
return constructor;
}
catch (UnsupportedOperationException ex) {
@@ -717,7 +743,8 @@ public abstract class BeanUtils {
/**
* Instantiate a Kotlin class using the provided constructor.
* @param ctor the constructor of the Kotlin class to instantiate
* @param args the constructor arguments to apply (use null for unspecified parameter if needed)
* @param args the constructor arguments to apply
* (use {@code null} for unspecified parameter if needed)
*/
public static <T> T instantiateClass(Constructor<T> ctor, Object... args)
throws IllegalAccessException, InvocationTargetException, InstantiationException {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -244,6 +244,27 @@ public class CachedIntrospectionResults {
return false;
}
/**
* Retrieve a {@link BeanInfo} descriptor for the given target class.
* @param beanClass the target class to introspect
* @param ignoreBeaninfoClasses whether to apply {@link Introspector#IGNORE_ALL_BEANINFO} mode
* @return the resulting {@code BeanInfo} descriptor (never {@code null})
* @throws IntrospectionException from the underlying {@link Introspector}
*/
private static BeanInfo getBeanInfo(Class<?> beanClass, boolean ignoreBeaninfoClasses)
throws IntrospectionException {
for (BeanInfoFactory beanInfoFactory : beanInfoFactories) {
BeanInfo beanInfo = beanInfoFactory.getBeanInfo(beanClass);
if (beanInfo != null) {
return beanInfo;
}
}
return (ignoreBeaninfoClasses ?
Introspector.getBeanInfo(beanClass, Introspector.IGNORE_ALL_BEANINFO) :
Introspector.getBeanInfo(beanClass));
}
/** The BeanInfo object for the introspected bean class */
private final BeanInfo beanInfo;
@@ -265,21 +286,7 @@ public class CachedIntrospectionResults {
if (logger.isTraceEnabled()) {
logger.trace("Getting BeanInfo for class [" + beanClass.getName() + "]");
}
BeanInfo beanInfo = null;
for (BeanInfoFactory beanInfoFactory : beanInfoFactories) {
beanInfo = beanInfoFactory.getBeanInfo(beanClass);
if (beanInfo != null) {
break;
}
}
if (beanInfo == null) {
// If none of the factories supported the class, fall back to the default
beanInfo = (shouldIntrospectorIgnoreBeaninfoClasses ?
Introspector.getBeanInfo(beanClass, Introspector.IGNORE_ALL_BEANINFO) :
Introspector.getBeanInfo(beanClass));
}
this.beanInfo = beanInfo;
this.beanInfo = getBeanInfo(beanClass, shouldIntrospectorIgnoreBeaninfoClasses);
if (logger.isTraceEnabled()) {
logger.trace("Caching PropertyDescriptors for class [" + beanClass.getName() + "]");
@@ -307,15 +314,17 @@ public class CachedIntrospectionResults {
// Explicitly check implemented interfaces for setter/getter methods as well,
// in particular for Java 8 default methods...
Class<?> clazz = beanClass;
while (clazz != null) {
while (clazz != null && clazz != Object.class) {
Class<?>[] ifcs = clazz.getInterfaces();
for (Class<?> ifc : ifcs) {
BeanInfo ifcInfo = Introspector.getBeanInfo(ifc, Introspector.IGNORE_ALL_BEANINFO);
PropertyDescriptor[] ifcPds = ifcInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : ifcPds) {
if (!this.propertyDescriptorCache.containsKey(pd.getName())) {
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
this.propertyDescriptorCache.put(pd.getName(), pd);
if (!ClassUtils.isJavaLanguageInterface(ifc)) {
BeanInfo ifcInfo = getBeanInfo(ifc, true);
PropertyDescriptor[] ifcPds = ifcInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : ifcPds) {
if (!this.propertyDescriptorCache.containsKey(pd.getName())) {
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
this.propertyDescriptorCache.put(pd.getName(), pd);
}
}
}
}
@@ -329,6 +338,7 @@ public class CachedIntrospectionResults {
}
}
BeanInfo getBeanInfo() {
return this.beanInfo;
}
@@ -341,10 +351,10 @@ public class CachedIntrospectionResults {
PropertyDescriptor getPropertyDescriptor(String name) {
PropertyDescriptor pd = this.propertyDescriptorCache.get(name);
if (pd == null && StringUtils.hasLength(name)) {
// Same lenient fallback checking as in PropertyTypeDescriptor...
pd = this.propertyDescriptorCache.get(name.substring(0, 1).toLowerCase() + name.substring(1));
// Same lenient fallback checking as in Property...
pd = this.propertyDescriptorCache.get(StringUtils.uncapitalize(name));
if (pd == null) {
pd = this.propertyDescriptorCache.get(name.substring(0, 1).toUpperCase() + name.substring(1));
pd = this.propertyDescriptorCache.get(StringUtils.capitalize(name));
}
}
return (pd == null || pd instanceof GenericTypeAwarePropertyDescriptor ? pd :
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -346,6 +346,7 @@ class TypeConverterDelegate {
// to be checked, hence we don't return it right away.
try {
Field enumField = requiredType.getField(trimmedValue);
ReflectionUtils.makeAccessible(enumField);
convertedValue = enumField.get(null);
}
catch (Throwable ex) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -280,7 +280,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
"Resolution of declared constructors on bean Class [" + beanClass.getName() +
"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
}
List<Constructor<?>> candidates = new ArrayList<Constructor<?>>(rawCandidates.length);
List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
Constructor<?> requiredConstructor = null;
Constructor<?> defaultConstructor = null;
Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);
@@ -347,7 +347,8 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
}
else if (nonSyntheticConstructors == 2 && primaryConstructor != null && defaultConstructor != null) {
else if (nonSyntheticConstructors == 2 && primaryConstructor != null
&& defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) {
candidateConstructors = new Constructor<?>[] {primaryConstructor, defaultConstructor};
}
else if (nonSyntheticConstructors == 1 && primaryConstructor != null) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -552,7 +552,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
// Check bean class whether we're dealing with a FactoryBean.
if (FactoryBean.class.isAssignableFrom(beanType)) {
if (!BeanFactoryUtils.isFactoryDereference(name)) {
if (!BeanFactoryUtils.isFactoryDereference(name) && beanInstance == null) {
// If it's a FactoryBean, we want to look at what it creates, not the factory class.
beanType = getTypeForFactoryBean(beanName, mbd);
if (beanType == null) {
@@ -1379,8 +1379,8 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
catch (ClassNotFoundException ex) {
throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex);
}
catch (LinkageError ex) {
throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex);
catch (LinkageError err) {
throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), err);
}
}
@@ -1614,8 +1614,13 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// Don't let calling code try to dereference the factory if the bean isn't a factory.
if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
}
}
// Now we have the bean instance, which may be a normal bean or a FactoryBean.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -739,19 +739,22 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
((SmartFactoryBean<?>) factory).isEagerInit(),
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
((SmartFactoryBean<?>) factory).isEagerInit(),
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -137,12 +137,12 @@ public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "] not found", ex);
throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
"] for namespace [" + namespaceUri + "]", ex);
}
catch (LinkageError err) {
throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "]: problem with handler class file or dependent class", err);
throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
className + "] for namespace [" + namespaceUri + "]", err);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@@ -173,4 +173,32 @@ public class BeanWrapperEnumTests {
assertEquals(new Integer(1), gb.getStandardEnumMap().get(CustomEnum.VALUE_1));
}
@Test
public void testNonPublicEnum() {
NonPublicEnumHolder holder = new NonPublicEnumHolder();
BeanWrapper bw = new BeanWrapperImpl(holder);
bw.setPropertyValue("nonPublicEnum", "VALUE_1");
assertEquals(NonPublicEnum.VALUE_1, holder.getNonPublicEnum());
}
enum NonPublicEnum {
VALUE_1, VALUE_2;
}
static class NonPublicEnumHolder {
private NonPublicEnum nonPublicEnum;
public NonPublicEnum getNonPublicEnum() {
return nonPublicEnum;
}
public void setNonPublicEnum(NonPublicEnum nonPublicEnum) {
this.nonPublicEnum = nonPublicEnum;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@@ -2736,6 +2736,16 @@ public class DefaultListableBeanFactoryTests {
assertSame(Optional.empty(), bf.getBean(Optional.class));
}
@Test
public void testNonPublicEnum() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
RootBeanDefinition bd = new RootBeanDefinition(NonPublicEnumHolder.class);
bd.getConstructorArgumentValues().addGenericArgumentValue("VALUE_1");
bf.registerBeanDefinition("holderBean", bd);
NonPublicEnumHolder holder = (NonPublicEnumHolder) bf.getBean("holderBean");
assertEquals(NonPublicEnum.VALUE_1, holder.getNonPublicEnum());
}
/**
* Test that by-type bean lookup caching is working effectively by searching for a
* bean of type B 10K times within a container having 1K additional beans of type A.
@@ -3263,4 +3273,24 @@ public class DefaultListableBeanFactoryTests {
}
}
enum NonPublicEnum {
VALUE_1, VALUE_2;
}
static class NonPublicEnumHolder {
final NonPublicEnum nonPublicEnum;
public NonPublicEnumHolder(NonPublicEnum nonPublicEnum) {
this.nonPublicEnum = nonPublicEnum;
}
public NonPublicEnum getNonPublicEnum() {
return nonPublicEnum;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,6 +43,7 @@ import org.mockito.Mockito;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
@@ -152,6 +153,35 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertEquals("nestedTestBean", depBeans[1]);
}
@Test
public void testExtendedResourceInjectionWithDestruction() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerResolvableDependency(BeanFactory.class, bf);
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(TypedExtendedResourceInjectionBean.class));
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
NestedTestBean ntb = new NestedTestBean();
bf.registerSingleton("nestedTestBean", ntb);
TestBean tb = bf.getBean("testBean", TestBean.class);
TypedExtendedResourceInjectionBean bean = (TypedExtendedResourceInjectionBean) bf.getBean("annotatedBean");
assertSame(tb, bean.getTestBean());
assertSame(tb, bean.getTestBean2());
assertSame(tb, bean.getTestBean3());
assertSame(tb, bean.getTestBean4());
assertSame(ntb, bean.getNestedTestBean());
assertSame(bf, bean.getBeanFactory());
assertArrayEquals(new String[] {"testBean", "nestedTestBean"}, bf.getDependenciesForBean("annotatedBean"));
bf.destroySingleton("testBean");
assertFalse(bf.containsSingleton("testBean"));
assertFalse(bf.containsSingleton("annotatedBean"));
assertTrue(bean.destroyed);
assertSame(0, bf.getDependenciesForBean("annotatedBean").length);
}
@Test
public void testExtendedResourceInjectionWithOverriding() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -1286,9 +1316,6 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.destroySingletons();
}
@Qualifier("testBean")
private void testBeanQualifierProvider() {}
@Test
public void testObjectFactorySerialization() throws Exception {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -2494,6 +2521,9 @@ public class AutowiredAnnotationBeanPostProcessorTests {
}
@Qualifier("testBean")
private void testBeanQualifierProvider() {}
@Qualifier("integerRepo")
private Repository<?> integerRepositoryQualifierProvider;
@@ -2579,7 +2609,15 @@ public class AutowiredAnnotationBeanPostProcessorTests {
}
public static class TypedExtendedResourceInjectionBean extends NonPublicResourceInjectionBean<NestedTestBean> {
public static class TypedExtendedResourceInjectionBean extends NonPublicResourceInjectionBean<NestedTestBean>
implements DisposableBean {
public boolean destroyed = false;
@Override
public void destroy() {
this.destroyed = true;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -66,10 +66,9 @@ public class CustomProblemReporterTests {
private static class CollatingProblemReporter implements ProblemReporter {
private List<Problem> errors = new ArrayList<>();
private List<Problem> warnings = new ArrayList<>();
private final List<Problem> errors = new ArrayList<>();
private final List<Problem> warnings = new ArrayList<>();
@Override
public void fatal(Problem problem) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -49,8 +49,7 @@ public class XmlBeanDefinitionReaderTests {
@Test(expected = BeanDefinitionStoreException.class)
public void withOpenInputStream() {
SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
Resource resource = new InputStreamResource(getClass().getResourceAsStream(
"test.xml"));
Resource resource = new InputStreamResource(getClass().getResourceAsStream("test.xml"));
new XmlBeanDefinitionReader(registry).loadBeanDefinitions(resource);
}
@@ -122,16 +121,16 @@ public class XmlBeanDefinitionReaderTests {
}
@Test
public void dtdValidationAutodetect() throws Exception {
public void dtdValidationAutodetect() {
doTestValidation("validateWithDtd.xml");
}
@Test
public void xsdValidationAutodetect() throws Exception {
public void xsdValidationAutodetect() {
doTestValidation("validateWithXsd.xml");
}
private void doTestValidation(String resourceName) throws Exception {
private void doTestValidation(String resourceName) {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
Resource resource = new ClassPathResource(resourceName, getClass());
new XmlBeanDefinitionReader(factory).loadBeanDefinitions(resource);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@@ -169,6 +169,20 @@ class KotlinAutowiredTests {
assertEquals(tb, kb.testBean)
}
@Test // SPR-16289
fun `Instantiation via secondary constructor when a default primary is defined`() {
val bf = DefaultListableBeanFactory()
val bpp = AutowiredAnnotationBeanPostProcessor()
bpp.setBeanFactory(bf)
bf.addBeanPostProcessor(bpp)
val bd = RootBeanDefinition(KotlinBeanWithPrimaryAndSecondaryConstructors::class.java)
bd.scope = RootBeanDefinition.SCOPE_PROTOTYPE
bf.registerBeanDefinition("bean", bd)
bf.getBean(KotlinBeanWithPrimaryAndSecondaryConstructors::class.java, "foo")
bf.getBean(KotlinBeanWithPrimaryAndSecondaryConstructors::class.java)
}
@Test(expected = BeanCreationException::class) // SPR-16022
fun `No autowiring with primary and secondary non annotated constructors`() {
val bf = DefaultListableBeanFactory()
@@ -244,6 +258,11 @@ class KotlinAutowiredTests {
constructor() : this(TestBean())
}
@Suppress("unused", "UNUSED_PARAMETER")
class KotlinBeanWithPrimaryAndSecondaryConstructors() {
constructor(p: String) : this()
}
class KotlinBeanWithSecondaryConstructor(
val optional: String = "foo",
val injectedFromConstructor: TestBean
@@ -8,8 +8,8 @@ dependencies {
optional(project(":spring-tx")) // for Quartz support
optional("javax.activation:activation:1.1.1")
optional("javax.mail:javax.mail-api:1.6.0")
optional("javax.cache:cache-api:1.0.0")
optional("com.github.ben-manes.caffeine:caffeine:2.6.0")
optional("javax.cache:cache-api:1.1.0")
optional("com.github.ben-manes.caffeine:caffeine:2.6.1")
optional("net.sf.ehcache:ehcache:2.10.4")
optional("org.quartz-scheduler:quartz:2.3.0")
optional("org.codehaus.fabric3.api:commonj:1.1.0")
@@ -17,7 +17,7 @@ dependencies {
testCompile(project(":spring-context"))
testCompile("org.hsqldb:hsqldb:${hsqldbVersion}")
testCompile("org.slf4j:slf4j-api:${slf4jVersion}")
testCompile("org.hibernate:hibernate-validator:6.0.5.Final")
testCompile("org.hibernate:hibernate-validator:6.0.7.Final")
testRuntime("org.ehcache:jcache:1.0.1")
testRuntime("org.ehcache:ehcache:3.4.0")
testRuntime("org.glassfish:javax.el:3.0.1-b08")
+1 -1
View File
@@ -23,7 +23,7 @@ dependencies {
optional("org.hibernate:hibernate-validator:5.4.2.Final")
optional("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
optional("org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}")
testCompile("org.apache.commons:commons-pool2:2.4.3")
testCompile("org.apache.commons:commons-pool2:2.5.0")
testCompile("org.slf4j:slf4j-api:${slf4jVersion}")
testCompile("javax.inject:javax.inject-tck:1")
testRuntime("javax.xml.bind:jaxb-api:2.3.0")
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@ import org.springframework.lang.Nullable;
* for detailed instructions.
*
* @author Chris Beams
* @author Stephane Nicoll
* @since 3.1
* @see EnableCaching
* @see CachingConfigurerSupport
@@ -69,8 +70,8 @@ public interface CachingConfigurer {
* Return the {@link CacheResolver} bean to use to resolve regular caches for
* annotation-driven cache management. This is an alternative and more powerful
* option of specifying the {@link CacheManager} to use.
* <p>If both a {@link #cacheManager()} and {@link #cacheResolver()} are set, the
* cache manager is ignored.
* <p>If both a {@link #cacheManager()} and {@code #cacheResolver()} are set,
* the cache manager is ignored.
* <p>Implementations must explicitly declare
* {@link org.springframework.context.annotation.Bean @Bean}, e.g.
* <pre class="code">
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -51,22 +51,20 @@ import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Base class for caching aspects, such as the {@link CacheInterceptor}
* or an AspectJ aspect.
* Base class for caching aspects, such as the {@link CacheInterceptor} or an
* AspectJ aspect.
*
* <p>This enables the underlying Spring caching infrastructure to be
* used easily to implement an aspect for any aspect system.
* <p>This enables the underlying Spring caching infrastructure to be used easily
* to implement an aspect for any aspect system.
*
* <p>Subclasses are responsible for calling methods in this class in
* the correct order.
* <p>Subclasses are responsible for calling relevant methods in the correct order.
*
* <p>Uses the <b>Strategy</b> design pattern. A {@link CacheResolver}
* implementation will resolve the actual cache(s) to use, and a
* {@link CacheOperationSource} is used for determining caching
* operations.
* <p>Uses the <b>Strategy</b> design pattern. A {@link CacheOperationSource} is
* used for determining caching operations, a {@link KeyGenerator} will build the
* cache keys, and a {@link CacheResolver} will resolve the actual cache(s) to use.
*
* <p>A cache aspect is serializable if its {@code CacheResolver} and
* {@code CacheOperationSource} are serializable.
* <p>Note: A cache aspect is serializable but does not perform any actual caching
* after deserialization.
*
* @author Costin Leau
* @author Juergen Hoeller
@@ -121,7 +119,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
/**
* Set the default {@link KeyGenerator} that this cache aspect should delegate to
* if no specific key generator has been set for the operation.
* <p>The default is a {@link SimpleKeyGenerator}
* <p>The default is a {@link SimpleKeyGenerator}.
*/
public void setKeyGenerator(KeyGenerator keyGenerator) {
this.keyGenerator = keyGenerator;
@@ -134,22 +132,12 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
return this.keyGenerator;
}
/**
* Set the {@link CacheManager} to use to create a default {@link CacheResolver}.
* Replace the current {@link CacheResolver}, if any.
* @see #setCacheResolver(CacheResolver)
* @see SimpleCacheResolver
*/
public void setCacheManager(CacheManager cacheManager) {
this.cacheResolver = new SimpleCacheResolver(cacheManager);
}
/**
* Set the default {@link CacheResolver} that this cache aspect should delegate
* to if no specific cache resolver has been set for the operation.
* <p>The default resolver resolves the caches against their names and the
* default cache manager.
* @see #setCacheManager(org.springframework.cache.CacheManager)
* @see #setCacheManager
* @see SimpleCacheResolver
*/
public void setCacheResolver(@Nullable CacheResolver cacheResolver) {
@@ -164,6 +152,16 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
return this.cacheResolver;
}
/**
* Set the {@link CacheManager} to use to create a default {@link CacheResolver}.
* Replace the current {@link CacheResolver}, if any.
* @see #setCacheResolver
* @see SimpleCacheResolver
*/
public void setCacheManager(CacheManager cacheManager) {
this.cacheResolver = new SimpleCacheResolver(cacheManager);
}
/**
* Set the containing {@link BeanFactory} for {@link CacheManager} and other
* service lookups.
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,10 @@ package org.springframework.cache.interceptor;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.AbstractSingletonProxyFactoryBean;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.cache.CacheManager;
/**
* Proxy factory bean for simplified declarative caching handling.
@@ -41,24 +45,59 @@ import org.springframework.aop.support.DefaultPointcutAdvisor;
* @see CacheInterceptor
*/
@SuppressWarnings("serial")
public class CacheProxyFactoryBean extends AbstractSingletonProxyFactoryBean {
public class CacheProxyFactoryBean extends AbstractSingletonProxyFactoryBean
implements BeanFactoryAware, SmartInitializingSingleton {
private final CacheInterceptor cachingInterceptor = new CacheInterceptor();
private final CacheInterceptor cacheInterceptor = new CacheInterceptor();
private Pointcut pointcut = Pointcut.TRUE;
/**
* Set the sources used to find cache operations.
* Set one or more sources to find cache operations.
* @see CacheInterceptor#setCacheOperationSources
*/
public void setCacheOperationSources(CacheOperationSource... cacheOperationSources) {
this.cachingInterceptor.setCacheOperationSources(cacheOperationSources);
this.cacheInterceptor.setCacheOperationSources(cacheOperationSources);
}
/**
* Set a pointcut, i.e a bean that can cause conditional invocation
* of the CacheInterceptor depending on method and attributes passed.
* Note: Additional interceptors are always invoked.
* Set the default {@link KeyGenerator} that this cache aspect should delegate to
* if no specific key generator has been set for the operation.
* <p>The default is a {@link SimpleKeyGenerator}.
* @since 5.0.3
* @see CacheInterceptor#setKeyGenerator
*/
public void setKeyGenerator(KeyGenerator keyGenerator) {
this.cacheInterceptor.setKeyGenerator(keyGenerator);
}
/**
* Set the default {@link CacheResolver} that this cache aspect should delegate
* to if no specific cache resolver has been set for the operation.
* <p>The default resolver resolves the caches against their names and the
* default cache manager.
* @since 5.0.3
* @see CacheInterceptor#setCacheResolver
*/
public void setCacheResolver(CacheResolver cacheResolver) {
this.cacheInterceptor.setCacheResolver(cacheResolver);
}
/**
* Set the {@link CacheManager} to use to create a default {@link CacheResolver}.
* Replace the current {@link CacheResolver}, if any.
* @since 5.0.3
* @see CacheInterceptor#setCacheManager
*/
public void setCacheManager(CacheManager cacheManager) {
this.cacheInterceptor.setCacheManager(cacheManager);
}
/**
* Set a pointcut, i.e. a bean that triggers conditional invocation of the
* {@link CacheInterceptor} depending on the method and attributes passed.
* <p>Note: Additional interceptors are always invoked.
* @see #setPreInterceptors
* @see #setPostInterceptors
*/
@@ -66,11 +105,21 @@ public class CacheProxyFactoryBean extends AbstractSingletonProxyFactoryBean {
this.pointcut = pointcut;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.cacheInterceptor.setBeanFactory(beanFactory);
}
@Override
public void afterSingletonsInstantiated() {
this.cacheInterceptor.afterSingletonsInstantiated();
}
@Override
protected Object createMainInterceptor() {
this.cachingInterceptor.afterPropertiesSet();
return new DefaultPointcutAdvisor(this.pointcut, this.cachingInterceptor);
this.cacheInterceptor.afterPropertiesSet();
return new DefaultPointcutAdvisor(this.pointcut, this.cacheInterceptor);
}
}
@@ -78,7 +78,7 @@ import org.springframework.core.annotation.AliasFor;
* </pre>
*
* The semantics of the above-mentioned annotations match their use at the component
* class level: {@code Profile} allows for selective inclusion of certain beans.
* class level: {@code @Profile} allows for selective inclusion of certain beans.
* {@code @Scope} changes the bean's scope from singleton to the specified scope.
* {@code @Lazy} only has an actual effect in case of the default singleton scope.
* {@code @DependsOn} enforces the creation of specific other beans before this
@@ -96,12 +96,12 @@ import org.springframework.core.annotation.AliasFor;
* order values determine the order of resolved elements in case of collection
* injection points (with several target beans matching by type and qualifier).
*
* <p><b>NOTE:</b> {@code @Order} values may influence priorities at injection points
* <p><b>NOTE:</b> {@code @Order} values may influence priorities at injection points,
* but please be aware that they do not influence singleton startup order which is an
* orthogonal concern determined by dependency relationships and {@code @DependsOn}
* declarations as mentioned above. Also, {@link javax.annotation.Priority} is not
* available at this level since it cannot be declared on methods; its semantics can
* be modelled through {@code @Order} values in combination with {@code @Primary} on
* be modeled through {@code @Order} values in combination with {@code @Primary} on
* a single bean per type.
*
* <h3>{@code @Bean} Methods in {@code @Configuration} Classes</h3>
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,7 +25,6 @@ import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
@@ -216,6 +215,10 @@ public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
scanner.addExcludeFilter(typeFilter);
}
}
catch (ClassNotFoundException ex) {
parserContext.getReaderContext().warning(
"Ignoring non-present type filter class: " + ex, parserContext.extractSource(element));
}
catch (Exception ex) {
parserContext.getReaderContext().error(
ex.getMessage(), parserContext.extractSource(element), ex.getCause());
@@ -225,39 +228,34 @@ public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
}
@SuppressWarnings("unchecked")
protected TypeFilter createTypeFilter(
Element element, @Nullable ClassLoader classLoader, ParserContext parserContext) {
protected TypeFilter createTypeFilter(Element element, @Nullable ClassLoader classLoader,
ParserContext parserContext) throws ClassNotFoundException {
String filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE);
String expression = element.getAttribute(FILTER_EXPRESSION_ATTRIBUTE);
expression = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(expression);
try {
if ("annotation".equals(filterType)) {
return new AnnotationTypeFilter((Class<Annotation>) ClassUtils.forName(expression, classLoader));
}
else if ("assignable".equals(filterType)) {
return new AssignableTypeFilter(ClassUtils.forName(expression, classLoader));
}
else if ("aspectj".equals(filterType)) {
return new AspectJTypeFilter(expression, classLoader);
}
else if ("regex".equals(filterType)) {
return new RegexPatternTypeFilter(Pattern.compile(expression));
}
else if ("custom".equals(filterType)) {
Class<?> filterClass = ClassUtils.forName(expression, classLoader);
if (!TypeFilter.class.isAssignableFrom(filterClass)) {
throw new IllegalArgumentException(
"Class is not assignable to [" + TypeFilter.class.getName() + "]: " + expression);
}
return (TypeFilter) BeanUtils.instantiateClass(filterClass);
}
else {
throw new IllegalArgumentException("Unsupported filter type: " + filterType);
}
if ("annotation".equals(filterType)) {
return new AnnotationTypeFilter((Class<Annotation>) ClassUtils.forName(expression, classLoader));
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("Type filter class not found: " + expression, ex);
else if ("assignable".equals(filterType)) {
return new AssignableTypeFilter(ClassUtils.forName(expression, classLoader));
}
else if ("aspectj".equals(filterType)) {
return new AspectJTypeFilter(expression, classLoader);
}
else if ("regex".equals(filterType)) {
return new RegexPatternTypeFilter(Pattern.compile(expression));
}
else if ("custom".equals(filterType)) {
Class<?> filterClass = ClassUtils.forName(expression, classLoader);
if (!TypeFilter.class.isAssignableFrom(filterClass)) {
throw new IllegalArgumentException(
"Class is not assignable to [" + TypeFilter.class.getName() + "]: " + expression);
}
return (TypeFilter) BeanUtils.instantiateClass(filterClass);
}
else {
throw new IllegalArgumentException("Unsupported filter type: " + filterType);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -712,8 +712,7 @@ class ConfigurationClassParser {
@Override
@Nullable
public AnnotationMetadata getImportingClassFor(String importedClass) {
List<AnnotationMetadata> list = this.imports.get(importedClass);
return (!CollectionUtils.isEmpty(list) ? list.get(list.size() - 1) : null);
return CollectionUtils.lastElement(this.imports.get(importedClass));
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -285,9 +285,9 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
if ((getBeanFactory().containsSingleton(beanNameToRegister) &&
(!isFactoryBean || matchesBeanType(Lifecycle.class, beanNameToCheck))) ||
matchesBeanType(SmartLifecycle.class, beanNameToCheck)) {
Lifecycle bean = getBeanFactory().getBean(beanNameToCheck, Lifecycle.class);
if (bean != this) {
beans.put(beanNameToRegister, bean);
Object bean = getBeanFactory().getBean(beanNameToCheck);
if (bean != this && bean instanceof Lifecycle) {
beans.put(beanNameToRegister, (Lifecycle) bean);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -185,7 +185,7 @@ public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerS
/**
* Implemented for compatibility with {@link org.springframework.beans.factory.config.PlaceholderConfigurerSupport}.
* @deprecated in favor of {@link #processProperties(ConfigurableListableBeanFactory, ConfigurablePropertyResolver)}
* @throws UnsupportedOperationException
* @throws UnsupportedOperationException in this implementation
*/
@Override
@Deprecated
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -85,7 +85,8 @@ import org.springframework.util.StringUtils;
* @see ResourceBundleMessageSource
* @see java.util.ResourceBundle
*/
public class ReloadableResourceBundleMessageSource extends AbstractResourceBasedMessageSource implements ResourceLoaderAware {
public class ReloadableResourceBundleMessageSource extends AbstractResourceBasedMessageSource
implements ResourceLoaderAware {
private static final String PROPERTIES_SUFFIX = ".properties";
@@ -101,17 +102,14 @@ public class ReloadableResourceBundleMessageSource extends AbstractResourceBased
private ResourceLoader resourceLoader = new DefaultResourceLoader();
/** Cache to hold filename lists per Locale */
private final ConcurrentMap<String, Map<Locale, List<String>>> cachedFilenames =
new ConcurrentHashMap<>();
// Cache to hold filename lists per Locale
private final ConcurrentMap<String, Map<Locale, List<String>>> cachedFilenames = new ConcurrentHashMap<>();
/** Cache to hold already loaded properties per filename */
private final ConcurrentMap<String, PropertiesHolder> cachedProperties =
new ConcurrentHashMap<>();
// Cache to hold already loaded properties per filename
private final ConcurrentMap<String, PropertiesHolder> cachedProperties = new ConcurrentHashMap<>();
/** Cache to hold merged loaded properties per locale */
private final ConcurrentMap<Locale, PropertiesHolder> cachedMergedProperties =
new ConcurrentHashMap<>();
// Cache to hold already loaded properties per filename
private final ConcurrentMap<Locale, PropertiesHolder> cachedMergedProperties = new ConcurrentHashMap<>();
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@ import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.lang.Nullable;
@@ -80,7 +81,7 @@ public class ResourceBundleMessageSource extends AbstractResourceBasedMessageSou
* This allows for very efficient hash lookups, significantly faster
* than the ResourceBundle class's own cache.
*/
private final Map<String, Map<Locale, ResourceBundle>> cachedResourceBundles = new HashMap<>();
private final Map<String, Map<Locale, ResourceBundle>> cachedResourceBundles = new ConcurrentHashMap<>();
/**
* Cache to hold already generated MessageFormats.
@@ -90,7 +91,7 @@ public class ResourceBundleMessageSource extends AbstractResourceBasedMessageSou
* very efficient hash lookups without concatenated keys.
* @see #getMessageFormat
*/
private final Map<ResourceBundle, Map<String, Map<Locale, MessageFormat>>> cachedBundleMessageFormats = new HashMap<>();
private final Map<ResourceBundle, Map<String, Map<Locale, MessageFormat>>> cachedBundleMessageFormats = new ConcurrentHashMap<>();
/**
@@ -178,31 +179,32 @@ public class ResourceBundleMessageSource extends AbstractResourceBasedMessageSou
}
else {
// Cache forever: prefer locale cache over repeated getBundle calls.
synchronized (this.cachedResourceBundles) {
Map<Locale, ResourceBundle> localeMap = this.cachedResourceBundles.get(basename);
if (localeMap != null) {
ResourceBundle bundle = localeMap.get(locale);
if (bundle != null) {
return bundle;
}
}
try {
ResourceBundle bundle = doGetBundle(basename, locale);
if (localeMap == null) {
localeMap = new HashMap<>();
this.cachedResourceBundles.put(basename, localeMap);
}
localeMap.put(locale, bundle);
Map<Locale, ResourceBundle> localeMap = this.cachedResourceBundles.get(basename);
if (localeMap != null) {
ResourceBundle bundle = localeMap.get(locale);
if (bundle != null) {
return bundle;
}
catch (MissingResourceException ex) {
if (logger.isWarnEnabled()) {
logger.warn("ResourceBundle [" + basename + "] not found for MessageSource: " + ex.getMessage());
}
try {
ResourceBundle bundle = doGetBundle(basename, locale);
if (localeMap == null) {
localeMap = new ConcurrentHashMap<>();
Map<Locale, ResourceBundle> existing = this.cachedResourceBundles.putIfAbsent(basename, localeMap);
if (existing != null) {
localeMap = existing;
}
// Assume bundle not found
// -> do NOT throw the exception to allow for checking parent message source.
return null;
}
localeMap.put(locale, bundle);
return bundle;
}
catch (MissingResourceException ex) {
if (logger.isWarnEnabled()) {
logger.warn("ResourceBundle [" + basename + "] not found for MessageSource: " + ex.getMessage());
}
// Assume bundle not found
// -> do NOT throw the exception to allow for checking parent message source.
return null;
}
}
}
@@ -249,36 +251,40 @@ public class ResourceBundleMessageSource extends AbstractResourceBasedMessageSou
protected MessageFormat getMessageFormat(ResourceBundle bundle, String code, Locale locale)
throws MissingResourceException {
synchronized (this.cachedBundleMessageFormats) {
Map<String, Map<Locale, MessageFormat>> codeMap = this.cachedBundleMessageFormats.get(bundle);
Map<Locale, MessageFormat> localeMap = null;
if (codeMap != null) {
localeMap = codeMap.get(code);
if (localeMap != null) {
MessageFormat result = localeMap.get(locale);
if (result != null) {
return result;
}
Map<String, Map<Locale, MessageFormat>> codeMap = this.cachedBundleMessageFormats.get(bundle);
Map<Locale, MessageFormat> localeMap = null;
if (codeMap != null) {
localeMap = codeMap.get(code);
if (localeMap != null) {
MessageFormat result = localeMap.get(locale);
if (result != null) {
return result;
}
}
String msg = getStringOrNull(bundle, code);
if (msg != null) {
if (codeMap == null) {
codeMap = new HashMap<>();
this.cachedBundleMessageFormats.put(bundle, codeMap);
}
if (localeMap == null) {
localeMap = new HashMap<>();
codeMap.put(code, localeMap);
}
MessageFormat result = createMessageFormat(msg, locale);
localeMap.put(locale, result);
return result;
}
return null;
}
String msg = getStringOrNull(bundle, code);
if (msg != null) {
if (codeMap == null) {
codeMap = new ConcurrentHashMap<>();
Map<String, Map<Locale, MessageFormat>> existing = this.cachedBundleMessageFormats.putIfAbsent(bundle, codeMap);
if (existing != null) {
codeMap = existing;
}
}
if (localeMap == null) {
localeMap = new ConcurrentHashMap<>();
Map<Locale, MessageFormat> existing = codeMap.putIfAbsent(code, localeMap);
if (existing != null) {
localeMap = existing;
}
}
MessageFormat result = createMessageFormat(msg, locale);
localeMap.put(locale, result);
return result;
}
return null;
}
/**
@@ -394,7 +400,9 @@ public class ResourceBundleMessageSource extends AbstractResourceBasedMessageSou
}
@Override
public boolean needsReload(String baseName, Locale locale, String format, ClassLoader loader, ResourceBundle bundle, long loadTime) {
public boolean needsReload(
String baseName, Locale locale, String format, ClassLoader loader, ResourceBundle bundle, long loadTime) {
if (super.needsReload(baseName, locale, format, loader, bundle, loadTime)) {
cachedBundleMessageFormats.remove(bundle);
return true;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,7 +47,7 @@ import org.springframework.util.Assert;
* appropriate weaver implementation: As of Spring Framework 5.0, it detects
* Oracle WebLogic 10+, GlassFish 4+, Tomcat 8+, WildFly 8+, IBM WebSphere 8.5+,
* {@link InstrumentationSavingAgent Spring's VM agent}, and any {@link ClassLoader}
* supported by Spring's {@link ReflectiveLoadTimeWeaver}.
* supported by Spring's {@link ReflectiveLoadTimeWeaver} (such as Liberty's).
*
* @author Juergen Hoeller
* @author Ramnivas Laddad
@@ -104,32 +104,29 @@ public class DefaultContextLoadTimeWeaver implements LoadTimeWeaver, BeanClassLo
* This method never fails, allowing to try other possible ways to use an
* server-agnostic weaver. This non-failure logic is required since
* determining a load-time weaver based on the ClassLoader name alone may
* legitimately fail due to other mismatches. Specific case in point: the
* use of WebLogicLoadTimeWeaver works for WLS 10 but fails due to the lack
* of a specific method (addInstanceClassPreProcessor) for any earlier
* versions even though the ClassLoader name is the same.
* legitimately fail due to other mismatches.
*/
@Nullable
protected LoadTimeWeaver createServerSpecificLoadTimeWeaver(ClassLoader classLoader) {
String name = classLoader.getClass().getName();
try {
if (name.startsWith("weblogic")) {
return new WebLogicLoadTimeWeaver(classLoader);
if (name.startsWith("org.apache.catalina")) {
return new TomcatLoadTimeWeaver(classLoader);
}
else if (name.startsWith("org.glassfish")) {
return new GlassFishLoadTimeWeaver(classLoader);
}
else if (name.startsWith("org.apache.catalina")) {
return new TomcatLoadTimeWeaver(classLoader);
}
else if (name.startsWith("org.jboss")) {
else if (name.startsWith("org.jboss.modules")) {
return new JBossLoadTimeWeaver(classLoader);
}
else if (name.startsWith("com.ibm")) {
else if (name.startsWith("com.ibm.ws.classloader")) {
return new WebSphereLoadTimeWeaver(classLoader);
}
else if (name.startsWith("weblogic")) {
return new WebLogicLoadTimeWeaver(classLoader);
}
}
catch (IllegalStateException ex) {
catch (Exception ex) {
if (logger.isInfoEnabled()) {
logger.info("Could not obtain server-specific LoadTimeWeaver: " + ex.getMessage());
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -70,6 +70,7 @@ class WebLogicClassLoaderAdapter {
throw new IllegalStateException(
"Could not initialize WebLogic LoadTimeWeaver because WebLogic 10 API classes are not available", ex);
}
if (!wlGenericClassLoaderClass.isInstance(classLoader)) {
throw new IllegalArgumentException(
"ClassLoader must be an instance of [" + wlGenericClassLoaderClass.getName() + "]: " + classLoader);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -74,7 +74,8 @@ class WebSphereClassLoaderAdapter {
}
if (!wsCompoundClassLoaderClass.isInstance(classLoader)) {
throw new IllegalArgumentException("ClassLoader must be instance of " + COMPOUND_CLASS_LOADER_NAME);
throw new IllegalArgumentException(
"ClassLoader must be an instance of [" + COMPOUND_CLASS_LOADER_NAME + "]: " + classLoader);
}
this.classLoader = classLoader;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,6 +32,7 @@ import java.util.concurrent.ScheduledExecutorService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
@@ -311,7 +312,7 @@ public class ScheduledAnnotationBeanPostProcessor
@Override
public Object postProcessAfterInitialization(final Object bean, String beanName) {
Class<?> targetClass = AopUtils.getTargetClass(bean);
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
if (!this.nonAnnotatedClasses.contains(targetClass)) {
Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
(MethodIntrospector.MetadataLookup<Set<Scheduled>>) method -> {
@@ -365,7 +366,7 @@ public class ScheduledAnnotationBeanPostProcessor
}
catch (NumberFormatException ex) {
throw new IllegalArgumentException(
"Invalid initialDelayString value \"" + initialDelayString + "\" - cannot parse into integer");
"Invalid initialDelayString value \"" + initialDelayString + "\" - cannot parse into long");
}
}
}
@@ -443,7 +444,7 @@ public class ScheduledAnnotationBeanPostProcessor
}
catch (NumberFormatException ex) {
throw new IllegalArgumentException(
"Invalid fixedRateString value \"" + fixedRateString + "\" - cannot parse into integer");
"Invalid fixedRateString value \"" + fixedRateString + "\" - cannot parse into long");
}
tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay)));
}
@@ -0,0 +1,129 @@
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Test;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.junit.Assert.*;
/**
* Integration tests for {@link CacheProxyFactoryBean}.
*
* @author John Blum
* @author Juergen Hoeller
*/
public class CacheProxyFactoryBeanTests {
@Test
public void configurationClassWithCacheProxyFactoryBean() {
try (AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(CacheProxyFactoryBeanConfiguration.class)) {
Greeter greeter = applicationContext.getBean("greeter", Greeter.class);
assertNotNull(greeter);
assertFalse(greeter.isCacheMiss());
assertEquals("Hello John!", greeter.greet("John"));
assertTrue(greeter.isCacheMiss());
assertEquals("Hello Jon!", greeter.greet("Jon"));
assertTrue(greeter.isCacheMiss());
assertEquals("Hello John!", greeter.greet("John"));
assertFalse(greeter.isCacheMiss());
assertEquals("Hello World!", greeter.greet());
assertTrue(greeter.isCacheMiss());
assertEquals("Hello World!", greeter.greet());
assertFalse(greeter.isCacheMiss());
}
}
@Configuration
@EnableCaching
static class CacheProxyFactoryBeanConfiguration {
@Bean
ConcurrentMapCacheManager cacheManager() {
return new ConcurrentMapCacheManager("Greetings");
}
@Bean
CacheProxyFactoryBean greeter() {
CacheProxyFactoryBean factoryBean = new CacheProxyFactoryBean();
factoryBean.setCacheOperationSources(newCacheOperationSource("greet", newCacheOperation("Greetings")));
factoryBean.setTarget(new SimpleGreeter());
return factoryBean;
}
CacheOperationSource newCacheOperationSource(String methodName, CacheOperation... cacheOperations) {
NameMatchCacheOperationSource cacheOperationSource = new NameMatchCacheOperationSource();
cacheOperationSource.addCacheMethod(methodName, Arrays.asList(cacheOperations));
return cacheOperationSource;
}
CacheableOperation newCacheOperation(String cacheName) {
CacheableOperation.Builder builder = new CacheableOperation.Builder();
builder.setCacheManager("cacheManager");
builder.setCacheName(cacheName);
return builder.build();
}
}
interface Greeter {
default boolean isCacheHit() {
return !isCacheMiss();
}
boolean isCacheMiss();
void setCacheMiss();
default String greet() {
return greet("World");
}
default String greet(String name) {
setCacheMiss();
return String.format("Hello %s!", name);
}
}
static class SimpleGreeter implements Greeter {
private final AtomicBoolean cacheMiss = new AtomicBoolean(false);
@Override
public boolean isCacheMiss() {
return this.cacheMiss.getAndSet(false);
}
@Override
public void setCacheMiss() {
this.cacheMiss.set(true);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -1132,6 +1132,11 @@ public class ConfigurationClassPostProcessorTests {
public RepositoryFactoryBean<Object> repoFactoryBean() {
return new RepositoryFactoryBean<>();
}
@Bean
public FactoryBean<Object> nullFactoryBean() {
return null;
}
}
@Configuration
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@ import java.util.TimeZone;
import org.junit.After;
import org.junit.Test;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.BeanDefinition;
@@ -179,6 +180,14 @@ public class ScheduledAnnotationBeanPostProcessorTests {
severalFixedRates(context, processorDefinition, targetDefinition);
}
@Test
public void severalFixedRatesAgainstNestedCglibProxy() {
BeanDefinition processorDefinition = new RootBeanDefinition(ScheduledAnnotationBeanPostProcessor.class);
BeanDefinition targetDefinition = new RootBeanDefinition(SeveralFixedRatesWithRepeatedScheduledAnnotationTestBean.class);
targetDefinition.setFactoryMethodName("nestedProxy");
severalFixedRates(context, processorDefinition, targetDefinition);
}
private void severalFixedRates(StaticApplicationContext context,
BeanDefinition processorDefinition, BeanDefinition targetDefinition) {
@@ -631,6 +640,14 @@ public class ScheduledAnnotationBeanPostProcessorTests {
@Scheduled(fixedRate = 4000, initialDelay = 2000)
public void fixedRate() {
}
static SeveralFixedRatesWithRepeatedScheduledAnnotationTestBean nestedProxy() {
ProxyFactory pf1 = new ProxyFactory(new SeveralFixedRatesWithRepeatedScheduledAnnotationTestBean());
pf1.setProxyTargetClass(true);
ProxyFactory pf2 = new ProxyFactory(pf1.getProxy());
pf2.setProxyTargetClass(true);
return (SeveralFixedRatesWithRepeatedScheduledAnnotationTestBean) pf2.getProxy();
}
}
+1 -1
View File
@@ -17,7 +17,7 @@ dependencyManagement {
// both into the spring-core jar. cglib 3.2 itself depends on asm 5.x and is therefore
// further transformed by the JarJar task to depend on org.springframework.asm; this
// avoids including two different copies of asm unnecessarily.
def cglibVersion = "3.2.5"
def cglibVersion = "3.2.6"
def objenesisVersion = "2.6"
configurations {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -196,7 +196,10 @@ public abstract class BridgeMethodResolver {
return method;
}
else {
return searchInterfaces(ifc.getInterfaces(), bridgeMethod);
method = searchInterfaces(ifc.getInterfaces(), bridgeMethod);
if (method != null) {
return method;
}
}
}
return null;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,16 +16,10 @@
package org.springframework.core;
import java.io.Externalizable;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -47,19 +41,7 @@ public abstract class Conventions {
*/
private static final String PLURAL_SUFFIX = "List";
/**
* Set of interfaces that are supposed to be ignored
* when searching for the 'primary' interface of a proxy.
*/
private static final Set<Class<?>> IGNORED_INTERFACES;
static {
IGNORED_INTERFACES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
Serializable.class, Externalizable.class, Cloneable.class, Comparable.class)));
}
private static final ReactiveAdapterRegistry reactiveAdapterRegistry =
ReactiveAdapterRegistry.getSharedInstance();
private static final ReactiveAdapterRegistry reactiveAdapterRegistry = ReactiveAdapterRegistry.getSharedInstance();
/**
@@ -67,16 +49,13 @@ public abstract class Conventions {
* based on its concrete type. The convention used is to return the
* un-capitalized short name of the {@code Class}, according to JavaBeans
* property naming rules.
*
* <p>For example:<br>
* {@code com.myapp.Product} becomes {@code "product"}<br>
* {@code com.myapp.MyProduct} becomes {@code "myProduct"}<br>
* {@code com.myapp.UKProduct} becomes {@code "UKProduct"}<br>
*
* <p>For arrays the pluralized version of the array component type is used.
* For {@code Collection}s an attempt is made to 'peek ahead' to determine
* the component type and return its pluralized version.
*
* @param value the value to generate a variable name for
* @return the generated variable name
*/
@@ -110,12 +89,10 @@ public abstract class Conventions {
/**
* Determine the conventional variable name for the given parameter taking
* the generic collection type, if any, into account.
*
* <p>As of 5.0 this method supports reactive types:<br>
* {@code Mono<com.myapp.Product>} becomes {@code "productMono"}<br>
* {@code Flux<com.myapp.MyProduct>} becomes {@code "myProductFlux"}<br>
* {@code Observable<com.myapp.MyProduct>} becomes {@code "myProductObservable"}<br>
*
* @param parameter the method or constructor parameter
* @return the generated variable name
*/
@@ -295,7 +272,7 @@ public abstract class Conventions {
if (Proxy.isProxyClass(valueClass)) {
Class<?>[] ifcs = valueClass.getInterfaces();
for (Class<?> ifc : ifcs) {
if (!IGNORED_INTERFACES.contains(ifc)) {
if (!ClassUtils.isJavaLanguageInterface(ifc)) {
return ifc;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -117,36 +117,35 @@ public class ResolvableType implements Serializable {
@Nullable
private final ResolvableType componentType;
/**
* Copy of the resolved value.
*/
@Nullable
private final Class<?> resolved;
@Nullable
private final Integer hash;
@Nullable
private ResolvableType superType;
private Class<?> resolved;
@Nullable
private ResolvableType[] interfaces;
private volatile ResolvableType superType;
@Nullable
private ResolvableType[] generics;
private volatile ResolvableType[] interfaces;
@Nullable
private volatile ResolvableType[] generics;
/**
* Private constructor used to create a new {@link ResolvableType} for cache key purposes,
* with no upfront resolution.
*/
private ResolvableType(Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) {
private ResolvableType(
Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) {
this.type = type;
this.typeProvider = typeProvider;
this.variableResolver = variableResolver;
this.componentType = null;
this.resolved = null;
this.hash = calculateHashCode();
this.resolved = null;
}
/**
@@ -161,8 +160,8 @@ public class ResolvableType implements Serializable {
this.typeProvider = typeProvider;
this.variableResolver = variableResolver;
this.componentType = null;
this.resolved = resolveClass();
this.hash = hash;
this.resolved = resolveClass();
}
/**
@@ -176,8 +175,8 @@ public class ResolvableType implements Serializable {
this.typeProvider = typeProvider;
this.variableResolver = variableResolver;
this.componentType = componentType;
this.resolved = resolveClass();
this.hash = null;
this.resolved = resolveClass();
}
/**
@@ -453,10 +452,12 @@ public class ResolvableType implements Serializable {
if (resolved == null || resolved.getGenericSuperclass() == null) {
return NONE;
}
if (this.superType == null) {
this.superType = forType(SerializableTypeWrapper.forGenericSuperclass(resolved), asVariableResolver());
ResolvableType superType = this.superType;
if (superType == null) {
superType = forType(SerializableTypeWrapper.forGenericSuperclass(resolved), asVariableResolver());
this.superType = superType;
}
return this.superType;
return superType;
}
/**
@@ -470,10 +471,12 @@ public class ResolvableType implements Serializable {
if (resolved == null || ObjectUtils.isEmpty(resolved.getGenericInterfaces())) {
return EMPTY_TYPES_ARRAY;
}
if (this.interfaces == null) {
this.interfaces = forTypes(SerializableTypeWrapper.forGenericInterfaces(resolved), asVariableResolver());
ResolvableType[] interfaces = this.interfaces;
if (interfaces == null) {
interfaces = forTypes(SerializableTypeWrapper.forGenericInterfaces(resolved), asVariableResolver());
this.interfaces = interfaces;
}
return this.interfaces;
return interfaces;
}
/**
@@ -667,24 +670,25 @@ public class ResolvableType implements Serializable {
if (this == NONE) {
return EMPTY_TYPES_ARRAY;
}
if (this.generics == null) {
ResolvableType[] generics = this.generics;
if (generics == null) {
if (this.type instanceof Class) {
Class<?> typeClass = (Class<?>) this.type;
this.generics = forTypes(SerializableTypeWrapper.forTypeParameters(typeClass), this.variableResolver);
generics = forTypes(SerializableTypeWrapper.forTypeParameters(typeClass), this.variableResolver);
}
else if (this.type instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) this.type).getActualTypeArguments();
ResolvableType[] generics = new ResolvableType[actualTypeArguments.length];
generics = new ResolvableType[actualTypeArguments.length];
for (int i = 0; i < actualTypeArguments.length; i++) {
generics[i] = forType(actualTypeArguments[i], this.variableResolver);
}
this.generics = generics;
}
else {
this.generics = resolveType().getGenerics();
generics = resolveType().getGenerics();
}
this.generics = generics;
}
return this.generics;
return generics;
}
/**
@@ -748,7 +752,7 @@ public class ResolvableType implements Serializable {
*/
@Nullable
public Class<?> resolve() {
return (this.resolved != null ? this.resolved : null);
return this.resolved;
}
/**
@@ -1372,7 +1376,9 @@ public class ResolvableType implements Serializable {
* @param variableResolver the variable resolver or {@code null}
* @return a {@link ResolvableType} for the specified {@link Type} and {@link VariableResolver}
*/
static ResolvableType forType(@Nullable Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) {
static ResolvableType forType(
@Nullable Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) {
if (type == null && typeProvider != null) {
type = SerializableTypeWrapper.forTypeProvider(typeProvider);
}
@@ -1390,13 +1396,14 @@ public class ResolvableType implements Serializable {
cache.purgeUnreferencedEntries();
// Check the cache - we may have a ResolvableType which has been resolved before...
ResolvableType key = new ResolvableType(type, typeProvider, variableResolver);
ResolvableType resolvableType = cache.get(key);
if (resolvableType == null) {
resolvableType = new ResolvableType(type, typeProvider, variableResolver, key.hash);
cache.put(resolvableType, resolvableType);
ResolvableType resultType = new ResolvableType(type, typeProvider, variableResolver);
ResolvableType cachedType = cache.get(resultType);
if (cachedType == null) {
cachedType = new ResolvableType(type, typeProvider, variableResolver, resultType.hash);
cache.put(cachedType, cachedType);
}
return resolvableType;
resultType.resolved = cachedType.resolved;
return resultType;
}
/**
@@ -42,7 +42,7 @@ import org.springframework.core.Ordered;
*
* <p>Since Spring 4.1, the standard {@link javax.annotation.Priority} annotation
* can be used as a drop-in replacement for this annotation in ordering scenarios.
* Note that {@code Priority} may have additional semantics when a single element
* Note that {@code @Priority} may have additional semantics when a single element
* has to be picked (see {@link AnnotationAwareOrderComparator#getPriority}).
*
* <p>Alternatively, order values may also be determined on a per-instance basis
@@ -0,0 +1,76 @@
/*
* Copyright 2002-2018 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.core.codec;
import java.util.Map;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.MimeType;
/**
* Abstract base class for {@code Decoder} implementations that can decode
* a {@code DataBuffer} directly to the target element type.
*
* <p>Sub-classes must implement {@link #decodeDataBuffer} to provide a way to
* transform a {@code DataBuffer} to the target data type. The default
* {@link #decode} implementation transforms each individual data buffer while
* {@link #decodeToMono} applies "reduce" and transforms the aggregated buffer.
*
* <p>Sub-classes can override {@link #decode} in order to split the input stream
* along different boundaries (e.g. on new line characters for {@code String})
* or always reduce to a single data buffer (e.g. {@code Resource}).
*
* @author Rossen Stoyanchev
* @since 5.0
*/
public abstract class AbstractDataBufferDecoder<T> extends AbstractDecoder<T> {
protected AbstractDataBufferDecoder(MimeType... supportedMimeTypes) {
super(supportedMimeTypes);
}
@Override
public Flux<T> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
return Flux.from(inputStream).map(buffer -> decodeDataBuffer(buffer, elementType, mimeType, hints));
}
@Override
public Mono<T> decodeToMono(Publisher<DataBuffer> inputStream, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
return DataBufferUtils.join(inputStream)
.map(buffer -> decodeDataBuffer(buffer, elementType, mimeType, hints));
}
/**
* How to decode a {@code DataBuffer} to the target element type.
*/
protected abstract T decodeDataBuffer(DataBuffer buffer, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints);
}
@@ -18,9 +18,6 @@ package org.springframework.core.codec;
import java.util.Map;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
@@ -32,9 +29,11 @@ import org.springframework.util.MimeTypeUtils;
* Decoder for {@code byte} arrays.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 5.0
*/
public class ByteArrayDecoder extends AbstractDecoder<byte[]> {
public class ByteArrayDecoder extends AbstractDataBufferDecoder<byte[]> {
public ByteArrayDecoder() {
super(MimeTypeUtils.ALL);
@@ -48,16 +47,13 @@ public class ByteArrayDecoder extends AbstractDecoder<byte[]> {
}
@Override
public Flux<byte[]> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType,
protected byte[] decodeDataBuffer(DataBuffer dataBuffer, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
return Flux.from(inputStream).map((dataBuffer) -> {
byte[] result = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(result);
DataBufferUtils.release(dataBuffer);
return result ;
});
byte[] result = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(result);
DataBufferUtils.release(dataBuffer);
return result;
}
}
@@ -19,9 +19,6 @@ package org.springframework.core.codec;
import java.nio.ByteBuffer;
import java.util.Map;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
@@ -34,9 +31,11 @@ import org.springframework.util.MimeTypeUtils;
*
* @author Sebastien Deleuze
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 5.0
*/
public class ByteBufferDecoder extends AbstractDecoder<ByteBuffer> {
public class ByteBufferDecoder extends AbstractDataBufferDecoder<ByteBuffer> {
public ByteBufferDecoder() {
super(MimeTypeUtils.ALL);
@@ -50,16 +49,14 @@ public class ByteBufferDecoder extends AbstractDecoder<ByteBuffer> {
}
@Override
public Flux<ByteBuffer> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType,
protected ByteBuffer decodeDataBuffer(DataBuffer dataBuffer, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
return Flux.from(inputStream).map((dataBuffer) -> {
ByteBuffer copy = ByteBuffer.allocate(dataBuffer.readableByteCount());
copy.put(dataBuffer.asByteBuffer());
copy.flip();
DataBufferUtils.release(dataBuffer);
return copy;
});
ByteBuffer copy = ByteBuffer.allocate(dataBuffer.readableByteCount());
copy.put(dataBuffer.asByteBuffer());
copy.flip();
DataBufferUtils.release(dataBuffer);
return copy;
}
}
@@ -34,9 +34,11 @@ import org.springframework.util.MimeTypeUtils;
* {@link org.springframework.core.io.buffer.DataBufferUtils#release(DataBuffer)}.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 5.0
*/
public class DataBufferDecoder extends AbstractDecoder<DataBuffer> {
public class DataBufferDecoder extends AbstractDataBufferDecoder<DataBuffer> {
public DataBufferDecoder() {
super(MimeTypeUtils.ALL);
@@ -56,4 +58,11 @@ public class DataBufferDecoder extends AbstractDecoder<DataBuffer> {
return Flux.from(inputStream);
}
@Override
protected DataBuffer decodeDataBuffer(DataBuffer buffer, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
return buffer;
}
}
@@ -21,7 +21,6 @@ import java.util.Map;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.ByteArrayResource;
@@ -38,9 +37,11 @@ import org.springframework.util.MimeTypeUtils;
* Decoder for {@link Resource}s.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 5.0
*/
public class ResourceDecoder extends AbstractDecoder<Resource> {
public class ResourceDecoder extends AbstractDataBufferDecoder<Resource> {
public ResourceDecoder() {
super(MimeTypeUtils.ALL);
@@ -63,30 +64,24 @@ public class ResourceDecoder extends AbstractDecoder<Resource> {
}
@Override
public Mono<Resource> decodeToMono(Publisher<DataBuffer> inputStream, ResolvableType elementType,
protected Resource decodeDataBuffer(DataBuffer dataBuffer, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
DataBufferUtils.release(dataBuffer);
Class<?> clazz = elementType.getRawClass();
Assert.state(clazz != null, "No resource class");
Mono<byte[]> byteArray = Flux.from(inputStream).
reduce(DataBuffer::write).
map(dataBuffer -> {
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
DataBufferUtils.release(dataBuffer);
return bytes;
});
if (InputStreamResource.class == clazz) {
return Mono.from(byteArray.map(ByteArrayInputStream::new).map(InputStreamResource::new));
return new InputStreamResource(new ByteArrayInputStream(bytes));
}
else if (clazz.isAssignableFrom(ByteArrayResource.class)) {
return Mono.from(byteArray.map(ByteArrayResource::new));
return new ByteArrayResource(bytes);
}
else {
return Mono.error(new IllegalStateException("Unsupported resource class: " + clazz));
throw new IllegalStateException("Unsupported resource class: " + clazz);
}
}
@@ -26,7 +26,6 @@ import java.util.function.IntPredicate;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.DataBuffer;
@@ -49,7 +48,7 @@ import org.springframework.util.MimeTypeUtils;
* @since 5.0
* @see CharSequenceEncoder
*/
public class StringDecoder extends AbstractDecoder<String> {
public class StringDecoder extends AbstractDataBufferDecoder<String> {
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
@@ -84,16 +83,7 @@ public class StringDecoder extends AbstractDecoder<String> {
if (this.splitOnNewline) {
inputFlux = Flux.from(inputStream).flatMap(StringDecoder::splitOnNewline);
}
return inputFlux.map(buffer -> decodeDataBuffer(buffer, mimeType));
}
@Override
public Mono<String> decodeToMono(Publisher<DataBuffer> inputStream, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
return Flux.from(inputStream)
.reduce(DataBuffer::write)
.map(buffer -> decodeDataBuffer(buffer, mimeType));
return super.decode(inputFlux, elementType, mimeType, hints);
}
private static Flux<DataBuffer> splitOnNewline(DataBuffer dataBuffer) {
@@ -113,7 +103,10 @@ public class StringDecoder extends AbstractDecoder<String> {
return Flux.fromIterable(results);
}
private String decodeDataBuffer(DataBuffer dataBuffer, @Nullable MimeType mimeType) {
@Override
protected String decodeDataBuffer(DataBuffer dataBuffer, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
Charset charset = getCharset(mimeType);
CharBuffer charBuffer = charset.decode(dataBuffer.asByteBuffer());
DataBufferUtils.release(dataBuffer);
@@ -239,11 +239,9 @@ public final class Property {
field = ReflectionUtils.findField(declaringClass, name);
if (field == null) {
// Same lenient fallback checking as in CachedIntrospectionResults...
field = ReflectionUtils.findField(declaringClass,
name.substring(0, 1).toLowerCase() + name.substring(1));
field = ReflectionUtils.findField(declaringClass, StringUtils.uncapitalize(name));
if (field == null) {
field = ReflectionUtils.findField(declaringClass,
name.substring(0, 1).toUpperCase() + name.substring(1));
field = ReflectionUtils.findField(declaringClass, StringUtils.capitalize(name));
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@ import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
* {@link CommandLinePropertySource} implementation backed by a JOpt {@link OptionSet}.
@@ -86,13 +88,13 @@ public class JOptCommandLinePropertySource extends CommandLinePropertySource<Opt
public String[] getPropertyNames() {
List<String> names = new ArrayList<>();
for (OptionSpec<?> spec : this.source.specs()) {
List<String> aliases = new ArrayList<>(spec.options());
if (!aliases.isEmpty()) {
String lastOption = CollectionUtils.lastElement(spec.options());
if (lastOption != null) {
// Only the longest name is used for enumerating
names.add(aliases.get(aliases.size() - 1));
names.add(lastOption);
}
}
return names.toArray(new String[names.size()]);
return StringUtils.toStringArray(names);
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.NoSuchFileException;
import java.nio.file.StandardOpenOption;
import org.springframework.util.ResourceUtils;
@@ -42,6 +43,68 @@ import org.springframework.util.ResourceUtils;
*/
public abstract class AbstractFileResolvingResource extends AbstractResource {
@Override
public boolean exists() {
try {
URL url = getURL();
if (ResourceUtils.isFileURL(url)) {
// Proceed with file system resolution
return getFile().exists();
}
else {
// Try a URL connection content-length header
URLConnection con = url.openConnection();
customizeConnection(con);
HttpURLConnection httpCon =
(con instanceof HttpURLConnection ? (HttpURLConnection) con : null);
if (httpCon != null) {
int code = httpCon.getResponseCode();
if (code == HttpURLConnection.HTTP_OK) {
return true;
}
else if (code == HttpURLConnection.HTTP_NOT_FOUND) {
return false;
}
}
if (con.getContentLength() >= 0) {
return true;
}
if (httpCon != null) {
// no HTTP OK status, and no content-length header: give up
httpCon.disconnect();
return false;
}
else {
// Fall back to stream existence: can we open the stream?
InputStream is = getInputStream();
is.close();
return true;
}
}
}
catch (IOException ex) {
return false;
}
}
@Override
public boolean isReadable() {
try {
URL url = getURL();
if (ResourceUtils.isFileURL(url)) {
// Proceed with file system resolution
File file = getFile();
return (file.canRead() && !file.isDirectory());
}
else {
return true;
}
}
catch (IOException ex) {
return false;
}
}
@Override
public boolean isFile() {
try {
@@ -123,81 +186,20 @@ public abstract class AbstractFileResolvingResource extends AbstractResource {
* This implementation returns a FileChannel for the given URI-identified
* resource, provided that it refers to a file in the file system.
* @since 5.0
* @see #getFile(URI)
* @see #getFile()
*/
@Override
public ReadableByteChannel readableChannel() throws IOException {
if (isFile()) {
try {
// Try file system channel
return FileChannel.open(getFile().toPath(), StandardOpenOption.READ);
}
else {
catch (FileNotFoundException | NoSuchFileException ex) {
// Fall back to InputStream adaptation in superclass
return super.readableChannel();
}
}
@Override
public boolean exists() {
try {
URL url = getURL();
if (ResourceUtils.isFileURL(url)) {
// Proceed with file system resolution
return getFile().exists();
}
else {
// Try a URL connection content-length header
URLConnection con = url.openConnection();
customizeConnection(con);
HttpURLConnection httpCon =
(con instanceof HttpURLConnection ? (HttpURLConnection) con : null);
if (httpCon != null) {
int code = httpCon.getResponseCode();
if (code == HttpURLConnection.HTTP_OK) {
return true;
}
else if (code == HttpURLConnection.HTTP_NOT_FOUND) {
return false;
}
}
if (con.getContentLength() >= 0) {
return true;
}
if (httpCon != null) {
// no HTTP OK status, and no content-length header: give up
httpCon.disconnect();
return false;
}
else {
// Fall back to stream existence: can we open the stream?
InputStream is = getInputStream();
is.close();
return true;
}
}
}
catch (IOException ex) {
return false;
}
}
@Override
public boolean isReadable() {
try {
URL url = getURL();
if (ResourceUtils.isFileURL(url)) {
// Proceed with file system resolution
File file = getFile();
return (file.canRead() && !file.isDirectory());
}
else {
return true;
}
}
catch (IOException ex) {
return false;
}
}
@Override
public long contentLength() throws IOException {
URL url = getURL();
@@ -231,7 +233,6 @@ public abstract class AbstractFileResolvingResource extends AbstractResource {
return con.getLastModified();
}
/**
* Customize the given {@link URLConnection}, obtained in the course of an
* {@link #exists()}, {@link #contentLength()} or {@link #lastModified()} call.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package org.springframework.core.io;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -26,6 +27,7 @@ import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.StandardOpenOption;
import org.springframework.util.Assert;
@@ -115,12 +117,17 @@ public class FileSystemResource extends AbstractResource implements WritableReso
}
/**
* This implementation opens a FileInputStream for the underlying file.
* This implementation opens a NIO file stream for the underlying file.
* @see java.io.FileInputStream
*/
@Override
public InputStream getInputStream() throws IOException {
return Files.newInputStream(this.file.toPath());
try {
return Files.newInputStream(this.file.toPath());
}
catch (NoSuchFileException ex) {
throw new FileNotFoundException(ex.getMessage());
}
}
/**
@@ -183,7 +190,12 @@ public class FileSystemResource extends AbstractResource implements WritableReso
*/
@Override
public ReadableByteChannel readableChannel() throws IOException {
return FileChannel.open(this.file.toPath(), StandardOpenOption.READ);
try {
return FileChannel.open(this.file.toPath(), StandardOpenOption.READ);
}
catch (NoSuchFileException ex) {
throw new FileNotFoundException(ex.getMessage());
}
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,11 +26,13 @@ import java.nio.channels.WritableByteChannel;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import org.springframework.lang.Nullable;
import org.springframework.util.ResourceUtils;
/**
* Subclass of {@link UrlResource} which assumes file resolution, to the degree
* of implementing the {@link WritableResource} interface for it.
* of implementing the {@link WritableResource} interface for it. This resource
* variant also caches resolved {@link File} handles from {@link #getFile()}.
*
* <p>This is the class resolved by {@link DefaultResourceLoader} for a "file:..."
* URL location, allowing a downcast to {@link WritableResource} for it.
@@ -44,6 +46,10 @@ import org.springframework.util.ResourceUtils;
*/
public class FileUrlResource extends UrlResource implements WritableResource {
@Nullable
private volatile File file;
/**
* Create a new {@code FileUrlResource} based on the given URL object.
* <p>Note that this does not enforce "file" as URL protocol. If a protocol
@@ -71,11 +77,14 @@ public class FileUrlResource extends UrlResource implements WritableResource {
@Override
public Resource createRelative(String relativePath) throws MalformedURLException {
if (relativePath.startsWith("/")) {
relativePath = relativePath.substring(1);
public File getFile() throws IOException {
File file = this.file;
if (file != null) {
return file;
}
return new FileUrlResource(new URL(getURL(), relativePath));
file = super.getFile();
this.file = file;
return file;
}
@Override
@@ -106,4 +115,12 @@ public class FileUrlResource extends UrlResource implements WritableResource {
return FileChannel.open(getFile().toPath(), StandardOpenOption.WRITE);
}
@Override
public Resource createRelative(String relativePath) throws MalformedURLException {
if (relativePath.startsWith("/")) {
relativePath = relativePath.substring(1);
}
return new FileUrlResource(new URL(getURL(), relativePath));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import java.net.URL;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -202,7 +203,12 @@ public class PathResource extends AbstractResource implements WritableResource {
*/
@Override
public ReadableByteChannel readableChannel() throws IOException {
return Files.newByteChannel(this.path, StandardOpenOption.READ);
try {
return Files.newByteChannel(this.path, StandardOpenOption.READ);
}
catch (NoSuchFileException ex) {
throw new FileNotFoundException(ex.getMessage());
}
}
/**
@@ -215,7 +221,7 @@ public class PathResource extends AbstractResource implements WritableResource {
}
/**
* This implementation returns the underlying File's length.
* This implementation returns the underlying file's length.
*/
@Override
public long contentLength() throws IOException {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -190,7 +190,8 @@ public interface DataBuffer {
/**
* Write one or more {@code DataBuffer}s to this buffer, starting at the current
* writing position.
* writing position. It is the responsibility of the caller to
* {@linkplain DataBufferUtils#release(DataBuffer) release} the given data buffers.
* @param buffers the byte buffers to write into this buffer
* @return this buffer
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package org.springframework.core.io.buffer;
import java.nio.ByteBuffer;
import java.util.List;
/**
* A factory for {@link DataBuffer}s, allowing for allocation and wrapping of
@@ -61,4 +62,16 @@ public interface DataBufferFactory {
*/
DataBuffer wrap(byte[] bytes);
/**
* Return a new {@code DataBuffer} composed of the {@code dataBuffers} elements joined together.
* Depending on the implementation, the returned buffer may be a single buffer containing all
* data of the provided buffers, or it may be a true composite that contains references to the
* buffers.
* <p>Note that the given data buffers do <strong>not</strong> have to be released, as they are
* released as part of the returned composite.
* @param dataBuffers the data buffers to be composed
* @return a buffer that is composed from the {@code dataBuffers} argument
* @since 5.0.3
*/
DataBuffer join(List<? extends DataBuffer> dataBuffers);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,9 +28,10 @@ import java.nio.channels.CompletionHandler;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import org.reactivestreams.Publisher;
@@ -38,6 +39,7 @@ import org.reactivestreams.Subscription;
import reactor.core.publisher.BaseSubscriber;
import reactor.core.publisher.Flux;
import reactor.core.publisher.FluxSink;
import reactor.core.publisher.Mono;
import reactor.core.publisher.SynchronousSink;
import org.springframework.core.io.Resource;
@@ -55,74 +57,170 @@ public abstract class DataBufferUtils {
private static final Consumer<DataBuffer> RELEASE_CONSUMER = DataBufferUtils::release;
private static final BinaryOperator<DataBuffer> WRITE_AGGREGATOR =
(dataBuffer1, dataBuffer2) -> {
DataBuffer result = dataBuffer1.write(dataBuffer2);
release(dataBuffer2);
return result;
};
//---------------------------------------------------------------------
// Reading
//---------------------------------------------------------------------
/**
* Read the given {@code InputStream} into a {@code Flux} of
* Read the given {@code InputStream} into a <strong>read-once</strong> {@code Flux} of
* {@code DataBuffer}s. Closes the input stream when the flux is terminated.
* <p>The resulting {@code Flux} can only be subscribed to once. See
* {@link #readInputStream(Callable, DataBufferFactory, int)} for a variant that supports
* multiple subscriptions.
* @param inputStream the input stream to read from
* @param dataBufferFactory the factory to create data buffers with
* @param bufferSize the maximum size of the data buffers
* @return a flux of data buffers read from the given channel
* @deprecated as of Spring 5.0.3, in favor of
* {@link #readInputStream(Callable, DataBufferFactory, int)}, to be removed in Spring 5.1
*/
@Deprecated
public static Flux<DataBuffer> read(InputStream inputStream,
DataBufferFactory dataBufferFactory, int bufferSize) {
Assert.notNull(inputStream, "InputStream must not be null");
ReadableByteChannel channel = Channels.newChannel(inputStream);
return read(channel, dataBufferFactory, bufferSize);
return readInputStream(() -> inputStream, dataBufferFactory, bufferSize);
}
/**
* Read the given {@code ReadableByteChannel} into a {@code Flux} of
* {@code DataBuffer}s. Closes the channel when the flux is terminated.
* @param channel the channel to read from
* Obtain a {@link InputStream} from the given supplier, and read it into a {@code Flux} of
* {@code DataBuffer}s. Closes the input stream when the flux is terminated.
* @param inputStreamSupplier the supplier for the input stream to read from
* @param dataBufferFactory the factory to create data buffers with
* @param bufferSize the maximum size of the data buffers
* @return a flux of data buffers read from the given channel
*/
public static Flux<DataBuffer> readInputStream(Callable<InputStream> inputStreamSupplier,
DataBufferFactory dataBufferFactory, int bufferSize) {
Assert.notNull(inputStreamSupplier, "'inputStreamSupplier' must not be null");
return readByteChannel(() -> Channels.newChannel(inputStreamSupplier.call()),
dataBufferFactory, bufferSize);
}
/**
* Read the given {@code ReadableByteChannel} into a <strong>read-once</strong> {@code Flux} of
* {@code DataBuffer}s. Closes the channel when the flux is terminated.
* <p>The resulting {@code Flux} can only be subscribed to once. See
* {@link #readByteChannel(Callable, DataBufferFactory, int)} for a variant that supports
* multiple subscriptions.
* @param channel the channel to read from
* @param dataBufferFactory the factory to create data buffers with
* @param bufferSize the maximum size of the data buffers
* @return a flux of data buffers read from the given channel
* @deprecated as of Spring 5.0.3, in favor of
* {@link #readByteChannel(Callable, DataBufferFactory, int)}, to be removed in Spring 5.1
*/
@Deprecated
public static Flux<DataBuffer> read(ReadableByteChannel channel,
DataBufferFactory dataBufferFactory, int bufferSize) {
Assert.notNull(channel, "ReadableByteChannel must not be null");
Assert.notNull(dataBufferFactory, "DataBufferFactory must not be null");
Assert.isTrue(bufferSize > 0, "'bufferSize' must be > 0");
return Flux.generate(() -> channel,
new ReadableByteChannelGenerator(dataBufferFactory, bufferSize),
DataBufferUtils::closeChannel);
return readByteChannel(() -> channel, dataBufferFactory, bufferSize);
}
/**
* Read the given {@code AsynchronousFileChannel} into a {@code Flux} of
* {@code DataBuffer}s. Closes the channel when the flux is terminated.
* @param channel the channel to read from
* Obtain a {@link ReadableByteChannel} from the given supplier, and read it into a
* {@code Flux} of {@code DataBuffer}s. Closes the channel when the flux is terminated.
* @param channelSupplier the supplier for the channel to read from
* @param dataBufferFactory the factory to create data buffers with
* @param bufferSize the maximum size of the data buffers
* @return a flux of data buffers read from the given channel
*/
public static Flux<DataBuffer> read(AsynchronousFileChannel channel,
public static Flux<DataBuffer> readByteChannel(Callable<ReadableByteChannel> channelSupplier,
DataBufferFactory dataBufferFactory, int bufferSize) {
return read(channel, 0, dataBufferFactory, bufferSize);
Assert.notNull(channelSupplier, "'channelSupplier' must not be null");
Assert.notNull(dataBufferFactory, "'dataBufferFactory' must not be null");
Assert.isTrue(bufferSize > 0, "'bufferSize' must be > 0");
return Flux.using(channelSupplier,
channel -> {
ReadableByteChannelGenerator generator =
new ReadableByteChannelGenerator(channel, dataBufferFactory,
bufferSize);
return Flux.generate(generator);
},
DataBufferUtils::closeChannel
);
}
/**
* Read the given {@code AsynchronousFileChannel} into a {@code Flux} of
* {@code DataBuffer}s, starting at the given position. Closes the channel when the flux is
* Read the given {@code AsynchronousFileChannel} into a <strong>read-once</strong> {@code Flux}
* of {@code DataBuffer}s. Closes the channel when the flux is terminated.
* <p>The resulting {@code Flux} can only be subscribed to once. See
* {@link #readAsynchronousFileChannel(Callable, DataBufferFactory, int)} for a variant that
* supports multiple subscriptions.
* @param channel the channel to read from
* @param dataBufferFactory the factory to create data buffers with
* @param bufferSize the maximum size of the data buffers
* @return a flux of data buffers read from the given channel
* @deprecated as of Spring 5.0.3, in favor of
* {@link #readAsynchronousFileChannel(Callable, DataBufferFactory, int)}, to be removed in
* Spring 5.1
*/
@Deprecated
public static Flux<DataBuffer> read(AsynchronousFileChannel channel,
DataBufferFactory dataBufferFactory, int bufferSize) {
return readAsynchronousFileChannel(() -> channel, dataBufferFactory, bufferSize);
}
/**
* Read the given {@code AsynchronousFileChannel} into a <strong>read-once</strong> {@code Flux}
* of {@code DataBuffer}s, starting at the given position. Closes the channel when the flux is
* terminated.
* <p>The resulting {@code Flux} can only be subscribed to once. See
* {@link #readAsynchronousFileChannel(Callable, long, DataBufferFactory, int)} for a variant
* that supports multiple subscriptions.
* @param channel the channel to read from
* @param position the position to start reading from
* @param dataBufferFactory the factory to create data buffers with
* @param bufferSize the maximum size of the data buffers
* @return a flux of data buffers read from the given channel
* @deprecated as of Spring 5.0.3, in favor of
* {@link #readAsynchronousFileChannel(Callable, long, DataBufferFactory, int)}, to be removed
* in Spring 5.1
*/
@Deprecated
public static Flux<DataBuffer> read(AsynchronousFileChannel channel,
long position, DataBufferFactory dataBufferFactory, int bufferSize) {
return readAsynchronousFileChannel(() -> channel, position, dataBufferFactory, bufferSize);
}
Assert.notNull(channel, "'channel' must not be null");
/**
* Obtain a {@code AsynchronousFileChannel} from the given supplier, and read it into a
* {@code Flux} of {@code DataBuffer}s. Closes the channel when the flux is terminated.
* @param channelSupplier the supplier for the channel to read from
* @param dataBufferFactory the factory to create data buffers with
* @param bufferSize the maximum size of the data buffers
* @return a flux of data buffers read from the given channel
*/
public static Flux<DataBuffer> readAsynchronousFileChannel(
Callable<AsynchronousFileChannel> channelSupplier,
DataBufferFactory dataBufferFactory, int bufferSize) {
return readAsynchronousFileChannel(channelSupplier, 0, dataBufferFactory, bufferSize);
}
/**
* Obtain a {@code AsynchronousFileChannel} from the given supplier, and read it into a
* {@code Flux} of {@code DataBuffer}s, starting at the given position. Closes the channel when
* the flux is terminated.
* @param channelSupplier the supplier for the channel to read from
* @param position the position to start reading from
* @param dataBufferFactory the factory to create data buffers with
* @param bufferSize the maximum size of the data buffers
* @return a flux of data buffers read from the given channel
*/
public static Flux<DataBuffer> readAsynchronousFileChannel(
Callable<AsynchronousFileChannel> channelSupplier,
long position, DataBufferFactory dataBufferFactory, int bufferSize) {
Assert.notNull(channelSupplier, "'channelSupplier' must not be null");
Assert.notNull(dataBufferFactory, "'dataBufferFactory' must not be null");
Assert.isTrue(position >= 0, "'position' must be >= 0");
Assert.isTrue(bufferSize > 0, "'bufferSize' must be > 0");
@@ -130,22 +228,23 @@ public abstract class DataBufferUtils {
DataBuffer dataBuffer = dataBufferFactory.allocateBuffer(bufferSize);
ByteBuffer byteBuffer = dataBuffer.asByteBuffer(0, bufferSize);
return Flux.create(sink -> {
sink.onDispose(() -> closeChannel(channel));
CompletionHandler<Integer, DataBuffer> completionHandler =
new AsynchronousFileChannelReadCompletionHandler(channel, sink, position,
dataBufferFactory, bufferSize);
channel.read(byteBuffer, position, dataBuffer, completionHandler);
});
return Flux.using(channelSupplier,
channel -> Flux.create(sink -> {
CompletionHandler<Integer, DataBuffer> completionHandler =
new AsynchronousFileChannelReadCompletionHandler(channel,
sink, position, dataBufferFactory, bufferSize);
channel.read(byteBuffer, position, dataBuffer, completionHandler);
}),
DataBufferUtils::closeChannel);
}
/**
* Read the given {@code Resource} into a {@code Flux} of {@code DataBuffer}s.
* <p>If the resource is a file, it is read into an
* {@code AsynchronousFileChannel} and turned to {@code Flux} via
* {@link #read(AsynchronousFileChannel, DataBufferFactory, int)} or else
* fall back on {@link #read(InputStream, DataBufferFactory, int)} closes
* the channel when the flux is terminated.
* {@link #readAsynchronousFileChannel(Callable, DataBufferFactory, int)} or else
* fall back to {@link #readByteChannel(Callable, DataBufferFactory, int)}.
* Closes the channel when the flux is terminated.
* @param resource the resource to read from
* @param dataBufferFactory the factory to create data buffers with
* @param bufferSize the maximum size of the data buffers
@@ -162,9 +261,9 @@ public abstract class DataBufferUtils {
* starting at the given position.
* <p>If the resource is a file, it is read into an
* {@code AsynchronousFileChannel} and turned to {@code Flux} via
* {@link #read(AsynchronousFileChannel, DataBufferFactory, int)} or else
* fall back on {@link #read(InputStream, DataBufferFactory, int)}. Closes
* the channel when the flux is terminated.
* {@link #readAsynchronousFileChannel(Callable, DataBufferFactory, int)} or else
* fall back on {@link #readByteChannel(Callable, DataBufferFactory, int)}.
* Closes the channel when the flux is terminated.
* @param resource the resource to read from
* @param position the position to start reading from
* @param dataBufferFactory the factory to create data buffers with
@@ -177,26 +276,23 @@ public abstract class DataBufferUtils {
try {
if (resource.isFile()) {
File file = resource.getFile();
AsynchronousFileChannel channel =
AsynchronousFileChannel.open(file.toPath(), StandardOpenOption.READ);
return DataBufferUtils.read(channel, position, dataBufferFactory, bufferSize);
return readAsynchronousFileChannel(
() -> AsynchronousFileChannel.open(file.toPath(), StandardOpenOption.READ),
position, dataBufferFactory, bufferSize);
}
}
catch (IOException ignore) {
// fallback to resource.readableChannel(), below
}
try {
ReadableByteChannel channel = resource.readableChannel();
Flux<DataBuffer> in = DataBufferUtils.read(channel, dataBufferFactory, bufferSize);
return DataBufferUtils.skipUntilByteCount(in, position);
}
catch (IOException ex) {
return Flux.error(ex);
}
Flux<DataBuffer> result = readByteChannel(resource::readableChannel, dataBufferFactory, bufferSize);
return position == 0 ? result : skipUntilByteCount(result, position);
}
//---------------------------------------------------------------------
// Writing
//---------------------------------------------------------------------
@@ -295,7 +391,7 @@ public abstract class DataBufferUtils {
private static void closeChannel(@Nullable Channel channel) {
try {
if (channel != null) {
if (channel != null && channel.isOpen()) {
channel.close();
}
}
@@ -303,6 +399,10 @@ public abstract class DataBufferUtils {
}
}
//---------------------------------------------------------------------
// Various
//---------------------------------------------------------------------
/**
* Relay buffers from the given {@link Publisher} until the total
* {@linkplain DataBuffer#readableByteCount() byte count} reaches
@@ -335,10 +435,6 @@ public abstract class DataBufferUtils {
});
}
//---------------------------------------------------------------------
// Various
//---------------------------------------------------------------------
/**
* Skip buffers from the given {@link Publisher} until the total
* {@linkplain DataBuffer#readableByteCount() byte count} reaches
@@ -403,55 +499,82 @@ public abstract class DataBufferUtils {
}
/**
* Returns a consumer that calls {@link #release(DataBuffer)} on all
* Return a consumer that calls {@link #release(DataBuffer)} on all
* passed data buffers.
*/
public static Consumer<DataBuffer> releaseConsumer() {
return RELEASE_CONSUMER;
}
/**
* Return a new {@code DataBuffer} composed of the {@code dataBuffers} elements joined together.
* Depending on the {@link DataBuffer} implementation, the returned buffer may be a single
* buffer containing all data of the provided buffers, or it may be a true composite that
* contains references to the buffers.
* @param dataBuffers the data buffers that are to be composed
* @return a buffer that is composed from the {@code dataBuffers} argument
* @since 5.0.3
*/
public static Mono<DataBuffer> join(Publisher<? extends DataBuffer> dataBuffers) {
Assert.notNull(dataBuffers, "'dataBuffers' must not be null");
return Flux.from(dataBuffers)
.collectList()
.filter(list -> !list.isEmpty())
.map(list -> {
DataBufferFactory bufferFactory = list.get(0).factory();
return bufferFactory.join(list);
});
}
private static class ReadableByteChannelGenerator
implements BiFunction<ReadableByteChannel, SynchronousSink<DataBuffer>, ReadableByteChannel> {
implements Consumer<SynchronousSink<DataBuffer>> {
private final ReadableByteChannel channel;
private final DataBufferFactory dataBufferFactory;
private final int bufferSize;
public ReadableByteChannelGenerator(DataBufferFactory dataBufferFactory, int bufferSize) {
public ReadableByteChannelGenerator(ReadableByteChannel channel,
DataBufferFactory dataBufferFactory, int bufferSize) {
this.channel = channel;
this.dataBufferFactory = dataBufferFactory;
this.bufferSize = bufferSize;
}
@Override
public ReadableByteChannel apply(ReadableByteChannel channel,
SynchronousSink<DataBuffer> sub) {
public void accept(SynchronousSink<DataBuffer> sink) {
boolean release = true;
DataBuffer dataBuffer = this.dataBufferFactory.allocateBuffer(this.bufferSize);
try {
int read;
ByteBuffer byteBuffer = dataBuffer.asByteBuffer(0, dataBuffer.capacity());
if ((read = channel.read(byteBuffer)) >= 0) {
if ((read = this.channel.read(byteBuffer)) >= 0) {
dataBuffer.writePosition(read);
release = false;
sub.next(dataBuffer);
sink.next(dataBuffer);
}
else {
sub.complete();
sink.complete();
}
}
catch (IOException ex) {
sub.error(ex);
sink.error(ex);
}
finally {
if (release) {
release(dataBuffer);
}
}
return channel;
}
}
private static class AsynchronousFileChannelReadCompletionHandler
implements CompletionHandler<Integer, DataBuffer> {
@@ -463,7 +586,10 @@ public abstract class DataBufferUtils {
private final int bufferSize;
private AtomicLong position;
private final AtomicLong position;
private final AtomicBoolean disposed = new AtomicBoolean();
private AsynchronousFileChannelReadCompletionHandler(
AsynchronousFileChannel channel, FluxSink<DataBuffer> sink,
@@ -482,7 +608,7 @@ public abstract class DataBufferUtils {
dataBuffer.writePosition(read);
this.sink.next(dataBuffer);
if (!this.sink.isCancelled()) {
if (!this.disposed.get()) {
DataBuffer newDataBuffer =
this.dataBufferFactory.allocateBuffer(this.bufferSize);
ByteBuffer newByteBuffer = newDataBuffer.asByteBuffer(0, this.bufferSize);
@@ -491,7 +617,6 @@ public abstract class DataBufferUtils {
}
else {
release(dataBuffer);
closeChannel(this.channel);
this.sink.complete();
}
}
@@ -499,11 +624,12 @@ public abstract class DataBufferUtils {
@Override
public void failed(Throwable exc, DataBuffer dataBuffer) {
release(dataBuffer);
closeChannel(this.channel);
this.sink.error(exc);
}
}
private static class AsynchronousFileChannelWriteCompletionHandler
extends BaseSubscriber<DataBuffer>
implements CompletionHandler<Integer, ByteBuffer> {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package org.springframework.core.io.buffer;
import java.nio.ByteBuffer;
import java.util.List;
import org.springframework.util.Assert;
@@ -102,6 +103,26 @@ public class DefaultDataBufferFactory implements DataBufferFactory {
return DefaultDataBuffer.fromFilledByteBuffer(this, wrapper);
}
/**
* {@inheritDoc}
* <p>This implementation creates a single {@link DefaultDataBuffer} to contain the data
* in {@code dataBuffers}.
*/
@Override
public DataBuffer join(List<? extends DataBuffer> dataBuffers) {
Assert.notEmpty(dataBuffers, "'dataBuffers' must not be empty");
int capacity = dataBuffers.stream()
.mapToInt(DataBuffer::readableByteCount)
.sum();
DefaultDataBuffer dataBuffer = allocateBuffer(capacity);
DataBuffer result = dataBuffers.stream()
.map(o -> (DataBuffer) o)
.reduce(dataBuffer, DataBuffer::write);
dataBuffers.forEach(DataBufferUtils::release);
return result;
}
@Override
public String toString() {
return "DefaultDataBufferFactory (preferDirect=" + this.preferDirect + ")";
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,11 +25,8 @@ import java.util.function.IntPredicate;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.Unpooled;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Implementation of the {@code DataBuffer} interface that wraps a Netty
@@ -42,7 +39,7 @@ public class NettyDataBuffer implements PooledDataBuffer {
private final NettyDataBufferFactory dataBufferFactory;
private ByteBuf byteBuf;
private final ByteBuf byteBuf;
/**
@@ -174,8 +171,10 @@ public class NettyDataBuffer implements PooledDataBuffer {
@Override
public NettyDataBuffer write(DataBuffer... buffers) {
if (!ObjectUtils.isEmpty(buffers)) {
if (buffers[0] instanceof NettyDataBuffer) {
Assert.notNull(buffers, "'buffers' must not be null");
if (buffers.length > 0) {
if (hasNettyDataBuffers(buffers)) {
ByteBuf[] nativeBuffers = Arrays.stream(buffers)
.map(b -> ((NettyDataBuffer) b).getNativeBuffer())
.toArray(ByteBuf[]::new);
@@ -191,12 +190,23 @@ public class NettyDataBuffer implements PooledDataBuffer {
return this;
}
private static boolean hasNettyDataBuffers(DataBuffer[] dataBuffers) {
for (DataBuffer dataBuffer : dataBuffers) {
if (!(dataBuffer instanceof NettyDataBuffer)) {
return false;
}
}
return true;
}
@Override
public NettyDataBuffer write(ByteBuffer... buffers) {
Assert.notNull(buffers, "'buffers' must not be null");
ByteBuf[] wrappedBuffers = Arrays.stream(buffers).map(Unpooled::wrappedBuffer)
.toArray(ByteBuf[]::new);
return write(wrappedBuffers);
for (ByteBuffer buffer : buffers) {
this.byteBuf.writeBytes(buffer);
}
return this;
}
/**
@@ -208,17 +218,8 @@ public class NettyDataBuffer implements PooledDataBuffer {
public NettyDataBuffer write(ByteBuf... byteBufs) {
Assert.notNull(byteBufs, "'byteBufs' must not be null");
if (this.byteBuf instanceof CompositeByteBuf) {
CompositeByteBuf composite = (CompositeByteBuf) this.byteBuf;
composite.addComponents(true, byteBufs);
}
else {
ByteBuf oldByteBuf = this.byteBuf;
CompositeByteBuf composite = oldByteBuf.alloc().compositeBuffer(byteBufs.length + 1);
composite.addComponent(true, oldByteBuf);
composite.addComponents(true, byteBufs);
this.byteBuf = composite;
for (ByteBuf byteBuf : byteBufs) {
this.byteBuf.writeBytes(byteBuf);
}
return this;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,9 +17,11 @@
package org.springframework.core.io.buffer;
import java.nio.ByteBuffer;
import java.util.List;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.Unpooled;
import org.springframework.util.Assert;
@@ -80,6 +82,22 @@ public class NettyDataBufferFactory implements DataBufferFactory {
return new NettyDataBuffer(byteBuf, this);
}
/**
* {@inheritDoc}
* <p>This implementation uses Netty's {@link CompositeByteBuf}.
*/
@Override
public DataBuffer join(List<? extends DataBuffer> dataBuffers) {
Assert.notNull(dataBuffers, "'dataBuffers' must not be null");
CompositeByteBuf composite = this.byteBufAllocator.compositeBuffer(dataBuffers.size());
for (DataBuffer dataBuffer : dataBuffers) {
Assert.isInstanceOf(NettyDataBuffer.class, dataBuffer);
NettyDataBuffer nettyDataBuffer = (NettyDataBuffer) dataBuffer;
composite.addComponent(true, nettyDataBuffer.getNativeBuffer());
}
return new NettyDataBuffer(composite, this);
}
/**
* Wrap the given Netty {@link ByteBuf} in a {@code NettyDataBuffer}.
* @param byteBuf the Netty byte buffer to wrap
@@ -427,7 +427,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
int prefixIndex = filePath.indexOf(':');
if (prefixIndex == 1) {
// Possibly "c:" drive prefix on Windows, to be upper-cased for proper duplicate detection
filePath = filePath.substring(0, 1).toUpperCase() + filePath.substring(1);
filePath = StringUtils.capitalize(filePath);
}
UrlResource jarResource = new UrlResource(ResourceUtils.JAR_URL_PREFIX +
ResourceUtils.FILE_URL_PREFIX + filePath + ResourceUtils.JAR_URL_SEPARATOR);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.springframework.core.type.classreading;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.springframework.core.io.DefaultResourceLoader;
@@ -73,10 +74,13 @@ public class SimpleMetadataReaderFactory implements MetadataReaderFactory {
@Override
public MetadataReader getMetadataReader(String className) throws IOException {
String resourcePath = ResourceLoader.CLASSPATH_URL_PREFIX +
ClassUtils.convertClassNameToResourcePath(className) + ClassUtils.CLASS_FILE_SUFFIX;
Resource resource = this.resourceLoader.getResource(resourcePath);
if (!resource.exists()) {
try {
String resourcePath = ResourceLoader.CLASSPATH_URL_PREFIX +
ClassUtils.convertClassNameToResourcePath(className) + ClassUtils.CLASS_FILE_SUFFIX;
Resource resource = this.resourceLoader.getResource(resourcePath);
return getMetadataReader(resource);
}
catch (FileNotFoundException ex) {
// Maybe an inner class name using the dot name syntax? Need to use the dollar syntax here...
// ClassUtils.forName has an equivalent check for resolution into Class references later on.
int lastDotIndex = className.lastIndexOf('.');
@@ -87,11 +91,11 @@ public class SimpleMetadataReaderFactory implements MetadataReaderFactory {
ClassUtils.convertClassNameToResourcePath(innerClassName) + ClassUtils.CLASS_FILE_SUFFIX;
Resource innerClassResource = this.resourceLoader.getResource(innerClassResourcePath);
if (innerClassResource.exists()) {
resource = innerClassResource;
return getMetadataReader(innerClassResource);
}
}
throw ex;
}
return getMetadataReader(resource);
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,8 @@
package org.springframework.util;
import java.beans.Introspector;
import java.io.Externalizable;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
@@ -74,6 +76,13 @@ public abstract class ClassUtils {
public static final String CLASS_FILE_SUFFIX = ".class";
/**
* Common Java language interfaces which are supposed to be ignored
* when searching for 'primary' user-level interfaces.
*/
private static final Set<Class<?>> javaLanguageInterfaces = new HashSet<>(
Arrays.asList(Serializable.class, Externalizable.class, Cloneable.class, Comparable.class));
/**
* Map with primitive wrapper type as key and corresponding primitive
* type as value, for example: Integer.class -> int.class.
@@ -289,11 +298,10 @@ public abstract class ClassUtils {
return forName(className, classLoader);
}
catch (ClassNotFoundException ex) {
throw new IllegalArgumentException("Cannot find class [" + className + "]", ex);
throw new IllegalArgumentException("Could not find class [" + className + "]", ex);
}
catch (LinkageError ex) {
throw new IllegalArgumentException(
"Error loading class [" + className + "]: problem with class file or dependent class.", ex);
catch (LinkageError err) {
throw new IllegalArgumentException("Unresolvable class definition for class [" + className + "]", err);
}
}
@@ -1218,6 +1226,19 @@ public abstract class ClassUtils {
}
}
/**
* Determine whether the given interface is a common Java language interface:
* {@link Serializable}, {@link Externalizable}, {@link Cloneable}, {@link Comparable}
* - all of which can be ignored when looking for 'primary' user-level interfaces.
* Common characteristics: no service-level operations, no bean property methods,
* no default methods.
* @param ifc the interface to check
* @since 5.0.3
*/
public static boolean isJavaLanguageInterface(Class<?> ifc) {
return javaLanguageInterfaces.contains(ifc);
}
/**
* Check whether the given object is a CGLIB proxy.
* @param object the object to check
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import org.springframework.lang.Nullable;
@@ -312,6 +313,48 @@ public abstract class CollectionUtils {
return candidate;
}
/**
* Retrieve the last element of the given Set, using {@link SortedSet#last()}
* or otherwise iterating over all elements (assuming a linked set).
* @param set the Set to check (may be {@code null} or empty)
* @return the last element, or {@code null} if none
* @since 5.0.3
* @see SortedSet
* @see LinkedHashMap#keySet()
* @see java.util.LinkedHashSet
*/
@Nullable
public static <T> T lastElement(@Nullable Set<T> set) {
if (isEmpty(set)) {
return null;
}
if (set instanceof SortedSet) {
return ((SortedSet<T>) set).last();
}
// Full iteration necessary...
Iterator<T> it = set.iterator();
T last = null;
while (it.hasNext()) {
last = it.next();
}
return last;
}
/**
* Retrieve the last element of the given List, accessing the highest index.
* @param list the List to check (may be {@code null} or empty)
* @return the last element, or {@code null} if none
* @since 5.0.3
*/
@Nullable
public static <T> T lastElement(@Nullable List<T> list) {
if (isEmpty(list)) {
return null;
}
return list.get(list.size() - 1);
}
/**
* Marshal the elements from the given enumeration into an array of the given type.
* Enumeration elements must be assignable to the type of the given array. The array
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 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.
@@ -251,19 +251,20 @@ public class SocketUtils {
*/
int findAvailablePort(int minPort, int maxPort) {
Assert.isTrue(minPort > 0, "'minPort' must be greater than 0");
Assert.isTrue(maxPort >= minPort, "'maxPort' must be greater than or equals 'minPort'");
Assert.isTrue(maxPort >= minPort, "'maxPort' must be greater than or equal to 'minPort'");
Assert.isTrue(maxPort <= PORT_RANGE_MAX, "'maxPort' must be less than or equal to " + PORT_RANGE_MAX);
int portRange = maxPort - minPort;
int candidatePort;
int searchCounter = 0;
do {
if (++searchCounter > portRange) {
if (searchCounter > portRange) {
throw new IllegalStateException(String.format(
"Could not find an available %s port in the range [%d, %d] after %d attempts",
name(), minPort, maxPort, searchCounter));
}
candidatePort = findRandomPort(minPort, maxPort);
searchCounter++;
}
while (!isPortAvailable(candidatePort));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -741,7 +741,7 @@ public class BridgeMethodResolverTests {
}
@SuppressWarnings({ "unused", "unchecked" })
@SuppressWarnings({"unused", "unchecked"})
public static abstract class GenericEventBroadcasterImpl<T extends Event>
extends GenericBroadcasterImpl implements EventBroadcaster {
@@ -806,10 +806,10 @@ public class BridgeMethodResolverTests {
}
@SuppressWarnings("unchecked")
public static class MessageBroadcasterImpl extends
GenericEventBroadcasterImpl<MessageEvent>
implements MessageBroadcaster {
@SuppressWarnings({"serial", "unchecked"})
public static class MessageBroadcasterImpl extends GenericEventBroadcasterImpl<MessageEvent>
implements Serializable, // implement an unrelated interface first (SPR-16288)
MessageBroadcaster {
public MessageBroadcasterImpl() {
super(NewMessageEvent.class);
@@ -864,7 +864,7 @@ public class BridgeMethodResolverTests {
@SuppressWarnings("unchecked")
public static class SettableRepositoryRegistry<R extends SimpleGenericRepository<?>>
implements RepositoryRegistry {
implements RepositoryRegistry {
protected void injectInto(R rep) {
}
@@ -902,7 +902,7 @@ public class BridgeMethodResolverTests {
public static class GenericHibernateRepository<T, ID extends Serializable>
implements ConvenientGenericRepository<T, ID> {
implements ConvenientGenericRepository<T, ID> {
/**
* @param c Mandatory. The domain class this repository is responsible for.
@@ -964,8 +964,8 @@ public class BridgeMethodResolverTests {
}
public static class HibernateRepositoryRegistry extends
SettableRepositoryRegistry<GenericHibernateRepository<?, ?>> {
public static class HibernateRepositoryRegistry
extends SettableRepositoryRegistry<GenericHibernateRepository<?, ?>> {
@Override
public void injectInto(GenericHibernateRepository<?, ?> rep) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -64,6 +64,7 @@ import static org.mockito.BDDMockito.*;
*
* @author Phillip Webb
* @author Juergen Hoeller
* @author Sebastien Deleuze
*/
@SuppressWarnings("rawtypes")
@RunWith(MockitoJUnitRunner.class)
@@ -176,11 +177,13 @@ public class ResolvableTypeTests {
ResolvableType type = ResolvableType.forField(field);
assertThat(type.getType(), equalTo(field.getGenericType()));
assertThat(type.resolve(), equalTo((Class) List.class));
assertThat(type.getSource(), sameInstance(field));
Field field2 = Fields.class.getDeclaredField("otherPrivateField");
ResolvableType type2 = ResolvableType.forField(field2);
assertThat(type2.getType(), equalTo(field2.getGenericType()));
assertThat(type2.resolve(), equalTo((Class) List.class));
assertThat(type2.getSource(), sameInstance(field2));
assertEquals(type, type2);
assertEquals(type.hashCode(), type2.hashCode());
@@ -259,6 +262,19 @@ public class ResolvableTypeTests {
ResolvableType.forMethodParameter(null);
}
@Test // SPR-16210
public void forMethodParameterWithSameSignatureAndGenerics() throws Exception {
Method method = Methods.class.getMethod("list1");
MethodParameter methodParameter = MethodParameter.forExecutable(method, -1);
ResolvableType type = ResolvableType.forMethodParameter(methodParameter);
assertThat(((MethodParameter)type.getSource()).getMethod(), equalTo(method));
method = Methods.class.getMethod("list2");
methodParameter = MethodParameter.forExecutable(method, -1);
type = ResolvableType.forMethodParameter(methodParameter);
assertThat(((MethodParameter)type.getSource()).getMethod(), equalTo(method));
}
@Test
public void forMethodReturn() throws Exception {
Method method = Methods.class.getMethod("charSequenceReturn");
@@ -1437,6 +1453,10 @@ public class ResolvableTypeTests {
T typedReturn();
Set<?> wildcardSet();
List<String> list1();
List<String> list2();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import java.util.Collections;
import org.junit.Test;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.core.ResolvableType;
@@ -39,6 +40,7 @@ public class ByteArrayDecoderTests extends AbstractDataBufferAllocatingTestCase
private final ByteArrayDecoder decoder = new ByteArrayDecoder();
@Test
public void canDecode() {
assertTrue(this.decoder.canDecode(ResolvableType.forClass(byte[].class),
@@ -65,4 +67,19 @@ public class ByteArrayDecoderTests extends AbstractDataBufferAllocatingTestCase
.verify();
}
@Test
public void decodeToMono() {
DataBuffer fooBuffer = stringBuffer("foo");
DataBuffer barBuffer = stringBuffer("bar");
Flux<DataBuffer> source = Flux.just(fooBuffer, barBuffer);
Mono<byte[]> output = this.decoder.decodeToMono(source,
ResolvableType.forClassWithGenerics(Publisher.class, byte[].class),
null, Collections.emptyMap());
StepVerifier.create(output)
.consumeNextWith(bytes -> assertArrayEquals("foobar".getBytes(), bytes))
.expectComplete()
.verify();
}
}
@@ -22,6 +22,7 @@ import java.util.Collections;
import org.junit.Test;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.core.ResolvableType;
@@ -63,4 +64,19 @@ public class ByteBufferDecoderTests extends AbstractDataBufferAllocatingTestCase
.expectComplete()
.verify();
}
@Test
public void decodeToMono() {
DataBuffer fooBuffer = stringBuffer("foo");
DataBuffer barBuffer = stringBuffer("bar");
Flux<DataBuffer> source = Flux.just(fooBuffer, barBuffer);
Mono<ByteBuffer> output = this.decoder.decodeToMono(source,
ResolvableType.forClassWithGenerics(Publisher.class, ByteBuffer.class),
null, Collections.emptyMap());
StepVerifier.create(output)
.expectNext(ByteBuffer.wrap("foobar".getBytes()))
.expectComplete()
.verify();
}
}
@@ -17,15 +17,19 @@
package org.springframework.core.codec;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Collections;
import org.junit.Test;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
import org.springframework.util.MimeTypeUtils;
import static org.junit.Assert.*;
@@ -53,11 +57,25 @@ public class DataBufferDecoderTests extends AbstractDataBufferAllocatingTestCase
DataBuffer barBuffer = stringBuffer("bar");
Flux<DataBuffer> source = Flux.just(fooBuffer, barBuffer);
Flux<DataBuffer> output = this.decoder.decode(source,
ResolvableType.forClassWithGenerics(Publisher.class, ByteBuffer.class),
ResolvableType.forClassWithGenerics(Publisher.class, DataBuffer.class),
null, Collections.emptyMap());
assertSame(source, output);
release(fooBuffer, barBuffer);
}
}
@Test
public void decodeToMono() {
DataBuffer fooBuffer = stringBuffer("foo");
DataBuffer barBuffer = stringBuffer("bar");
Flux<DataBuffer> source = Flux.just(fooBuffer, barBuffer);
Mono<DataBuffer> output = this.decoder.decodeToMono(source,
ResolvableType.forClassWithGenerics(Publisher.class, DataBuffer.class),
null, Collections.emptyMap());
DataBuffer outputBuffer = output.block(Duration.ofSeconds(5));
assertEquals("foobar", DataBufferTestUtils.dumpString(outputBuffer, StandardCharsets.UTF_8));
release(outputBuffer);
}}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,13 +18,13 @@ package org.springframework.core.io;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.AccessDeniedException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -75,136 +75,136 @@ public class PathResourceTests {
@Test
public void nullPath() throws Exception {
public void nullPath() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Path must not be null");
new PathResource((Path) null);
}
@Test
public void nullPathString() throws Exception {
public void nullPathString() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Path must not be null");
new PathResource((String) null);
}
@Test
public void nullUri() throws Exception {
public void nullUri() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("URI must not be null");
new PathResource((URI) null);
}
@Test
public void createFromPath() throws Exception {
public void createFromPath() {
Path path = Paths.get(TEST_FILE);
PathResource resource = new PathResource(path);
assertThat(resource.getPath(), equalTo(TEST_FILE));
}
@Test
public void createFromString() throws Exception {
public void createFromString() {
PathResource resource = new PathResource(TEST_FILE);
assertThat(resource.getPath(), equalTo(TEST_FILE));
}
@Test
public void createFromUri() throws Exception {
public void createFromUri() {
File file = new File(TEST_FILE);
PathResource resource = new PathResource(file.toURI());
assertThat(resource.getPath(), equalTo(file.getAbsoluteFile().toString()));
}
@Test
public void getPathForFile() throws Exception {
public void getPathForFile() {
PathResource resource = new PathResource(TEST_FILE);
assertThat(resource.getPath(), equalTo(TEST_FILE));
}
@Test
public void getPathForDir() throws Exception {
public void getPathForDir() {
PathResource resource = new PathResource(TEST_DIR);
assertThat(resource.getPath(), equalTo(TEST_DIR));
}
@Test
public void fileExists() throws Exception {
public void fileExists() {
PathResource resource = new PathResource(TEST_FILE);
assertThat(resource.exists(), equalTo(true));
}
@Test
public void dirExists() throws Exception {
public void dirExists() {
PathResource resource = new PathResource(TEST_DIR);
assertThat(resource.exists(), equalTo(true));
}
@Test
public void fileDoesNotExist() throws Exception {
public void fileDoesNotExist() {
PathResource resource = new PathResource(NON_EXISTING_FILE);
assertThat(resource.exists(), equalTo(false));
}
@Test
public void fileIsReadable() throws Exception {
public void fileIsReadable() {
PathResource resource = new PathResource(TEST_FILE);
assertThat(resource.isReadable(), equalTo(true));
}
@Test
public void doesNotExistIsNotReadable() throws Exception {
public void doesNotExistIsNotReadable() {
PathResource resource = new PathResource(NON_EXISTING_FILE);
assertThat(resource.isReadable(), equalTo(false));
}
@Test
public void directoryIsNotReadable() throws Exception {
public void directoryIsNotReadable() {
PathResource resource = new PathResource(TEST_DIR);
assertThat(resource.isReadable(), equalTo(false));
}
@Test
public void getInputStream() throws Exception {
public void getInputStream() throws IOException {
PathResource resource = new PathResource(TEST_FILE);
byte[] bytes = FileCopyUtils.copyToByteArray(resource.getInputStream());
assertThat(bytes.length, greaterThan(0));
}
@Test
public void getInputStreamForDir() throws Exception {
public void getInputStreamForDir() throws IOException {
PathResource resource = new PathResource(TEST_DIR);
thrown.expect(FileNotFoundException.class);
resource.getInputStream();
}
@Test
public void getInputStreamDoesNotExist() throws Exception {
public void getInputStreamDoesNotExist() throws IOException {
PathResource resource = new PathResource(NON_EXISTING_FILE);
thrown.expect(FileNotFoundException.class);
resource.getInputStream();
}
@Test
public void getUrl() throws Exception {
public void getUrl() throws IOException {
PathResource resource = new PathResource(TEST_FILE);
assertThat(resource.getURL().toString(), Matchers.endsWith("core/io/example.properties"));
}
@Test
public void getUri() throws Exception {
public void getUri() throws IOException {
PathResource resource = new PathResource(TEST_FILE);
assertThat(resource.getURI().toString(), Matchers.endsWith("core/io/example.properties"));
}
@Test
public void getFile() throws Exception {
public void getFile() throws IOException {
PathResource resource = new PathResource(TEST_FILE);
File file = new File(TEST_FILE);
assertThat(resource.getFile().getAbsoluteFile(), equalTo(file.getAbsoluteFile()));
}
@Test
public void getFileUnsupported() throws Exception {
public void getFileUnsupported() throws IOException {
Path path = mock(Path.class);
given(path.normalize()).willReturn(path);
given(path.toFile()).willThrow(new UnsupportedOperationException());
@@ -214,72 +214,72 @@ public class PathResourceTests {
}
@Test
public void contentLength() throws Exception {
public void contentLength() throws IOException {
PathResource resource = new PathResource(TEST_FILE);
File file = new File(TEST_FILE);
assertThat(resource.contentLength(), equalTo(file.length()));
}
@Test
public void contentLengthForDirectory() throws Exception {
public void contentLengthForDirectory() throws IOException {
PathResource resource = new PathResource(TEST_DIR);
File file = new File(TEST_DIR);
assertThat(resource.contentLength(), equalTo(file.length()));
}
@Test
public void lastModified() throws Exception {
public void lastModified() throws IOException {
PathResource resource = new PathResource(TEST_FILE);
File file = new File(TEST_FILE);
assertThat(resource.lastModified() / 1000, equalTo(file.lastModified() / 1000));
}
@Test
public void createRelativeFromDir() throws Exception {
public void createRelativeFromDir() throws IOException {
Resource resource = new PathResource(TEST_DIR).createRelative("example.properties");
assertThat(resource, equalTo((Resource) new PathResource(TEST_FILE)));
}
@Test
public void createRelativeFromFile() throws Exception {
public void createRelativeFromFile() throws IOException {
Resource resource = new PathResource(TEST_FILE).createRelative("../example.properties");
assertThat(resource, equalTo((Resource) new PathResource(TEST_FILE)));
}
@Test
public void filename() throws Exception {
public void filename() {
Resource resource = new PathResource(TEST_FILE);
assertThat(resource.getFilename(), equalTo("example.properties"));
}
@Test
public void description() throws Exception {
public void description() {
Resource resource = new PathResource(TEST_FILE);
assertThat(resource.getDescription(), containsString("path ["));
assertThat(resource.getDescription(), containsString(TEST_FILE));
}
@Test
public void fileIsWritable() throws Exception {
public void fileIsWritable() {
PathResource resource = new PathResource(TEST_FILE);
assertThat(resource.isWritable(), equalTo(true));
}
@Test
public void directoryIsNotWritable() throws Exception {
public void directoryIsNotWritable() {
PathResource resource = new PathResource(TEST_DIR);
assertThat(resource.isWritable(), equalTo(false));
}
@Test
public void outputStream() throws Exception {
public void outputStream() throws IOException {
PathResource resource = new PathResource(temporaryFolder.newFile("test").toPath());
FileCopyUtils.copy("test".getBytes(StandardCharsets.UTF_8), resource.getOutputStream());
assertThat(resource.contentLength(), equalTo(4L));
}
@Test
public void doesNotExistOutputStream() throws Exception {
public void doesNotExistOutputStream() throws IOException {
File file = temporaryFolder.newFile("test");
file.delete();
PathResource resource = new PathResource(file.toPath());
@@ -288,14 +288,14 @@ public class PathResourceTests {
}
@Test
public void directoryOutputStream() throws Exception {
public void directoryOutputStream() throws IOException {
PathResource resource = new PathResource(TEST_DIR);
thrown.expect(FileNotFoundException.class);
resource.getOutputStream();
}
@Test
public void getReadableByteChannel() throws Exception {
public void getReadableByteChannel() throws IOException {
PathResource resource = new PathResource(TEST_FILE);
ReadableByteChannel channel = null;
try {
@@ -313,7 +313,7 @@ public class PathResourceTests {
}
@Test
public void getReadableByteChannelForDir() throws Exception {
public void getReadableByteChannelForDir() throws IOException {
PathResource resource = new PathResource(TEST_DIR);
try {
resource.readableChannel();
@@ -324,14 +324,14 @@ public class PathResourceTests {
}
@Test
public void getReadableByteChannelDoesNotExist() throws Exception {
public void getReadableByteChannelDoesNotExist() throws IOException {
PathResource resource = new PathResource(NON_EXISTING_FILE);
thrown.expect(NoSuchFileException.class);
thrown.expect(FileNotFoundException.class);
resource.readableChannel();
}
@Test
public void getWritableChannel() throws Exception {
public void getWritableChannel() throws IOException {
PathResource resource = new PathResource(temporaryFolder.newFile("test").toPath());
ByteBuffer buffer = ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8));
WritableByteChannel channel = null;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -201,8 +201,8 @@ public class ResourceTests {
return name;
}
@Override
public InputStream getInputStream() {
return null;
public InputStream getInputStream() throws IOException {
throw new FileNotFoundException();
}
};
@@ -211,21 +211,21 @@ public class ResourceTests {
fail("FileNotFoundException should have been thrown");
}
catch (FileNotFoundException ex) {
assertTrue(ex.getMessage().indexOf(name) != -1);
assertTrue(ex.getMessage().contains(name));
}
try {
resource.getFile();
fail("FileNotFoundException should have been thrown");
}
catch (FileNotFoundException ex) {
assertTrue(ex.getMessage().indexOf(name) != -1);
assertTrue(ex.getMessage().contains(name));
}
try {
resource.createRelative("/testing");
fail("FileNotFoundException should have been thrown");
}
catch (FileNotFoundException ex) {
assertTrue(ex.getMessage().indexOf(name) != -1);
assertTrue(ex.getMessage().contains(name));
}
assertThat(resource.getFilename(), nullValue());
@@ -235,19 +235,19 @@ public class ResourceTests {
public void testContentLength() throws IOException {
AbstractResource resource = new AbstractResource() {
@Override
public InputStream getInputStream() throws IOException {
public InputStream getInputStream() {
return new ByteArrayInputStream(new byte[] { 'a', 'b', 'c' });
}
@Override
public String getDescription() {
return null;
return "";
}
};
assertThat(resource.contentLength(), is(3L));
}
@Test
public void testGetReadableByteChannel() throws IOException {
public void testReadableChannel() throws IOException {
Resource resource = new FileSystemResource(getClass().getResource("Resource.class").getFile());
ReadableByteChannel channel = null;
try {
@@ -264,4 +264,24 @@ public class ResourceTests {
}
}
@Test(expected = FileNotFoundException.class)
public void testInputStreamNotFoundOnFileSystemResource() throws IOException {
new FileSystemResource(getClass().getResource("Resource.class").getFile()).createRelative("X").getInputStream();
}
@Test(expected = FileNotFoundException.class)
public void testReadableChannelNotFoundOnFileSystemResource() throws IOException {
new FileSystemResource(getClass().getResource("Resource.class").getFile()).createRelative("X").readableChannel();
}
@Test(expected = FileNotFoundException.class)
public void testInputStreamNotFoundOnClassPathResource() throws IOException {
new ClassPathResource("Resource.class", getClass()).createRelative("X").getInputStream();
}
@Test(expected = FileNotFoundException.class)
public void testReadableChannelNotFoundOnClassPathResource() throws IOException {
new ClassPathResource("Resource.class", getClass()).createRelative("X").readableChannel();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -303,7 +303,7 @@ public class DataBufferTests extends AbstractDataBufferAllocatingTestCase {
assertArrayEquals(new byte[]{'a', 'b', 'c', 'd'}, result);
release(buffer1);
release(buffer1, buffer2, buffer3);
}
@Test
@@ -461,5 +461,36 @@ public class DataBufferTests extends AbstractDataBufferAllocatingTestCase {
release(buffer);
}
@Test
public void spr16351() {
DataBuffer buffer = createDataBuffer(6);
byte[] bytes = {'a', 'b', 'c', 'd', 'e', 'f'};
buffer.write(bytes);
DataBuffer slice = buffer.slice(3, 3);
buffer.writePosition(3);
buffer.write(slice);
assertEquals(6, buffer.readableByteCount());
byte[] result = new byte[6];
buffer.read(result);
assertArrayEquals(bytes, result);
release(buffer);
}
@Test
public void join() {
DataBuffer composite = this.bufferFactory.join(Arrays.asList(stringBuffer("a"),
stringBuffer("b"), stringBuffer("c")));
assertEquals(3, composite.readableByteCount());
byte[] bytes = new byte[3];
composite.read(bytes);
assertArrayEquals(new byte[] {'a','b','c'}, bytes);
release(composite);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -31,6 +32,7 @@ import java.nio.file.StandardOpenOption;
import java.time.Duration;
import java.util.stream.Collectors;
import io.netty.buffer.ByteBuf;
import org.junit.Test;
import org.mockito.stubbing.Answer;
import reactor.core.publisher.Flux;
@@ -38,6 +40,7 @@ import reactor.test.StepVerifier;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
@@ -49,10 +52,11 @@ import static org.mockito.Mockito.*;
public class DataBufferUtilsTests extends AbstractDataBufferAllocatingTestCase {
@Test
public void readReadableByteChannel() throws Exception {
public void readByteChannel() throws Exception {
URI uri = DataBufferUtilsTests.class.getResource("DataBufferUtilsTests.txt").toURI();
FileChannel channel = FileChannel.open(Paths.get(uri), StandardOpenOption.READ);
Flux<DataBuffer> flux = DataBufferUtils.read(channel, this.bufferFactory, 3);
Flux<DataBuffer> flux =
DataBufferUtils.readByteChannel(() -> FileChannel.open(Paths.get(uri), StandardOpenOption.READ),
this.bufferFactory, 3);
StepVerifier.create(flux)
.consumeNextWith(stringConsumer("foo"))
@@ -61,16 +65,14 @@ public class DataBufferUtilsTests extends AbstractDataBufferAllocatingTestCase {
.consumeNextWith(stringConsumer("qux"))
.expectComplete()
.verify(Duration.ofSeconds(5));
assertFalse(channel.isOpen());
}
@Test
public void readAsynchronousFileChannel() throws Exception {
URI uri = DataBufferUtilsTests.class.getResource("DataBufferUtilsTests.txt").toURI();
AsynchronousFileChannel
channel = AsynchronousFileChannel.open(Paths.get(uri), StandardOpenOption.READ);
Flux<DataBuffer> flux = DataBufferUtils.read(channel, this.bufferFactory, 3);
Flux<DataBuffer> flux = DataBufferUtils.readAsynchronousFileChannel(
() -> AsynchronousFileChannel.open(Paths.get(uri), StandardOpenOption.READ),
this.bufferFactory, 3);
StepVerifier.create(flux)
.consumeNextWith(stringConsumer("foo"))
@@ -84,9 +86,9 @@ public class DataBufferUtilsTests extends AbstractDataBufferAllocatingTestCase {
@Test
public void readAsynchronousFileChannelPosition() throws Exception {
URI uri = DataBufferUtilsTests.class.getResource("DataBufferUtilsTests.txt").toURI();
AsynchronousFileChannel
channel = AsynchronousFileChannel.open(Paths.get(uri), StandardOpenOption.READ);
Flux<DataBuffer> flux = DataBufferUtils.read(channel, 3, this.bufferFactory, 3);
Flux<DataBuffer> flux = DataBufferUtils.readAsynchronousFileChannel(
() -> AsynchronousFileChannel.open(Paths.get(uri), StandardOpenOption.READ),
3, this.bufferFactory, 3);
StepVerifier.create(flux)
.consumeNextWith(stringConsumer("bar"))
@@ -96,26 +98,12 @@ public class DataBufferUtilsTests extends AbstractDataBufferAllocatingTestCase {
.verify(Duration.ofSeconds(5));
}
@Test
public void readUnalignedChannel() throws Exception {
URI uri = DataBufferUtilsTests.class.getResource("DataBufferUtilsTests.txt").toURI();
FileChannel channel = FileChannel.open(Paths.get(uri), StandardOpenOption.READ);
Flux<DataBuffer> flux = DataBufferUtils.read(channel, this.bufferFactory, 5);
StepVerifier.create(flux)
.consumeNextWith(stringConsumer("fooba"))
.consumeNextWith(stringConsumer("rbazq"))
.consumeNextWith(stringConsumer("ux"))
.expectComplete()
.verify(Duration.ofSeconds(5));
assertFalse(channel.isOpen());
}
@Test
public void readInputStream() throws Exception {
InputStream is = DataBufferUtilsTests.class.getResourceAsStream("DataBufferUtilsTests.txt");
Flux<DataBuffer> flux = DataBufferUtils.read(is, this.bufferFactory, 3);
Flux<DataBuffer> flux = DataBufferUtils.readInputStream(
() -> DataBufferUtilsTests.class.getResourceAsStream("DataBufferUtilsTests.txt"),
this.bufferFactory, 3);
StepVerifier.create(flux)
.consumeNextWith(stringConsumer("foo"))
@@ -293,27 +281,35 @@ public class DataBufferUtilsTests extends AbstractDataBufferAllocatingTestCase {
flux.subscribe(DataBufferUtils.releaseConsumer());
// AbstractDataBufferAllocatingTestCase.LeakDetector will assert the release of the buffers
assertReleased(foo);
assertReleased(bar);
assertReleased(baz);
}
private static void assertReleased(DataBuffer dataBuffer) {
if (dataBuffer instanceof NettyDataBuffer) {
ByteBuf byteBuf = ((NettyDataBuffer) dataBuffer).getNativeBuffer();
assertEquals(0, byteBuf.refCnt());
}
}
@Test
public void SPR16070() throws Exception {
ReadableByteChannel channel = mock(ReadableByteChannel.class);
when(channel.read(any()))
.thenAnswer(putByte(1))
.thenAnswer(putByte(2))
.thenAnswer(putByte(3))
.thenAnswer(putByte('a'))
.thenAnswer(putByte('b'))
.thenAnswer(putByte('c'))
.thenReturn(-1);
Flux<DataBuffer> read = DataBufferUtils.read(channel, this.bufferFactory, 1);
Flux<DataBuffer> read = DataBufferUtils.readByteChannel(() -> channel, this.bufferFactory, 1);
StepVerifier.create(
read.reduce(DataBuffer::write)
.map(this::dataBufferToBytes)
.map(this::encodeHexString)
)
.expectNext("010203")
.verifyComplete();
StepVerifier.create(read)
.consumeNextWith(stringConsumer("a"))
.consumeNextWith(stringConsumer("b"))
.consumeNextWith(stringConsumer("c"))
.expectComplete()
.verify(Duration.ofSeconds(5));
}
@@ -325,27 +321,19 @@ public class DataBufferUtilsTests extends AbstractDataBufferAllocatingTestCase {
};
}
private byte[] dataBufferToBytes(DataBuffer buffer) {
try {
int byteCount = buffer.readableByteCount();
byte[] bytes = new byte[byteCount];
buffer.read(bytes);
return bytes;
}
finally {
release(buffer);
}
}
@Test
public void join() {
DataBuffer foo = stringBuffer("foo");
DataBuffer bar = stringBuffer("bar");
DataBuffer baz = stringBuffer("baz");
Flux<DataBuffer> flux = Flux.just(foo, bar, baz);
private String encodeHexString(byte[] data) {
StringBuilder builder = new StringBuilder();
for (byte b : data) {
builder.append((0xF0 & b) >>> 4);
builder.append(0x0F & b);
}
return builder.toString();
}
DataBuffer result = DataBufferUtils.join(flux).block(Duration.ofSeconds(5));
assertEquals("foobarbaz", DataBufferTestUtils.dumpString(result, StandardCharsets.UTF_8));
release(result);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,10 +22,14 @@ import java.net.ServerSocket;
import java.util.SortedSet;
import javax.net.ServerSocketFactory;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.springframework.util.SocketUtils.*;
import static org.springframework.util.SocketUtils.PORT_RANGE_MIN;
import static org.springframework.util.SocketUtils.PORT_RANGE_MAX;
/**
* Unit tests for {@link SocketUtils}.
@@ -35,15 +39,20 @@ import static org.springframework.util.SocketUtils.*;
*/
public class SocketUtilsTests {
@Rule
public final ExpectedException exception = ExpectedException.none();
// TCP
@Test(expected = IllegalArgumentException.class)
@Test
public void findAvailableTcpPortWithZeroMinPort() {
exception.expect(IllegalArgumentException.class);
SocketUtils.findAvailableTcpPort(0);
}
@Test(expected = IllegalArgumentException.class)
@Test
public void findAvailableTcpPortWithNegativeMinPort() {
exception.expect(IllegalArgumentException.class);
SocketUtils.findAvailableTcpPort(-500);
}
@@ -53,11 +62,22 @@ public class SocketUtilsTests {
assertPortInRange(port, PORT_RANGE_MIN, PORT_RANGE_MAX);
}
@Test(expected = IllegalStateException.class)
@Test
public void findAvailableTcpPortWithMinPortEqualToMaxPort() {
int minMaxPort = SocketUtils.findAvailableTcpPort();
int port = SocketUtils.findAvailableTcpPort(minMaxPort, minMaxPort);
assertEquals(minMaxPort, port);
}
@Test
public void findAvailableTcpPortWhenPortOnLoopbackInterfaceIsNotAvailable() throws Exception {
int port = SocketUtils.findAvailableTcpPort();
ServerSocket socket = ServerSocketFactory.getDefault().createServerSocket(port, 1, InetAddress.getByName("localhost"));
try {
exception.expect(IllegalStateException.class);
exception.expectMessage(startsWith("Could not find an available TCP port"));
exception.expectMessage(endsWith("after 1 attempts"));
// will only look for the exact port
SocketUtils.findAvailableTcpPort(port, port);
}
finally {
@@ -99,21 +119,24 @@ public class SocketUtilsTests {
findAvailableTcpPorts(50, 40000, 45000);
}
@Test(expected = IllegalArgumentException.class)
@Test
public void findAvailableTcpPortsWithRequestedNumberGreaterThanSizeOfRange() {
exception.expect(IllegalArgumentException.class);
findAvailableTcpPorts(50, 45000, 45010);
}
// UDP
@Test(expected = IllegalArgumentException.class)
@Test
public void findAvailableUdpPortWithZeroMinPort() {
exception.expect(IllegalArgumentException.class);
SocketUtils.findAvailableUdpPort(0);
}
@Test(expected = IllegalArgumentException.class)
@Test
public void findAvailableUdpPortWithNegativeMinPort() {
exception.expect(IllegalArgumentException.class);
SocketUtils.findAvailableUdpPort(-500);
}
@@ -123,11 +146,14 @@ public class SocketUtilsTests {
assertPortInRange(port, PORT_RANGE_MIN, PORT_RANGE_MAX);
}
@Test(expected = IllegalStateException.class)
@Test
public void findAvailableUdpPortWhenPortOnLoopbackInterfaceIsNotAvailable() throws Exception {
int port = SocketUtils.findAvailableUdpPort();
DatagramSocket socket = new DatagramSocket(port, InetAddress.getByName("localhost"));
try {
exception.expect(IllegalStateException.class);
exception.expectMessage(startsWith("Could not find an available UDP port"));
exception.expectMessage(endsWith("after 1 attempts"));
// will only look for the exact port
SocketUtils.findAvailableUdpPort(port, port);
}
@@ -170,8 +196,9 @@ public class SocketUtilsTests {
findAvailableUdpPorts(50, 40000, 45000);
}
@Test(expected = IllegalArgumentException.class)
@Test
public void findAvailableUdpPortsWithRequestedNumberGreaterThanSizeOfRange() {
exception.expect(IllegalArgumentException.class);
findAvailableUdpPorts(50, 45000, 45010);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import org.springframework.asm.ClassWriter;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
/**
* Manages the class being generated by the compilation process.
@@ -153,10 +154,7 @@ public class CodeFlow implements Opcodes {
*/
@Nullable
public String lastDescriptor() {
if (this.compilationScopes.peek().isEmpty()) {
return null;
}
return this.compilationScopes.peek().get(this.compilationScopes.peek().size() - 1);
return CollectionUtils.lastElement(this.compilationScopes.peek());
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -458,8 +458,8 @@ public class ConstructorReference extends SpelNodeImpl {
mv.visitInsn(DUP);
// children[0] is the type of the constructor, don't want to include that in argument processing
SpelNodeImpl[] arguments = new SpelNodeImpl[children.length - 1];
System.arraycopy(children, 1, arguments, 0, children.length - 1);
SpelNodeImpl[] arguments = new SpelNodeImpl[this.children.length - 1];
System.arraycopy(this.children, 1, arguments, 0, this.children.length - 1);
generateCodeForArguments(mv, cf, constructor, arguments);
mv.visitMethodInsn(INVOKESPECIAL, classDesc, "<init>", CodeFlow.createSignatureDescriptor(constructor), false);
cf.pushDescriptor(this.exitTypeDescriptor);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,6 +47,7 @@ import org.springframework.util.ReflectionUtils;
* (right now), so the names must be unique.
*
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
*/
public class FunctionReference extends SpelNodeImpl {
@@ -56,9 +57,7 @@ public class FunctionReference extends SpelNodeImpl {
// Captures the most recently used method for the function invocation *if* the method
// can safely be used for compilation (i.e. no argument conversion is going on)
@Nullable
private Method method;
private boolean argumentConversionOccurred;
private volatile Method method;
public FunctionReference(String functionName, int pos, SpelNodeImpl... arguments) {
@@ -73,9 +72,8 @@ public class FunctionReference extends SpelNodeImpl {
if (value == TypedValue.NULL) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.FUNCTION_NOT_DEFINED, this.name);
}
// Two possibilities: a lambda function or a Java static method registered as a function
if (!(value.getValue() instanceof Method)) {
// Two possibilities: a lambda function or a Java static method registered as a function
throw new SpelEvaluationException(
SpelMessage.FUNCTION_REFERENCE_CANNOT_BE_INVOKED, this.name, value.getClass());
}
@@ -90,21 +88,22 @@ public class FunctionReference extends SpelNodeImpl {
}
/**
* Execute a function represented as a java.lang.reflect.Method.
* Execute a function represented as a {@code java.lang.reflect.Method}.
* @param state the expression evaluation state
* @param method the method to invoke
* @return the return value of the invoked Java method
* @throws EvaluationException if there is any problem invoking the method
*/
private TypedValue executeFunctionJLRMethod(ExpressionState state, Method method) throws EvaluationException {
this.method = null;
Object[] functionArgs = getArguments(state);
if (!method.isVarArgs() && method.getParameterCount() != functionArgs.length) {
throw new SpelEvaluationException(SpelMessage.INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION,
functionArgs.length, method.getParameterCount());
if (!method.isVarArgs()) {
int declaredParamCount = method.getParameterCount();
if (declaredParamCount != functionArgs.length) {
throw new SpelEvaluationException(SpelMessage.INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION,
functionArgs.length, declaredParamCount);
}
}
// Only static methods can be called in this way
if (!Modifier.isStatic(method.getModifiers())) {
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.FUNCTION_MUST_BE_STATIC, ClassUtils.getQualifiedMethodName(method), this.name);
@@ -112,25 +111,33 @@ public class FunctionReference extends SpelNodeImpl {
// Convert arguments if necessary and remap them for varargs if required
TypeConverter converter = state.getEvaluationContext().getTypeConverter();
argumentConversionOccurred = ReflectionHelper.convertAllArguments(converter, functionArgs, method);
boolean argumentConversionOccurred = ReflectionHelper.convertAllArguments(converter, functionArgs, method);
if (method.isVarArgs()) {
functionArgs = ReflectionHelper.setupArgumentsForVarargsInvocation(
method.getParameterTypes(), functionArgs);
}
boolean compilable = false;
try {
ReflectionUtils.makeAccessible(method);
Object result = method.invoke(method.getClass(), functionArgs);
if (!argumentConversionOccurred) {
this.method = method;
this.exitTypeDescriptor = CodeFlow.toDescriptor(method.getReturnType());
}
compilable = !argumentConversionOccurred;
return new TypedValue(result, new TypeDescriptor(new MethodParameter(method, -1)).narrow(result));
}
catch (Exception ex) {
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_FUNCTION_CALL,
this.name, ex.getMessage());
}
finally {
if (compilable) {
this.exitTypeDescriptor = CodeFlow.toDescriptor(method.getReturnType());
this.method = method;
}
else {
this.exitTypeDescriptor = null;
this.method = null;
}
}
}
@Override
@@ -162,12 +169,13 @@ public class FunctionReference extends SpelNodeImpl {
@Override
public boolean isCompilable() {
if (this.method == null || this.argumentConversionOccurred) {
Method method = this.method;
if (method == null) {
return false;
}
int methodModifiers = this.method.getModifiers();
int methodModifiers = method.getModifiers();
if (!Modifier.isStatic(methodModifiers) || !Modifier.isPublic(methodModifiers) ||
!Modifier.isPublic(this.method.getDeclaringClass().getModifiers())) {
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
return false;
}
for (SpelNodeImpl child : this.children) {
@@ -179,12 +187,13 @@ public class FunctionReference extends SpelNodeImpl {
}
@Override
public void generateCode(MethodVisitor mv,CodeFlow cf) {
Assert.state(this.method != null, "No method handle");
String classDesc = this.method.getDeclaringClass().getName().replace('.', '/');
generateCodeForArguments(mv, cf, this.method, this.children);
mv.visitMethodInsn(INVOKESTATIC, classDesc, this.method.getName(),
CodeFlow.createSignatureDescriptor(this.method), false);
public void generateCode(MethodVisitor mv, CodeFlow cf) {
Method method = this.method;
Assert.state(method != null, "No method handle");
String classDesc = method.getDeclaringClass().getName().replace('.', '/');
generateCodeForArguments(mv, cf, method, this.children);
mv.visitMethodInsn(INVOKESTATIC, classDesc, method.getName(),
CodeFlow.createSignatureDescriptor(method), false);
cf.pushDescriptor(this.exitTypeDescriptor);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@@ -31,6 +31,7 @@ import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
/**
@@ -160,7 +161,7 @@ public class Selection extends SpelNodeImpl {
}
if (this.variant == LAST) {
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result.get(result.size() - 1)), this);
return new ValueRef.TypedValueHolderValueRef(new TypedValue(CollectionUtils.lastElement(result)), this);
}
if (operand instanceof Iterable) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@ import org.springframework.util.ObjectUtils;
* format expression.
*
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
*/
public abstract class SpelNodeImpl implements SpelNode, Opcodes {
@@ -46,7 +47,7 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
private static SpelNodeImpl[] NO_CHILDREN = new SpelNodeImpl[0];
protected int pos; // start = top 16bits, end = bottom 16bits
protected int pos; // start = top 16bits, end = bottom 16bits
protected SpelNodeImpl[] children = SpelNodeImpl.NO_CHILDREN;
@@ -81,7 +82,7 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
/**
* @return true if the next child is one of the specified classes
* Return {@code true} if the next child is one of the specified classes.
*/
protected boolean nextChildIs(Class<?>... clazzes) {
if (this.parent != null) {
@@ -123,8 +124,7 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
@Override
public void setValue(ExpressionState expressionState, @Nullable Object newValue) throws EvaluationException {
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.SETVALUE_NOT_SUPPORTED, getClass());
throw new SpelEvaluationException(getStartPosition(), SpelMessage.SETVALUE_NOT_SUPPORTED, getClass());
}
@Override
@@ -281,4 +281,5 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
}
cf.exitCompilationScope();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -4988,7 +4988,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
}
@Override
public void generateCode(String propertyName, MethodVisitor mv,CodeFlow cf) {
public void generateCode(String propertyName, MethodVisitor mv, CodeFlow cf) {
if (method == null) {
try {
method = Payload2.class.getDeclaredMethod("getField", String.class);
@@ -5005,7 +5005,8 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
mv.visitTypeInsn(CHECKCAST, memberDeclaringClassSlashedDescriptor);
}
mv.visitLdcInsn(propertyName);
mv.visitMethodInsn(INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor, method.getName(),CodeFlow.createSignatureDescriptor(method),false);
mv.visitMethodInsn(INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor, method.getName(),
CodeFlow.createSignatureDescriptor(method), false);
}
}
@@ -1,4 +1,4 @@
This meta-project is used to generate a bill-of-materials POM that contains the other
projects in a dependencyManagement section.
<http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/overview.html#overview-maven-bom>
<https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/overview.html#overview-getting-started>
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -58,7 +58,7 @@ public class ArgumentPreparedStatementSetter implements PreparedStatementSetter,
* @param ps the PreparedStatement
* @param parameterPosition index of the parameter position
* @param argValue the value to set
* @throws SQLException
* @throws SQLException if thrown by PreparedStatement methods
*/
protected void doSetValue(PreparedStatement ps, int parameterPosition, Object argValue) throws SQLException {
if (argValue instanceof SqlParameterValue) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -92,7 +92,7 @@ public class ArgumentTypePreparedStatementSetter implements PreparedStatementSet
* @param parameterPosition index of the parameter position
* @param argType the argument type
* @param argValue the argument value
* @throws SQLException
* @throws SQLException if thrown by PreparedStatement methods
*/
protected void doSetValue(PreparedStatement ps, int parameterPosition, int argType, Object argValue)
throws SQLException {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.springframework.jdbc.core;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
@@ -35,18 +36,18 @@ import org.springframework.util.Assert;
*/
public class SqlParameter {
/** The name of the parameter, if any */
// The name of the parameter, if any
@Nullable
private String name;
/** SQL type constant from {@code java.sql.Types} */
// SQL type constant from {@code java.sql.Types}
private final int sqlType;
/** Used for types that are user-named like: STRUCT, DISTINCT, JAVA_OBJECT, named array types */
// Used for types that are user-named like: STRUCT, DISTINCT, JAVA_OBJECT, named array types
@Nullable
private String typeName;
/** The scale to apply in case of a NUMERIC or DECIMAL type, if any */
// The scale to apply in case of a NUMERIC or DECIMAL type, if any
@Nullable
private Integer scale;
@@ -184,12 +185,16 @@ public class SqlParameter {
* to a List of SqlParameter objects as used in this package.
*/
public static List<SqlParameter> sqlTypesToAnonymousParameterList(@Nullable int... types) {
List<SqlParameter> result = new LinkedList<>();
List<SqlParameter> result;
if (types != null) {
result = new ArrayList<>(types.length);
for (int type : types) {
result.add(new SqlParameter(type));
}
}
else {
result = new LinkedList<>();
}
return result;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -53,47 +53,47 @@ import org.springframework.util.StringUtils;
*/
public class CallMetaDataContext {
/** Logger available to subclasses */
// Logger available to subclasses
protected final Log logger = LogFactory.getLog(getClass());
/** name of procedure to call **/
// Name of procedure to call
@Nullable
private String procedureName;
/** name of catalog for call **/
// Name of catalog for call
@Nullable
private String catalogName;
/** name of schema for call **/
// Name of schema for call
@Nullable
private String schemaName;
/** List of SqlParameter objects to be used in call execution */
// List of SqlParameter objects to be used in call execution
private List<SqlParameter> callParameters = new ArrayList<>();
/** Actual name to use for the return value in the output map */
// Actual name to use for the return value in the output map
@Nullable
private String actualFunctionReturnName;
/** Set of in parameter names to exclude use for any not listed */
// Set of in parameter names to exclude use for any not listed
private Set<String> limitedInParameterNames = new HashSet<>();
/** List of SqlParameter names for out parameters */
// List of SqlParameter names for out parameters
private List<String> outParameterNames = new ArrayList<>();
/** Indicates whether this is a procedure or a function **/
// Indicates whether this is a procedure or a function
private boolean function = false;
/** Indicates whether this procedure's return value should be included **/
// Indicates whether this procedure's return value should be included
private boolean returnValueRequired = false;
/** Should we access call parameter meta data info or not */
// Should we access call parameter meta data info or not
private boolean accessCallParameterMetaData = true;
/** Should we bind parameter by name **/
// Should we bind parameter by name
private boolean namedBinding;
/** The provider of call meta data */
// The provider of call meta data
@Nullable
private CallMetaDataProvider metaDataProvider;
@@ -274,7 +274,8 @@ public class CallMetaDataContext {
return new SqlOutParameter(parameterName, provider.getRefCursorSqlType(), rowMapper);
}
else {
throw new InvalidDataAccessApiUsageException("Return of a ResultSet from a stored procedure is not supported.");
throw new InvalidDataAccessApiUsageException(
"Return of a ResultSet from a stored procedure is not supported.");
}
}
}
@@ -493,31 +494,37 @@ public class CallMetaDataContext {
}
if (parameterName != null) {
if (parameterSource.hasValue(parameterName)) {
matchedParameters.put(parameterName, SqlParameterSourceUtils.getTypedValue(parameterSource, parameterName));
matchedParameters.put(parameterName,
SqlParameterSourceUtils.getTypedValue(parameterSource, parameterName));
}
else {
String lowerCaseName = parameterName.toLowerCase();
if (parameterSource.hasValue(lowerCaseName)) {
matchedParameters.put(parameterName, SqlParameterSourceUtils.getTypedValue(parameterSource, lowerCaseName));
matchedParameters.put(parameterName,
SqlParameterSourceUtils.getTypedValue(parameterSource, lowerCaseName));
}
else {
String englishLowerCaseName = parameterName.toLowerCase(Locale.ENGLISH);
if (parameterSource.hasValue(englishLowerCaseName)) {
matchedParameters.put(parameterName, SqlParameterSourceUtils.getTypedValue(parameterSource, englishLowerCaseName));
matchedParameters.put(parameterName,
SqlParameterSourceUtils.getTypedValue(parameterSource, englishLowerCaseName));
}
else {
String propertyName = JdbcUtils.convertUnderscoreNameToPropertyName(parameterName);
if (parameterSource.hasValue(propertyName)) {
matchedParameters.put(parameterName, SqlParameterSourceUtils.getTypedValue(parameterSource, propertyName));
matchedParameters.put(parameterName,
SqlParameterSourceUtils.getTypedValue(parameterSource, propertyName));
}
else {
if (caseInsensitiveParameterNames.containsKey(lowerCaseName)) {
String sourceName = caseInsensitiveParameterNames.get(lowerCaseName);
matchedParameters.put(parameterName, SqlParameterSourceUtils.getTypedValue(parameterSource, sourceName));
matchedParameters.put(parameterName,
SqlParameterSourceUtils.getTypedValue(parameterSource, sourceName));
}
else {
logger.warn("Unable to locate the corresponding parameter value for '" + parameterName +
"' within the parameter values provided: " + caseInsensitiveParameterNames.values());
logger.warn("Unable to locate the corresponding parameter value for '" +
parameterName + "' within the parameter values provided: " +
caseInsensitiveParameterNames.values());
}
}
}
@@ -567,8 +574,8 @@ public class CallMetaDataContext {
value = ((SqlParameterValue)value).getValue();
}
if (value != null) {
logger.debug("Unable to locate the corresponding IN or IN-OUT parameter for \"" + parameterName +
"\" in the parameters used: " + callParameterNames.keySet());
logger.debug("Unable to locate the corresponding IN or IN-OUT parameter for \"" +
parameterName + "\" in the parameters used: " + callParameterNames.keySet());
}
}
}
@@ -669,12 +676,7 @@ public class CallMetaDataContext {
* @since 4.2
*/
protected String createParameterBinding(SqlParameter parameter) {
if (isNamedBinding()) {
return parameter.getName() + " => ?";
}
else {
return "?";
}
return (isNamedBinding() ? parameter.getName() + " => ?" : "?");
}
private static String lowerCase(@Nullable String paramName) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -45,35 +45,35 @@ import org.springframework.util.Assert;
*/
public class TableMetaDataContext {
/** Logger available to subclasses */
// Logger available to subclasses
protected final Log logger = LogFactory.getLog(getClass());
/** Name of table for this context */
// Name of table for this context
@Nullable
private String tableName;
/** Name of catalog for this context */
// Name of catalog for this context
@Nullable
private String catalogName;
/** Name of schema for this context */
// Name of schema for this context
@Nullable
private String schemaName;
/** List of columns objects to be used in this context */
// List of columns objects to be used in this context
private List<String> tableColumns = new ArrayList<>();
/** should we access insert parameter meta data info or not */
// Should we access insert parameter meta data info or not
private boolean accessTableColumnMetaData = true;
/** should we override default for including synonyms for meta data lookups */
// Should we override default for including synonyms for meta data lookups
private boolean overrideIncludeSynonymsDefault = false;
/** the provider of table meta data */
// The provider of table meta data
@Nullable
private TableMetaDataProvider metaDataProvider;
/** are we using generated key columns */
// Are we using generated key columns
private boolean generatedKeyColumnsUsed = false;
@@ -206,8 +206,8 @@ public class TableMetaDataContext {
*/
public List<Object> matchInParameterValuesWithInsertColumns(SqlParameterSource parameterSource) {
List<Object> values = new ArrayList<>();
// for parameter source lookups we need to provide caseinsensitive lookup support since the
// database metadata is not necessarily providing case sensitive column names
// For parameter source lookups we need to provide case-insensitive lookup support since the
// database metadata is not necessarily providing case-sensitive column names
Map<String, String> caseInsensitiveParameterNames =
SqlParameterSourceUtils.extractCaseInsensitiveParameterNames(parameterSource);
for (String column : this.tableColumns) {
@@ -226,9 +226,8 @@ public class TableMetaDataContext {
}
else {
if (caseInsensitiveParameterNames.containsKey(lowerCaseName)) {
values.add(
SqlParameterSourceUtils.getTypedValue(parameterSource,
caseInsensitiveParameterNames.get(lowerCaseName)));
values.add(SqlParameterSourceUtils.getTypedValue(
parameterSource, caseInsensitiveParameterNames.get(lowerCaseName)));
}
else {
values.add(null);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -73,6 +73,26 @@ public class BeanPropertySqlParameterSource extends AbstractSqlParameterSource {
}
}
/**
* Derives a default SQL type from the corresponding property type.
* @see org.springframework.jdbc.core.StatementCreatorUtils#javaTypeToSqlParameterType
*/
@Override
public int getSqlType(String paramName) {
int sqlType = super.getSqlType(paramName);
if (sqlType != TYPE_UNKNOWN) {
return sqlType;
}
Class<?> propType = this.beanWrapper.getPropertyType(paramName);
return StatementCreatorUtils.javaTypeToSqlParameterType(propType);
}
@Override
@Nullable
public String[] getParameterNames() {
return getReadablePropertyNames();
}
/**
* Provide access to the property names of the wrapped bean.
* Uses support provided in the {@link PropertyAccessor} interface.
@@ -92,18 +112,4 @@ public class BeanPropertySqlParameterSource extends AbstractSqlParameterSource {
return this.propertyNames;
}
/**
* Derives a default SQL type from the corresponding property type.
* @see org.springframework.jdbc.core.StatementCreatorUtils#javaTypeToSqlParameterType
*/
@Override
public int getSqlType(String paramName) {
int sqlType = super.getSqlType(paramName);
if (sqlType != TYPE_UNKNOWN) {
return sqlType;
}
Class<?> propType = this.beanWrapper.getPropertyType(paramName);
return StatementCreatorUtils.javaTypeToSqlParameterType(propType);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,4 +54,10 @@ public class EmptySqlParameterSource implements SqlParameterSource {
return null;
}
@Override
@Nullable
public String[] getParameterNames() {
return null;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import java.util.Map;
import org.springframework.jdbc.core.SqlParameterValue;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* {@link SqlParameterSource} implementation that holds a given Map of parameters.
@@ -163,4 +164,10 @@ public class MapSqlParameterSource extends AbstractSqlParameterSource {
return this.values.get(paramName);
}
@Override
@Nullable
public String[] getParameterNames() {
return StringUtils.toStringArray(this.values.keySet());
}
}
@@ -105,14 +105,26 @@ public class NamedParameterJdbcTemplate implements NamedParameterJdbcOperations
/**
* Expose the classic Spring JdbcTemplate to allow invocation of
* less commonly used methods.
* Expose the classic Spring JdbcTemplate operations to allow invocation
* of less commonly used methods.
*/
@Override
public JdbcOperations getJdbcOperations() {
return this.classicJdbcTemplate;
}
/**
* Expose the classic Spring {@link JdbcTemplate} itself, if available,
* in particular for passing it on to other {@code JdbcTemplate} consumers.
* <p>If sufficient for the purposes at hand, {@link #getJdbcOperations()}
* is recommended over this variant.
* @since 5.0.3
*/
public JdbcTemplate getJdbcTemplate() {
Assert.state(this.classicJdbcTemplate instanceof JdbcTemplate, "No JdbcTemplate available");
return (JdbcTemplate) this.classicJdbcTemplate;
}
/**
* Specify the maximum number of entries for this template's SQL cache.
* Default is 256.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -42,24 +41,33 @@ import org.springframework.util.Assert;
*/
public abstract class NamedParameterUtils {
/**
* Set of characters that qualify as parameter separators,
* indicating that a parameter name in a SQL String has ended.
*/
private static final char[] PARAMETER_SEPARATORS =
new char[] {'"', '\'', ':', '&', ',', ';', '(', ')', '|', '=', '+', '-', '*', '%', '/', '\\', '<', '>', '^'};
/**
* Set of characters that qualify as comment or quotes starting characters.
*/
private static final String[] START_SKIP =
new String[] {"'", "\"", "--", "/*"};
private static final String[] START_SKIP = new String[] {"'", "\"", "--", "/*"};
/**
* Set of characters that at are the corresponding comment or quotes ending characters.
*/
private static final String[] STOP_SKIP =
new String[] {"'", "\"", "\n", "*/"};
private static final String[] STOP_SKIP = new String[] {"'", "\"", "\n", "*/"};
/**
* Set of characters that qualify as parameter separators,
* indicating that a parameter name in a SQL String has ended.
*/
private static final String PARAMETER_SEPARATORS = "\"':&,;()|=+-*%/\\<>^";
/**
* An index with separator flags per character code.
* Technically only needed between 34 and 124 at this point.
*/
private static final boolean[] separatorIndex = new boolean[128];
static {
for (char c : PARAMETER_SEPARATORS.toCharArray()) {
separatorIndex[c] = true;
}
}
//-------------------------------------------------------------------------
@@ -233,7 +241,6 @@ public abstract class NamedParameterUtils {
// character sequence ending comment or quote not found
return statement.length;
}
}
}
return position;
@@ -384,15 +391,7 @@ public abstract class NamedParameterUtils {
* that is, whether the given character qualifies as a separator.
*/
private static boolean isParameterSeparator(char c) {
if (Character.isWhitespace(c)) {
return true;
}
for (char separator : PARAMETER_SEPARATORS) {
if (c == separator) {
return true;
}
}
return false;
return (separatorIndex[c] || Character.isWhitespace(c));
}
/**
@@ -423,10 +422,10 @@ public abstract class NamedParameterUtils {
*/
public static List<SqlParameter> buildSqlParameterList(ParsedSql parsedSql, SqlParameterSource paramSource) {
List<String> paramNames = parsedSql.getParameterNames();
List<SqlParameter> params = new LinkedList<>();
List<SqlParameter> params = new ArrayList<>(paramNames.size());
for (String paramName : paramNames) {
params.add(
new SqlParameter(paramName, paramSource.getSqlType(paramName), paramSource.getTypeName(paramName)));
params.add(new SqlParameter(
paramName, paramSource.getSqlType(paramName), paramSource.getTypeName(paramName)));
}
return params;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -73,7 +73,9 @@ public interface SqlParameterSource {
* or {@code TYPE_UNKNOWN} if not known
* @see #TYPE_UNKNOWN
*/
int getSqlType(String paramName);
default int getSqlType(String paramName) {
return TYPE_UNKNOWN;
}
/**
* Determine the type name for the specified named parameter.
@@ -82,6 +84,22 @@ public interface SqlParameterSource {
* or {@code null} if not known
*/
@Nullable
String getTypeName(String paramName);
default String getTypeName(String paramName) {
return null;
}
/**
* Extract all available parameter names if possible.
* <p>This is an optional operation, primarily for use with
* {@link org.springframework.jdbc.core.simple.SimpleJdbcInsert}
* and {@link org.springframework.jdbc.core.simple.SimpleJdbcCall}.
* @return the array of parameter names, or {@code null} if not determinable
* @since 5.0.3
* @see SqlParameterSourceUtils#extractCaseInsensitiveParameterNames
*/
@Nullable
default String[] getParameterNames() {
return null;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,7 +42,7 @@ public abstract class SqlParameterSourceUtils {
* @return an array of {@link SqlParameterSource}
* @see MapSqlParameterSource
* @see BeanPropertySqlParameterSource
* @see NamedParameterJdbcTemplate#batchUpdate(String, SqlParameterSource[]))
* @see NamedParameterJdbcTemplate#batchUpdate(String, SqlParameterSource[])
*/
@SuppressWarnings("unchecked")
public static SqlParameterSource[] createBatch(Object... candidates) {
@@ -58,7 +58,7 @@ public abstract class SqlParameterSourceUtils {
* @since 5.0.2
* @see MapSqlParameterSource
* @see BeanPropertySqlParameterSource
* @see NamedParameterJdbcTemplate#batchUpdate(String, SqlParameterSource[]))
* @see NamedParameterJdbcTemplate#batchUpdate(String, SqlParameterSource[])
*/
@SuppressWarnings("unchecked")
public static SqlParameterSource[] createBatch(Collection<?> candidates) {
@@ -117,14 +117,9 @@ public abstract class SqlParameterSourceUtils {
*/
public static Map<String, String> extractCaseInsensitiveParameterNames(SqlParameterSource parameterSource) {
Map<String, String> caseInsensitiveParameterNames = new HashMap<>();
if (parameterSource instanceof BeanPropertySqlParameterSource) {
String[] propertyNames = ((BeanPropertySqlParameterSource) parameterSource).getReadablePropertyNames();
for (String name : propertyNames) {
caseInsensitiveParameterNames.put(name.toLowerCase(), name);
}
}
else if (parameterSource instanceof MapSqlParameterSource) {
for (String name : ((MapSqlParameterSource) parameterSource).getValues().keySet()) {
String[] paramNames = parameterSource.getParameterNames();
if (paramNames != null) {
for (String name : paramNames) {
caseInsensitiveParameterNames.put(name.toLowerCase(), name);
}
}

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