Commit Graph
4863 Commits
Author SHA1 Message Date
Sam Brannen 42fe29218b Merge branch '7.0.x' 2026-09-05 13:42:41 +02:00
Sam Brannen 35d8c4d06f Align synthesized annotation toString() with JDK for NaN/Infinity
Closes gh-37244
2026-09-05 13:37:06 +02:00
Sam Brannen c1d241928c Rename maxAttemptsReached() to maxElapsedTimeReached() and organize tests 2026-09-05 13:08:12 +02:00
Brian Clozel 9bedb06b9b Merge branch '7.0.x' 2026-09-04 17:34:11 +02:00
junhyeong9812 4a803961bc Generate compilable code for non-finite floating-point values
PrimitiveDelegate generated code via "$LF" for Float and "(double) $L"
for Double, which emit the value's toString() verbatim. For NaN and
infinities this produced non-compilable source such as "NaNF" or
"(double) Infinity", causing the generated AOT sources to fail to
compile.

Detect NaN (via isNaN, since NaN is never equal to itself) and the
positive/negative infinities, emitting the corresponding constant
field references (Float.NaN, Double.POSITIVE_INFINITY, etc.) through
the "$T" placeholder. Finite values keep their existing handling.

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-09-04 17:23:41 +02:00
Brian Clozel 54b3a8c868 Merge branch '7.0.x' 2026-09-04 17:17:23 +02:00
junhyeong9812 2b5229ff8f Fix reserveMethodNames to reserve each supplied name
GeneratedClass.reserveMethodNames(String...) passed the entire varargs
array to MethodName.of() inside the per-name loop instead of the current
element. Since MethodName.of(String...) joins all parts into a single
camel-case name, reserving two or more names (for example "apply" and
"test") produced "applyTest", and the per-element check
Assert.state(generatedName.equals(reservedMethodName)) failed with an
IllegalStateException. Single-name calls worked only by accident.

Reserve each supplied name individually by passing the loop variable.

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-09-04 17:14:25 +02:00
Patrick Strawderman 658c263cf6 Use immutable map for static cache in TypeDescriptor
Switch to Map.of for the static commonTypesCache field for immutability.

Signed-off-by: Patrick Strawderman <pstrawderman@netflix.com>
2026-09-04 15:38:40 +02:00
Sam Brannen 21ee87448c Merge branch '7.0.x' 2026-09-04 15:28:48 +02:00
Hyunwoo Jung 8c151f5887 Move BackOff tests to correct package
Closes gh-37241

Signed-off-by: Hyunwoo Jung <hyunwoojung@kakao.com>
2026-09-04 15:27:27 +02:00
Sam Brannen c0519b9bbf Merge branch '7.0.x' 2026-09-04 15:14:36 +02:00
Sam Brannen ea2a26206c Polish contribution
See gh-36914
2026-09-04 15:13:35 +02:00
junhyeong9812 2b276311eb Honor sourceStart offset in AbstractXMLStreamReader#getTextCharacters
Prior to this commit, AbstractXMLStreamReader.getTextCharacters(int
sourceStart, char[], int, int) capped the copy length with
Math.min(length, source.length), ignoring sourceStart. When sourceStart
> 0 and sourceStart + length exceeds the text length, System.arraycopy
read past the end of the source array and threw
ArrayIndexOutOfBoundsException, contrary to the
XMLStreamReader#getTextCharacters contract (copy up to length
characters starting at sourceStart and return the number copied).

To address that, this commit caps the length by the number of
characters remaining from sourceStart.

Closes gh-36914

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-09-04 15:13:35 +02:00
Sam Brannen b5a358019f Polishing 2026-09-04 15:13:35 +02:00
Brian Clozel 136dddb67d Merge branch '7.0.x' 2026-09-03 17:38:29 +02:00
Brian Clozel 6316c06a97 Polishing contribution
See gh-36919
2026-09-03 17:36:26 +02:00
junhyeong9812 ad83d5ebd9 Render parameter type names in ClassFileMethodMetadata
ClassFileMethodMetadata's toString() formatted method parameter types
as packageName() + "." + displayName(). Since ClassDesc.packageName()
is empty for primitive, array and default-package types, these rendered
with a leading dot (for example ".int" and ".String[]") and reference
arrays lost their package. The return type already uses
ClassFileAnnotationMetadata.resolveTypeName(); apply it to the
parameters as well.

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-09-03 17:23:18 +02:00
Sam Brannen ff9192fa8f Merge branch '7.0.x' 2026-09-03 11:49:10 +02:00
Chengang Guan f2fe79e56d Use ArrayList instead of LinkedList in CompositeRetryListener
CompositeRetryListener currently uses LinkedList to store registered
listeners. The primary operation on this list is iteration (traversing
all listeners on every retry lifecycle event). ArrayList provides
better iteration performance due to better cache locality and lower
memory overhead.

Closes gh-37231

Signed-off-by: Chengang Guan <guanchengang@qq.com>
2026-09-03 11:45:36 +02:00
Artyom Tsvirko 2028eb3694 Handle MIME type parameter names case-insensitively
MIME type parameter names are case-insensitive, and MimeType already
stores them in a LinkedCaseInsensitiveMap. Several code paths, however,
still compared them with case-sensitive String.equals().

As a result, MimeType.hashCode() disagreed with MimeType.equals() for
parameter names that differ only in case, breaking the equals/hashCode
contract: text/plain;FOO=bar and text/plain;foo=bar are equal but hash
differently, so one is not found in a hash-based collection holding the
other. MimeType.compareTo() had the same blind spot for the charset
parameter.

MediaType was affected in two further ways: an out-of-range quality
value escaped validation when spelled Q=, and removeQualityValue() left
a Q= parameter in place.

Signed-off-by: Artyom Tsvirko <36863599+lArtiquel@users.noreply.github.com>
2026-08-31 17:30:39 +02:00
junhyeong9812andYash bc66622342 Reject MIME type parameters differing only in case
MIME type parameter names are case-insensitive, but MimeTypeParser
accumulates parameters in a case-sensitive LinkedHashMap. As a result,
duplicate parameters differing only in case (such as "charset" and
"CHARSET") were not rejected and were silently collapsed to the last
value by the case-insensitive parameter map of MimeType.

Accumulate parameters in a LinkedCaseInsensitiveMap so that duplicates
differing only in case map to the same key and are rejected consistently
with exact duplicates.

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
Co-authored-by: Yash <190389954+yashsiwacha@users.noreply.github.com>
2026-08-31 16:23:20 +02:00
Brian Clozel 5524fc6a55 Merge branch '7.0.x' 2026-08-31 12:58:00 +02:00
Manu Sridharan fce57adc31 Update to NullAway 0.14.0 and fix new warnings
See gh-37188

Signed-off-by: Manu Sridharan <msridhar@gmail.com>
2026-08-31 12:57:32 +02:00
Yanming Zhou 2588fb078e Polish ConcurrentLruCache to refine null-safety
Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2026-08-28 14:53:40 +02:00
Istvan Verhas 752193fca9 Refactor JettyDataBuffer with new JettyVirtualDataBuffer
This commits simplifies the Jetty buffer support by consolidating
the shared logic and delegating operations to the parent class
thanks to the new `JettyVirtualDataBuffer`.

Signed-off-by: Istvan Verhas <vi@mocker.guru>
2026-08-28 14:42:41 +02:00
junhyung8795 17e0daf8a1 Use computeIfAbsent in CommandLineArgs.addOptionArg
Signed-off-by: junhyung8795 <junhyung8795@naver.com>
2026-08-27 17:00:41 +02:00
Brian Clozel df72838ce1 Merge commit 'v7.1.0-M1~1' 2026-08-20 18:18:13 +02:00
Brian Clozel 0e9a1d72f5 Merge commit 'v7.0.9~1' into 7.0.x 2026-08-20 18:17:01 +02:00
Sam Brannen 6ee3ef6af5 Avoid unnecessarily synthesizing meta-annotations with attributes
In commit 622fc3edf7, I introduced a check in
TypeMappedAnnotation#isSynthesizable() intended to force synthesis when
an attribute value needs to be resolved from a different level of a
multi-level annotation hierarchy whose root annotation does not
redeclare the target attribute itself.

That check tested if `resolvedMirrors.length > 0` for a
meta-annotation; however, resolvedMirrors is always sized according to
the number of attributes declared by the mapped annotation type,
regardless of whether any of those attributes actually participate in
mirroring or an @⁠AliasFor override. As a result, the check effectively
synthesized any meta-annotation that declares at least one attribute,
which reintroduced the unnecessary-synthesis behavior that commit
d6768ccc18 had fixed, merely narrowed to meta-annotations with
attributes.

This commit replaces that overly broad check with a precise one in
AnnotationTypeMapping#computeSynthesizableFlag(), which now also
considers whether any attribute's value must be resolved from a
different annotation in the meta-annotation hierarchy (tracked via
annotationValueSource). This correctly identifies the original
multi-level hierarchy scenario without over-matching on ordinary
meta-annotations that have nothing to merge or override.

See gh-28704
See gh-28716
Closes gh-37135
2026-08-20 16:32:05 +02:00
greg taube 59a784cb7e Avoid unnecessary allocations for cached annotation mappings
This commit defers creation of the visited annotation types set until a
cache miss occurs, which avoids allocating a HashSet for every cached
annotation mapping lookup while preserving recursive annotation
handling during mapping creation.

Closes gh-37141

Signed-off-by: GT <gregjotau@gmail.com>
2026-08-20 16:27:18 +02:00
김준형 af466ccf63 Fix OptionalToObjectConverter applicability check
OptionalToObjectConverter.matches() used
TypeDescriptor.getElementTypeDescriptor(), which returns null for an
Optional (element types are only resolved for arrays, streams and
collections). ConversionUtils.canConvertElements() then treats a null
source element type as "maybe" and returns true unconditionally, so
ConversionService.canConvert(Optional<X>, target) reported true even
when X is not convertible to the target -- a violation of the
canConvert contract, since the subsequent conversion fails.

To address that, this commit resolves the Optional's element type from
its generic and checks it against the target, mirroring
ObjectToOptionalConverter. A raw or otherwise unresolved element type
remains permissive.

Closes gh-36913

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-08-20 16:19:37 +02:00
junhyeong9812 e8e293a706 Reject write methods not starting with "set" in Property
Prior to this commit, Property.resolveName() located the "set" prefix
of a write method with String.indexOf(), which matches the token
anywhere in the method name. A write method that merely contains "set"
(for example "offsetX" or "upset") was silently accepted and resolved
to a meaningless property name derived from whatever follows the token,
while only names with no "set" token at all were rejected.

To address that, this commit matches the "set" prefix only at the start
of the method name via startsWith(), so that an
IllegalArgumentException is consistently thrown for any write method
candidate that is not a setter.

See gh-36911
Closes gh-37139

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-08-19 18:26:46 +02:00
김준형 f067d40f0a Fix Property name resolution for record-style accessors
Property.resolveName() located the get/is accessor prefix with
String.indexOf, which matches the prefix anywhere in the method name.
A plain accessor whose name embeds such a prefix (for example
budget()) had the wrong portion stripped and resolved to an empty or
wrong property name, which in turn caused the backing field's
annotations to be silently dropped.

Match the get/is prefix only at the start of the method name and do
not strip it when the method is a plain accessor for a data class,
that is, a non-static no-arg method referring to an instance field of
the same name. This supports Java records, Kotlin data classes, and
custom Java data classes alike, without relying on java.lang.Record.

As a consequence, a getter backed by a field of the exact same name
(for example isUrgent()) now resolves to the field name.

Closes gh-36911

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-08-19 18:19:04 +02:00
Sam Brannen a947f9bf79 Merge branch '7.0.x' 2026-08-18 17:14:31 +02:00
Sam Brannen a4720ccf77 Guard all AsynchronousFileChannel#write call sites in DataBufferUtils
Prior to this commit, only the very first AsynchronousFileChannel#write
call in DataBufferUtils$WriteCompletionHandler#hookOnNext(DataBuffer)
was guarded against exceptions escaping synchronously, and even then
only via `catch (RuntimeException ex)`, per the original fix for
gh-36184.

While widening that guard to match the read side's `catch (Throwable
ex)` combined with `Exceptions.throwIfFatal(ex)` (see gh-37143), we
discovered that completed(Integer, Attachment) contains two more direct
`this.channel.write(...)` calls -- for continuing a partial write and
for advancing to the next ByteBuffer within the same DataBuffer's
iterator -- neither of which was guarded at all. Since completed() is
invoked by the channel's own completion callback, typically on a
different thread than the one that issued the original write, a
synchronous exception escaping either of those calls has no path back
to the FluxSink, and the resulting Flux hangs indefinitely, exactly as
described in gh-37143, for any write that receives a partial OS write
or spans multiple ByteBuffers.

To address that, this commit extracts a private write(ByteBuffer, long,
Attachment) helper that wraps the channel.write(...) call with a
try/catch block, routing any non-fatal Throwable -- via
Exceptions.throwIfFatal() -- to the existing failed(Throwable,
Attachment) handler. All three call sites (hookOnNext() and both
branches in completed()) now go through this helper, ensuring the
Flux always terminates with a proper error signal instead of hanging
silently, regardless of which write attempt fails or which thread it
fails on.

See gh-36184
See gh-37143
Closes gh-37145
2026-08-18 17:13:56 +02:00
Sam Brannen a13056f6af Merge branch '7.0.x' 2026-08-18 16:44:50 +02:00
Sam Brannen 15ef2b21f0 Handle synchronous exceptions from AsynchronousFileChannel#read
Prior to this commit, DataBufferUtils$ReadCompletionHandler#read()
invoked AsynchronousFileChannel#read(ByteBuffer, long, Attachment,
CompletionHandler) without guarding against exceptions thrown directly
by that call. Although that method is documented to report failures
asynchronously via the supplied CompletionHandler, some platform-
specific implementations can instead throw synchronously – for
example, on Windows with JDK 25, when the JDK rejects a ByteBuffer
backed by a closeable shared memory Arena, as produced by Netty 4.2's
off-heap buffer allocation.

When such an exception is thrown from a recursive read() invocation
triggered from completed() – which happens once a resource requires
more than a single chunk – the exception has no path back to the
FluxSink: it escapes on whatever thread invoked the CompletionHandler,
and the resulting Flux never signals onError or onComplete. In
practice, this surfaced as an indefinite hang when serving a Resource
whose HTTP response is not a ZeroCopyHttpOutputMessage, since
ResourceHttpMessageWriter falls back to ResourceEncoder, which reads
the resource via DataBufferUtils.

To address that, this commit wraps the channel.read(...) call in a
try/catch block and routes any non-fatal Throwable to the existing
failed(Throwable, Attachment) handler, via Exceptions.throwIfFatal(),
mirroring the equivalent fix already applied to the write side for
gh-36184. This ensures the allocated DataBuffer is released and the
Flux always terminates with a proper error signal instead of leaking a
buffer or hanging silently.

See gh-36184
Closes gh-37143
2026-08-18 16:32:34 +02:00
Sam Brannen 8a92c19e4d Limit result size of BigDecimal/BigInteger power operations in SpEL
This commit introduces a configurable limit on the estimated result size
of BigDecimal and BigInteger power operations within SpEL expressions.
The estimated result size in bits is computed as the product of the base
value's bit length and the exponent. If this limit is exceeded, a
SpelEvaluationException is thrown.

The limit defaults to 1,000,000 bits, which is approximately equivalent
to a decimal number with 300,000 digits, and can be configured either
on a per-use-case basis via the new maximumBigPowerBits constructor
argument in SpelParserConfiguration or globally as a JVM system
property or Spring property named `spring.expression.maxBigPowerBits`.
Parsers intended for trusted internal expressions may supply
Integer.MAX_VALUE to remove the limit entirely.

Closes ch-37034
2026-08-14 09:19:58 +02:00
Sam Brannen baae93f20a Limit result size of BigDecimal/BigInteger power operations in SpEL
This commit introduces a configurable limit on the estimated result size
of BigDecimal and BigInteger power operations within SpEL expressions.
The estimated result size in bits is computed as the product of the base
value's bit length and the exponent. If this limit is exceeded, a
SpelEvaluationException is thrown.

The limit defaults to 1,000,000 bits, which is approximately equivalent
to a decimal number with 300,000 digits, and can be configured either
on a per-use-case basis via the new maximumBigPowerBits constructor
argument in SpelParserConfiguration or globally as a JVM system
property or Spring property named `spring.expression.maxBigPowerBits`.
Parsers intended for trusted internal expressions may supply
Integer.MAX_VALUE to remove the limit entirely.

Closes ch-37034
2026-08-14 09:11:50 +02:00
Brian Clozel 4b5c92703c Merge branch '7.0.x' 2026-08-05 10:44:06 +02:00
junhyeong9812 7f1966f5f5 Reset TwoByteMatcher partial match on mismatching byte
DataBufferUtils.TwoByteMatcher inherited AbstractNestedMatcher.match(byte)
without providing the mismatch fallback that its siblings implement
(KnuthMorrisPrattMatcher backtracks via its suffix-prefix table, and
SingleByteMatcher is stateless). As a result, once the first delimiter
byte had matched, the match counter stayed at 1 across any number of
intervening non-matching bytes, so a later occurrence of the second
delimiter byte falsely completed the match.

For a two-byte delimiter such as \r\n this made the matcher report a
match across non-contiguous bytes. CompositeMatcher prefers the longest
delimiter that matches at a position, so the false \r\n match was chosen
over a real single \n, causing StringDecoder to strip two bytes and drop
the character preceding a lone \n whenever a line contained a stray \r.

TwoByteMatcher now overrides match(byte) to reset the counter to 0 when
the incoming byte is not the expected next delimiter byte before
delegating to super.match(), mirroring KnuthMorrisPrattMatcher. A
genuine contiguous delimiter is unaffected.

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-08-05 10:36:05 +02:00
Brian Clozel c17b4ad787 Merge branch '7.0.x' 2026-08-05 10:09:11 +02:00
Boris Perović b556766e1a Avoid retaining class files in annotation metadata
ClassFileAnnotationDelegate passed the raw java.lang.classfile
Annotation to MergedAnnotation.of() as the annotation source. That
annotation holds a Utf8Entry, so retaining the metadata of an
annotated class also retained its class file byte[], the parsed
constant pool, and the class model.

Store the declaring class name instead, as the ASM variant does.

See gh-37111

Signed-off-by: Boris Perović <boris.perovic@sysdig.com>
2026-08-05 10:07:44 +02:00
Juergen Hoeller 0376dd9a78 Merge branch '7.0.x' 2026-07-31 16:28:17 +02:00
Juergen Hoeller f5564e7e31 Consistently use default constants within builder
See gh-36983
2026-07-31 16:27:18 +02:00
Brian Clozel 079992021c Improve MimeType parser for RFC compliance
Prior to this commit, the `MimeType` class would compare raw parameter
values for the equals/hashcode contract. This went against the RFC which
states that quoted and unquoted parameter values are equivalent.

This commit rewrote the entire `MimeType` parser in `MimeTypeUtils`
as a state parser to improve robustness and performance.
The `MimeType` equals, compareTo and hascode contracts now unquote
parameter values before comparing them.

This change also optimizes the `tokenize` function that splits many
comma-separated MIME types into a list. Now that this method isn't used
anywhere else, it is also deprecated as of 7.1. This method was
initially made public to be reused within Spring Framework and has no
particular use in Spring applications in general.

Finally, this also makes `MediaType` and `MimeType` leverage the
`MimeType` LRU cache as much as possible, including when parsing
`Accept:` HTTP headers.

Closes gh-36729
2026-07-24 14:29:36 +02:00
Brian Clozel 224522244f Merge branch '7.0.x' 2026-07-22 10:54:58 +02:00
Brian Clozel 7de2b24d81 Fix primitive array annotation attributes on Java 24 class reading
Prior to this commit, `ClassFileAnnotationDelegate#parseArrayValue`
would only consider `int[]`, `double[]` and `long[]` array
annotation attributes; other primitive array types like `byte[]`
would use a generic path that would use boxed types.

This commit ensures that a comprehensive pass is made for all
primitive typed arrays. Because the `AnnotationValue` hierarchy
is sealed, we can now maje sure that the implementation is
exhaustive.

Closes gh-37083
2026-07-22 10:50:07 +02:00
Sam Brannen 38e1bf5970 Merge branch '7.0.x' 2026-07-20 11:07:00 +03:00
Manu Sridharan 233725c8f5 Add @⁠Nullable annotations when treating Map.remove() as returning @⁠Nullable
Closes gh-37067

Signed-off-by: Manu Sridharan <msridhar@gmail.com>
2026-07-20 11:02:55 +03:00