This commit updates the AOT infrastructure to handle generated resources
in a similar fashion than generated classes: naming conventions, feature
prefixes, and uniqueness are applied.
The new abstraction also provides a more explicit contract that guides
users to either create the resource or create it if it does not exist
and validate its content if it does.
As part of this change ClassNameGenerator has been renamed to
NameGenerator as it is responsible to generate names for both classes
and resources.
Closes gh-35862
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>
Now that all `PropertyAccessorUtils` has been removed and replaced by
`PropertyPath` and local private methods, we can officially deprecate
this utility class and remove it in the future.
Closes gh-37275
Prior to this commit, `DataBinder` and the property accessor
hierarchy relied on `PropertyAccessorUtils` and several
independent scanners for property paths.
This commit migrates all of them to `PropertyPath`, so
there is exactly one parser deciding what a well-formed property
path is, used identically for policy checks and for actual
navigation.
This removes long standing protected methods like
A`getPropertyAccessorForPropertyPathi` and `getFinalPath` from
`bstractNestablePropertyAccessor`. The path is now parsed exactly
once per public entry point, via the new `resolvePropertyPath`, which
returns a `ResolvedProperty`. Then, property navigation walks
the parsed segment list rather than re-scanning partial strings.
Any subclass overriding the removed method will need to adapt.
This removal initially conflicted with gh-37252 (maxNestedPathDepth
support). The public configuration remains but the actual behavior
changed; it is replaced with `PropertyPath.Options` which enforces
limit right after parsing, before property navigation begins.
The exception thrown changes from `InvalidPropertyException` to
`InvalidPropertyPathException`.
This commmit also reverts the `map[']` / `map["]` quoting behavior
from gh-36765 as it is incompatible with the new grammar.
See gh-37275
Prior to this commit, bean property path support would be duplicated in
`AbstractNestablePropertyAccessor` and `PropertyAccessorUtils`. This
means they both supported the parsing, validation and extraction of path
segments. Implementations were not always in sync and could cause
issues.
This commit introduces a new `PropertyPath` type that holds the
canonical form of the property path and the parsed path segments for
property access. This implements an efficient parser that rejects
invalid property paths early if they don't match the new grammar.
`PropertyPath.parse(String, Options)` additionally accepts a maximum
nesting depth, rejecting an excessively deep path immediately after
parsing and before any navigation of an object graph begins.
This moves the implementation introduced in gh-37252, but keeps the
public configuration in place.
`InvalidPropertyPathException` is introduced to report a syntactically
invalid path, as distinct from a syntactically valid path that
happens not to resolve against a particular target object (see
`NotReadablePropertyException` and `NotWritablePropertyException`).
It extends `PropertyAccessException`, but not `InvalidPropertyException`.
Malformed paths should be collected into
`PropertyBatchUpdateException` alongside other per-property failures.
See gh-37275
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
AbstractNestablePropertyAccessor resolves a nested property path
recursively, one recursive call per path segment, and there was
previously no limit on the nesting depth. Consequently, a sufficiently
deeply nested property path -- for example, against a self-referential
type -- could exhaust the current thread's call stack, resulting in a
StackOverflowError which lacks useful diagnostics for developers
attempting to assess what went wrong.
Note that the existing autoGrowCollectionLimit bounds array and
collection growth, not path depth.
The same is true for constructor binding via DataBinder.construct(),
which constructs a nested constructor argument recursively through a
nested property path. Path segments are constrained to declared
constructor parameters there, but a self-referential type nonetheless
permits an arbitrarily deep path.
With this commit, each property accessor tracks the number of nested
properties traversed to reach the object that it wraps, and an
InvalidPropertyException is thrown once the configured (or default)
maxNestedPathDepth limit is exceeded, with a message that reports the
configured limit. The limit applies regardless of autoGrowNestedPaths,
since resolving an existing deep object graph recurses in the same
manner as auto-growing one. Tracking the depth per property accessor
rather than threading it through the recursion allows the recursion to
dispatch through the protected
getPropertyAccessorForPropertyPath(String) method, which subclasses may
override, and avoids deriving the depth from the nested path, which
would require rescanning an ever longer path prefix at each level.
Constructor binding likewise tracks the nesting depth while
constructing nested objects as well as indexed and mapped elements, and
throws the same InvalidPropertyException once the limit is exceeded.
The maxNestedPathDepth (which defaults to 100) can be configured on a
per-use-case basis via ConfigurablePropertyAccessor or DataBinder,
which applies it to constructor binding directly and supplies it to the
property accessor via its binding result. In contrast to the auto-grow
collection limit, which is unlimited on a plain accessor, the nesting
depth is bounded by default even for programmatic property access,
since a large array or collection can be perfectly legitimate whereas a
deeply nested property path effectively never is.
Specifying zero for the maxNestedPathDepth disables support for nested
property paths altogether while continuing to allow simple, indexed,
and mapped property access, which is a reasonable way to constrain data
binding for a target object that is not intended to be traversed (such
as a flat DTO). However, negative values for maxNestedPathDepth are
always rejected.
Closes gh-37252
This commit adds additional "disconnected client" checks for
HttpMessageNotReadableException and HttpMessageNotWriteableException,
both of which wrap I/O errors and could be due to a lost connection.
Closes gh-37151
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>
Before this commit, the implementation uses `ResolvableType::isInstance` which doesn't take JDK proxy into account, it fails if `proxyTargetClass = false`:
```
Bean named 'userDao' is expected to be of type 'org.springframework.cache.config.ExpressionCachingIntegrationTests$BaseDao<org.springframework.cache.config.ExpressionCachingIntegrationTests$User>' but was actually of type 'org.springframework.cache.config.$Proxy53'
org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'userDao' is expected to be of type 'org.springframework.cache.config.ExpressionCachingIntegrationTests$BaseDao<org.springframework.cache.config.ExpressionCachingIntegrationTests$User>' but was actually of type 'org.springframework.cache.config.$Proxy53'
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:212)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1312)
at org.springframework.cache.config.ExpressionCachingIntegrationTests.expressionIsCacheBasedOnActualMethod(ExpressionCachingIntegrationTests.java:42)
```
See gh-34687
Closes gh-37047
Signed-off-by: Yanming Zhou <zhouyanming@gmail.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
Per the SSE specification, a "retry" field whose value is not made up
solely of ASCII digits must be ignored. ServerSentEventHttpMessageReader
passed the value straight to Long.parseLong, so "retry:none", an empty
"retry:", or a value too large for a long raised NumberFormatException
and terminated the event stream. A client cannot control what a server
sends, so an unusable reconnection hint would kill an otherwise healthy
subscription.
Signed-off-by: Artyom Tsvirko <36863599+lArtiquel@users.noreply.github.com>
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