Prior to this commit, a RetryException thrown for an
InterruptedException returned the wrong value from getRetryCount().
Specifically, the count was one more than it should have been, since the
suppressed exception list contains the initial exception as well as all
retry attempt exceptions.
To address that, this commit introduces an internal
RetryInterruptedException which accounts for this off-by-one error.
Closes gh-35434
In RetryTemplate, if we encounter an InterruptedException while
sleeping for the configured back-off duration, we throw a
RetryException with the InterruptedException as the cause.
However, prior to this commit, that RetryException propagated to the
caller without notifying the registered RetryListener.
To address that, this commit introduces a new
onRetryPolicyInterruption() callback in RetryListener as a companion to
the existing onRetryPolicyExhaustion() callback.
Closes gh-35442
In RetryTemplate, if we encounter an InterruptedException while
sleeping for the configured back-off duration, we throw a
RetryException with the InterruptedException as the cause.
However, in contrast to the specification for RetryException, we do not
currently include the exceptions for previous retry attempts as
suppressed exceptions in the RetryException which is thrown in such
scenarios.
In order to comply with the documented contract for RetryException,
this commit includes exceptions for previous attempts in the
RetryException thrown for an InterruptedException as well.
Closes gh-35434
Prior to this commit, we included the initial exception in the log
message for the initial invocation of a retryable operation; however,
we did not include the current exception in the log message for
subsequent attempts.
For consistency, we now include the current exception in log messages
for subsequent retry attempts as well.
Closes gh-35433
Prior to this commit, gh-32097 added native support for Jetty for both
client and server integrations. The `JettyDataBufferFactory` was
promoted as a first class citizen, extracted from a private class in the
client support. To accomodate with server-side requirements, an extra
`buffer.retain()` call was performed.
While this is useful for server-side support, this introduced a bug in
the data buffer factory, as wrapping an existing chunk means that this
chunk is already retained.
This commit fixes the buffer factory implementation and moved existing
tests from mocks to actual pooled buffer implementations from Jetty.
The extra `buffer.retain()` is now done from the server support, right
before wrapping the buffer.
Fixes gh-35319
As of gh-33616, Spring now supports metadata reading with the ClassFile
API on JDK 24+ runtimes. This commit fixes a bug where
`ArrayStoreException` were thrown when reading annotation attribute
values for arrays.
Fixes gh-35252
This commit mames `StartupStep` extend `AutoCloseable` in order to allow
the try/with resources syntax and making the `step.end()` call
transparent.
Closes gh-35277
Prior to this commit, the MergedAnnotations support (specifically
AnnotationsScanner) and AnnotatedMethod did not find annotations on
overridden methods in type hierarchies with unresolved generics.
The reason for this is that ResolvableType.resolve() returns null for
such an unresolved type, which prevents the search algorithms from
considering such methods as override candidates.
For example, given the following type hierarchy, the compiler does not
generate a method corresponding to processOneAndTwo(Long, String) for
GenericInterfaceImpl. Nonetheless, one would expect an invocation of
processOneAndTwo(Long, String) to be @Transactional since it is
effectively an invocation of processOneAndTwo(Long, C) in
GenericAbstractSuperclass, which overrides/implements
processOneAndTwo(A, B) in GenericInterface, which is annotated with
@Transactional.
However, the MergedAnnotations infrastructure currently does not
determine that processOneAndTwo(Long, C) is @Transactional since it is
not able to determine that processOneAndTwo(Long, C) overrides
processOneAndTwo(A, B) because of the unresolved generic C.
interface GenericInterface<A, B> {
@Transactional
void processOneAndTwo(A value1, B value2);
}
abstract class GenericAbstractSuperclass<C> implements GenericInterface<Long, C> {
@Override
public void processOneAndTwo(Long value1, C value2) {
}
}
static GenericInterfaceImpl extends GenericAbstractSuperclass<String> {
}
To address such issues, this commit changes the logic in
AnnotationsScanner.hasSameGenericTypeParameters() and
AnnotatedMethod.isOverrideFor() so that they use
ResolvableType.toClass() instead of ResolvableType.resolve(). The
former returns Object.class for an unresolved generic which in turn
allows the search algorithms to properly detect method overrides in
such type hierarchies.
Closes gh-35342
Prior to this commit, RetryTemplate supplied the wrong exception to
RetryListener.onRetryPolicyExhaustion(). Specifically, the execute()
method in RetryTemplate supplied the final, composite RetryException to
onRetryPolicyExhaustion() instead of the last exception thrown by the
Retryable operation.
This commit fixes that bug by ensuring that the last exception thrown by
the Retryable operation is supplied to onRetryPolicyExhaustion().
Closes gh-35334
This commit simplifies RetryException to always require a root cause
and mark it as not nullable. Such exception is the exception thrown by
the retryable operation and should always be available as it explains
why the invocation was a candidate for retrying in the first place.
Closes gh-35332
Refine the StringUtils#uriDecode method in the following ways:
- Use a StringBuilder instead of ByteArrayOutputStream, and only decode
%-encoded sequences.
- Use HexFormat.fromHexDigits to decode hex sequences.
- Decode to a byte array that is only allocated if encoded sequences are
encountered.
This commit adds another optimization mainly for the use case where
there is no encoded sequence, and updates the Javadoc of both
StringUtils#uriDecode and UriUtils#decode to match the implementation.
Signed-off-by: Patrick Strawderman <pstrawderman@netflix.com>
Co-Authored-by: Sebastien Deleuze <sebastien.deleuze@broadcom.com>
Closes gh-35253
While assessing #35195, I noticed the following issues with our
Checkstyle configuration regarding nullability annotations.
- "^(?!org\.jspecify|\.annotations).*(NonNull|Nullable)$" contains a "|".
- "^(?!org\.jspecify|\.annotations).*(NonNull|Nullable)$" matches against
NonNull but not against Nonnull, and therefore incorrectly permits
usage of javax.annotation.Nonnull.
- Some of the Checkstyle suppressions no longer apply.
This commit addresses all of the above issues and updates several tests
to use example annotations other than javax.annotation.Nonnull where
feasible.
See gh-35195
Closes gh-35205
If an application depends on automatic type conversion from
java.time.Instant to java.sql.Timestamp, the ObjectToObjectConverter
performs the conversion based on convention, by using reflection to
invoke Timestamp.from(Instant).
However, when running in a native image a user needs to explicitly
register runtime hints for that particular use of reflection.
To assist users who are running their applications in a native image,
this commit automatically registers the necessary runtime hints for
Timestamp.from(Instant) so that users do not have to.
See gh-35175
Closes gh-35156
In order to avoid unnecessary use of reflection and to simplify native
image deployments, this commit introduces explicit support for
automatic conversions from java.util.Date to java.time.Instant and vice
versa.
To achieve that, this commit introduces an InstantToDateConverter and a
DateToInstantConverter and registers them automatically in
DefaultConversionService.
See gh-35156
Closes gh-35175
This commit leverages flexible generics nullness at method and
type level when relevant in spring-jdbc.
Due to https://github.com/uber/NullAway/issues/1075, some related
`@SuppressWarnings("NullAway")` have been added.
JdbcOperations Kotlin extensions have been refined accordingly.
Closes gh-34911
In commit 97522cfa36, I implemented a
short-circuiting matching algorithm in DefaultRetryPolicy for includes
and excludes, which was later copied to MethodRetrySpec.
After we switched to using ExceptionTypeFilter, I realized that the
matching algorithm in InstanceFilter (the superclass of
ExceptionTypeFilter) does not exhibit the same short-circuiting
characteristics.
In light of that, this commit revises the matching algorithm in
InstanceFilter to mirror the original short-circuiting algorithm in
DefaultRetryPolicy.
See gh-35058
See gh-35109
See gh-35160
Closes gh-35161
Prior to this commit, ExceptionTypeFilter only supported matching
against an exception type. However, most use cases involve matching
against an exception instance. Moreover, every use case within the core
Spring Framework uses ExceptionTypeFilter to match against concrete
exception instances.
This commit therefore introduces an overloaded match(Throwable) method
in ExceptionTypeFilter in order to provide support for the most common
use cases.
See gh-35109
Closes gh-35160
Prior to this commit, the constructors for InstanceFilter and
ExceptionTypeFilter required one to supply the matchIfEmpty flag.
However, users will typically want that to be true. Moreover, we always
supply true for the matchIfEmpty flag within the Spring Framework.
This commit therefore makes the matchIfEmpty flag optional by
introducing overloaded constructors for InstanceFilter and
ExceptionTypeFilter that only accept the includes and excludes
collections.
In addition, this commit overhauls the Javadoc for InstanceFilter and
ExceptionTypeFilter, fixing several issues in the documentation.
Furthermore, this commit applies consistent @Nullable declarations
in ExceptionTypeFilter.
Closes gh-35158
Prior to this commit, the value attribute in @Contract was declared as
follows.
String value() default "";
That allowed empty declarations such as @Contract or @Contract();
however, a contract is not useful without declared constraints.
To address that, this commit removes the `default ""` declaration in
order to force users to provide explicit constraints.
Although one could technically still declare the annotation without
constraints via @Contract(""), it's unlikely that anyone would
intentionally do that.
Closes gh-35157