The opaque-host percent-escape validation guard reads
input.codePointAt(i + 2) after only checking 'input.length() - i < 2',
so an input such as 'foo://%4' throws StringIndexOutOfBoundsException
instead of reporting a validation error.
Fix the bounds guard to require two code points after '%' and check
ASCII hex digits rather than ASCII digits, matching the URL spec, where
invalid percent-escapes in opaque hosts are validation errors, not
failures.
Signed-off-by: Sagar Chanchal <Sagarr2112@gmail.com>
Add a "Combining @Retryable with Other Proxy-Based Features" section
to the resilience reference documentation, covering interaction with
@Transactional, @Cacheable, and @Async, as well as advice order
customization between @Retryable and @Async via
@EnableResilientMethods(order) and @EnableAsync(order).
Also document the advice chain semantics in @Retryable Javadoc, add
cross-reference TIP blocks in the @Async, @Cacheable, and
@Transactional reference sections, add a @Cacheable combination test
to RetryInterceptorTests, and add RetryableTransactionTests in
spring-tx for the @Transactional combination.
See gh-35584
Closes gh-37005
Signed-off-by: jhan0121 <jhan0121@gmail.com>
ConcurrentLruCache.clear() previously drained write operations before
cleaning up the cache. This could re-enqueue nodes that clear() was
about to remove, causing useless evictionQueue operations. Now clear()
iterates the cache values directly, removes and marks nodes as removed
first, and drains write operations afterward. Since AddTask fails
silently after a node is marked removed, this avoids the no-op work
and improves performance.
See gh-37287
Closes gh-37292
Signed-off-by: Chengang Guan <guanchengang@qq.com>
`RetryTemplateTests` can, under load, break the build because of a flaky
test: "retryableWithTimeoutExceededAfterSecondRetry".
This usually happens when many cores are busy and the timeout check
runs before each retry attempt.
This commit extends the timeout to avoid such cases and failures.
Previously, PartGenerator only enforced maxDiskUsagePerPart for body
buffers that arrived after a part had switched to file storage. The
content accumulated in memory that triggered the switch was written
to disk without any check against maxDiskUsagePerPart. As a result,
a part whose last body buffer caused the in-memory overflow was
accepted in full, even when its total size exceeded the configured
disk usage limit.
This commit checks the accumulated byte count against
maxDiskUsagePerPart before switching to file storage, and emits a
DataBufferLimitException, consistent with the existing check for
subsequent buffers.
Closes gh-35099
Signed-off-by: seonghun lee <harrisleesh@gmail.com>
Prior to this commit, clear() polled the eviction queue to remove
entries and only afterward drained the pending write operations queue.
Consequently, a put() whose AddTask had not yet been linked into the
eviction queue -- for example, because it lost the race to self-drain
while clear() held the eviction lock -- would only be applied by that
trailing drain, linking the entry into the eviction queue right after
clear() had already finished removing everything it could see.
The practical effect was that an entry already fully added to the cache
could still be present immediately after clear() returned, with no
further concurrent activity required at that point.
To address that, this commit revises clear() so that it drains the
pending write operations queue before polling the eviction queue, so
any write that was already queued gets cleaned up along with everything
else. However, a put() that is genuinely concurrent with an in-progress
clear() call can still survive, which is consistent with the cache's
weak-consistency design.
Thanks to @guanchengang for raising gh-37286, which prompted this fix.
Closes gh-37287
markAsRemoved() transitions a node to the removed state and decrements
the current size, but it did not check whether the node had already
been removed. The eviction path and an explicit removal can process
the same node in sequence: when a write drain runs a queued AddTask
whose eviction polls a node that a concurrent remove(K) has already
taken out of the cache, the eviction decrements the size, and the
queued RemovalTask for the same node decrements it again. The sibling
transition markForRemoval() guards against invalid transitions; this one
did not.
Each extra decrement makes currentSize permanently smaller than the
number of cached entries, so eviction stops triggering and the cache
exceeds its capacity for good, silently. A bounded two-thread stress
run accumulates the drift reliably: before the change the cache
stabilized far above its capacity in 20 out of 20 runs.
markAsRemoved() now returns without decrementing when the entry is
already in the removed state, mirroring the guard in markForRemoval().
The removed state is terminal, so each node is counted down exactly
once. The new test races explicit removals against eviction and then
verifies that the cache converges back to its capacity; it also
asserts that the racing thread ran and terminated cleanly.
Closes gh-37268
Signed-off-by: junhyeong9812 <pickjog@gmail.com>
This commit extends LazyConnectionInvocationHandler to cache early
calls to:
- setClientInfo(String, String)
- setNetworkTimeout(Executor, int)
These methods now defer physical connection acquisition until Statement
creation, consistent with existing lazy behavior for autoCommit,
readOnly, transactionIsolation, catalog, and schema.
We also accept and lazily cache calls to setNetworkTimeout() even when
the provided Executor is null. Since some JDBC driver implementations
completely ignore the Executor parameter (or fall back to a default
executor), we cannot meaningfully validate or handle a null Executor
before the physical connection is obtained.
getClientInfo() and getClientInfo(String) remain non-lazy (triggering
immediate connection fetch), because they are read operations whose
values cannot be reliably cached due to driver defaults, pooled
connection remnants, or external session modifications.
setClientInfo(Properties) also remains non-lazy. The reason is that JDBC
driver implementations are inconsistent. Some treat it as overwrite,
others as append/merge. To guarantee behavior identical to non-lazy
execution across all drivers, we choose not to cache or replay it,
avoiding any risk of semantic mismatch.
See gh-37258
Closes gh-37261
Signed-off-by: Chengang Guan <guanchengang@qq.com>
Since keySet() already applies the filter, size() evaluated the
predicate twice for every accepted key.
This commit uses delegate.keySet() instead, avoiding the second
evaluation as well as a FilteredSet and FilteredIterator allocation.
Closes gh-37256
Signed-off-by: Hyunwoo Jung <hyunwoojung@kakao.com>
Prior to this commit, LogAccessor's CharSequence-based logging methods
delegated directly to the corresponding method on the underlying
commons-logging Log instance without first checking whether the target
level was enabled. This differed from the Supplier-based overloads,
which have checked isXxxEnabled() before delegating since Spring
Framework 5.2.9.
That asymmetry was harmless as long as spring-jcl supplied the
underlying Log implementation, since its SLF4J adapter itself checked
the level before rendering the message. However, since Spring Framework
7 replaced spring-jcl with Apache commons-logging, whose SLF4J adapters
call String.valueOf(message) unconditionally, any CharSequence argument
-- most notably a LogMessage supplied via LogMessage.format(...) or
LogMessage.of(...) -- is now rendered eagerly, even when the
corresponding level is disabled. Since LogMessage exists specifically
to defer that work, and the idiom is used extensively throughout the
framework and its portfolio projects, this leads to unnecessary
computation and allocation whenever logging is disabled.
To address this, this commit adds the same isXxxEnabled() guard to all
twelve CharSequence-based methods in LogAccessor, matching the
existing Supplier-based overloads and making LogAccessor's laziness
guarantee independent of the underlying Log implementation.
This commit also introduces LogAccessorTests, which verifies that a
lazily rendering LogMessage passed to one of the CharSequence-based
methods is only rendered when the corresponding level is enabled.
See gh-25741
Closes gh-37266
Prior to this commit, gh-30953 fixed a case where multipart file parts
were not emitted properly when the body itself is empty.
There are other cases like this, depending on the order and slicing of
data buffers received by the parser. Here, a buffer containing the
entire boundary would not cause an empty file part to be emitted and
instead switch to the next header.
This commit ensures that empty file parts are always emitted as they
should.
Fixes gh-37264
The Mutiny Uni adapter registers its empty-value supplier as
Uni.createFrom().nothing(), which returns a Uni that never signals an
item, a failure, or completion. Every sibling registration supplies an
empty value that completes immediately: Mono.empty(), Maybe.empty(),
Completable.complete(), and CompletableDeferred(null); the Multi
registration uses Multi.createFrom().empty() as well.
ReactiveAdapter.toPublisher(null) substitutes that empty value whenever
a null source needs to be adapted, for example when a WebFlux handler
method with a Uni return type returns null. With a never-completing
empty value the resulting Publisher emits no signal at all, so the
response is never written and the request hangs until a timeout,
whereas the same handler declared with Mono completes empty. The
adapter also becomes asymmetric with its own fromPublisher function,
which adapts an empty Publisher to a Uni that completes with a null
item.
The supplier now uses Uni.createFrom().nullItem(), whose conversion to
a Publisher completes without emitting an item, matching the sibling
adapters and the round-trip through fromPublisher. The descriptor is
shared by the Mutiny 1 and Mutiny 2 registrations, so both paths are
covered.
Signed-off-by: junhyeong9812 <pickjog@gmail.com>
Prior to this commit, Hibernate 8 types extending
`MutationOrSelectionQuery` would fail proxying at runtime in native
images because reflection hints were not registered at build time.
While the `MutationOrSelectionQueryImpl` case can be solved with an
additional proxy hint, `NativeMutationOrSelectionQueryImpl` is
impossible to solve that way due to multi interface mismatch.
This was found in gh-36878 and handled with a fallback proxy.
In this case, native image will throw a
`MissingReflectionRegistrationError` - but obviously we cannot depend on
this type in JVM applications. `MissingReflectionRegistrationError`
extends `LinkageError`, which we will use along
IllegalArgumentException` to detect that the proxying operation failed
and that we should use the fallback.
This commit also register a proxy hint for the said fallback.
Fixes gh-37251
Prior to this commit, ConversionService#canConvert(Class, Class)
threw an IllegalArgumentException when invoked with Enum.class as the
target type (i.e., `canConvert(String.class, Enum.class)`), because
ConverterFactory#getConverter() in StringToEnumConverterFactory and
IntegerToEnumConverterFactory eagerly resolved the concrete enum type.
To address that, StringToEnumConverterFactory and
IntegerToEnumConverterFactory now implement ConditionalConverter so
that matches() can reject non-concrete-enum targets before
getConverter() is ever invoked.
Closes gh-34532
Signed-off-by: MoonFruit <dkmoonfruit@gmail.com>
Co-authored-by: Sam Brannen <104798+sbrannen@users.noreply.github.com>
Prior to this commit, calling `DefaultAsyncServerResponse.writeAsync()`
would unconditionally create a new `AsyncWebRequest` and install it
on the `WebAsyncManager`, even when one is already present for the
current request.
The functional web framework can do such a thing when returning a
`ServerResponse.async(future)` from a `HandlerFunction`; the
`HandlerFunctionAdapter` does install an async web request already.
This means that the async timeout configured at the application level
would be ignored and instead falling back to the Servlet container
default.
This commit makes the `DefaultAsyncServerResponse` skip async web
request creation it there is an existing one.
Fixes gh-37257
This commit makes use of the Java specification version in the Jar
metadata to make build easier to reproduce on a different environment.
Closes gh-37250
This commit adds the missing reflection hints for Hibernate 8 support:
`PersistenceUnitInfoDescriptor` and `StatelessSession`.
Fixes gh-37247
Fixes gh-37249
Every error code setter in SQLErrorCodes sorts its array with
StringUtils.sortStringArray, and CustomSQLErrorCodesTranslation does
the same, because SQLErrorCodeSQLExceptionTranslator looks the codes
up with Arrays.binarySearch. setDuplicateKeyCodes was the only setter
that stored the supplied array as-is.
With an unsorted list of duplicate key codes, the binary search finds
or misses a code depending on where the values happen to sit: for
codes it misses, the translator silently falls through to the SQLState
fallback and reports a DataIntegrityViolationException, or fails to
translate at all, instead of the configured DuplicateKeyException. The
default sql-error-codes.xml is not affected since its lists are
already sorted; the mismatch surfaces for custom configurations, for
example codes of different digit lengths listed in numeric order.
setDuplicateKeyCodes now sorts the array like all sibling setters. The
new test covers an unsorted custom list whose codes previously hit or
missed depending on their position.
Closes gh-37235
Signed-off-by: junhyeong9812 <pickjog@gmail.com>
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>
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>
Prior to this commit, AbstractBrokerMessageHandler logged the
BrokerAvailabilityEvent object directly at INFO level. With a structured
JSON logging layout, the event could be serialized as an object rather
than via toString(), causing the layout to traverse the event source
(a SimpleBrokerMessageHandler) object graph. That graph contains a
cyclic reference through the client inbound channel executor's thread
factory, which fails serialization at the maximum nesting depth.
This commit logs the event's toString() representation instead, keeping
the same operational signal while preventing structured logging layouts
from traversing framework internals.
Signed-off-by: kodacme <kodac.saito@kodac.me>
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>
The static SimpleDateFormat instance in
AbstractMockHttpServletRequestBuilder is shared across all
instances. SimpleDateFormat.format() mutates internal Calendar
state and is not thread-safe, which can produce corrupt date
strings or ArrayIndexOutOfBoundsException when tests run in
parallel.
Replace with DateTimeFormatter which is immutable and thread-safe.
Signed-off-by: Sebastien Tardif <SebTardif@ncf.ca>
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>