ProtobufDecoder released the buffer of a partially read message only when the input completed or failed. When a subscriber cancelled while a message was split across data buffers, for example right after receiving the previous message, that buffer was never released. With pooled buffers, such as Netty's, it leaked.
Fix
The decoder now releases the buffer in doFinally, as the character-sequence decoders already did, so cancellation releases it too. A new test decodes a buffer that holds one message and half of the next, cancels after the first message, and asserts that the leak-aware buffer factory reports no leak. It reports one leak without the change.
Request.create documents that the body can be null, but the constructor stored the null and body(), charset(), isBinary() and length() dereferenced it, so any body-less Request built outside RequestTemplate threw a NullPointerException on first use.
Fix
The constructor normalises a null body to Body.empty(), the default RequestTemplate already uses, which gives exactly the documented contract so clients that branch on body() != null keep working. toString() checks the body's bytes so a body-less request still prints only the request line and headers. Two tests pin the contract and the changelog has an entry.
The String and text/plain body readers decoded the request body with a charset lookup that falls back to the Accept-Charset header when the Content-Type has none. For a request, those are the request's own headers, so Content-Type application/json with Accept-Charset Big5 made a UTF-8 body decode as Big5 and corrupted non-ASCII text. Accept-Charset states a preference for the response, not the encoding of the request body.
Fix
The readers now use only the charset parameter of the Content-Type, falling back to the configured default charset. Eleven tests cover buffered, streamed and chunked reads with Accept-Charset Big5 and ISO-8859-1, a missing media type, text/plain, and that an explicit Content-Type charset still wins. Nine of them fail on the release branch with the garbled output from the report.
The deployment scanner grouped scanned paths into apps by their basename, which drops everything after the last dot, for directories too. A webapp directory foo.com became the app foo, separate from the app foo.com of foo.com.properties, so the context deployed empty and answered 404. The code meant to strip .d from nominated directories never ran, because the extension check is always false for a directory.
Fix
A new helper gives the app name of a scanned path: a file keeps its name without extension, a directory keeps its whole name (dropping only a .d suffix), and a path that no longer exists maps to the app that already holds it, so removing foo.com undeploys foo.com. Tests cover directories with dots, nominated directories and the removal case.
Ignoring the result of Arrays.copyOf was never reported, and neither was Arrays.binarySearch, which is on the allow-list. The cause was not the allow-list. java.util.Arrays uses assert, so javac gives it a static initializer that calls Class.desiredAssertionStatus(). That call was taken for a side effect, so every static method of the class was recorded as possibly called only to trigger class initialization, which the checker never reports. The same happened to the pure static methods and constructors of any analysed class containing an assert.
Fix
Class.desiredAssertionStatus() is now treated as side-effect free, which fixes the root cause. Arrays.copyOf and copyOfRange are also listed explicitly, because copyOfRange throws on an invalid range, which the bytecode analysis takes for a side effect. A reproducer covers both overloads, a user class with assert, and used results.
Found by differential fuzzing against java.time, with no prior report. Splitting a negative epoch millisecond value with a truncating division gave a negative nanosecond-of-second, breaking an invariant every consumer relies on. The per-minute formatter cache was keyed with a truncating division as well and rendered seconds with a truncating remainder, so the wrong cached minute was chosen and a date pattern printed 23:59:+'.000 for a time one minute before the epoch.
Fix
Both places now use Math.floorDiv and Math.floorMod, which agree with the truncating operators for every instant after the epoch, so nothing changes there. Tests cover seven epoch-millisecond values around the boundary and the formatter's cached-minute path, which the reviewer asked for; reverting the division makes that test fail.
The policy matched the configured logger name with startsWith, so a rule for com.foo also matched com.foobar.Baz, which merely shares a character prefix. Every other logger-name feature in Log4j resolves by truncating at dots, and the mismatch silently demoted levels for packages the operator never meant to touch.
Fix
A match now requires the exact name or a dot-separated descendant. Review found that an empty name (catch-all) and a name ending in a dot had worked before and would have stopped; both are kept working and tested, alongside the sibling, child and exact cases.
containsTypeVariables ignored the owner type of a ParameterizedType, so Outer<T>.Inner was reported as containing no type variables, against its own Javadoc. unrollVariables never unrolled the owner, and for Outer<T>.Inner<T> it merged the raw type's own parameters over the caller's map, discarding the caller's T to String. A guard from an earlier fix also stopped a variable that occurs twice from being unrolled everywhere, so Map<T, T> became Map<String, T>.
Fix
Both methods now recurse into the owner type, unrolling the type arguments against the caller's map and rebuilding the type with its unrolled owner. The guard is released once a variable is resolved, so repeated variables unroll while genuine cycles are still cut; review added identity-based cycle detection through owners and type arguments. A new fixture gives regression tests that fail on master, and the default Maven build, over 85,000 tests with Checkstyle, PMD, SpotBugs and japicmp, passed before each round.
When a config server answered with a body the client could not read as an Environment, such as the HTML login page of a misconfigured security setup, RestTemplate threw UnknownContentTypeException. That exception stopped the client at the first URI even with the strategy set to always, while a 4xx or 5xx response moved on to the next one.
Fix
Both the config data loader and the bootstrap property source locator now try the next URI after an unreadable response when the strategy is always, logging it like the other failures. The last URI, and any URI with connection-timeout-only, rethrows as before so fail-fast behaviour is unchanged. Written test first: the new tests failed on main with a fail-fast exception.
DefaultBinderFactory became a SmartLifecycle so that a CRaC checkpoint can stop the binder child contexts and a restore can start them again. Its start() also ran on the regular refresh, after the binder contexts were already running, and start() on a running context starts every Lifecycle bean, including the plain ones a refresh deliberately leaves alone, such as the header channel registry and its reaper task.
Fix
start() now starts only binder contexts that are not running, that is the ones stopped for a checkpoint. A freshly refreshed context is left alone, so auto-startup beans behave as before and plain Lifecycle beans are no longer started. One test fails on main and passes with the change; another guards the stop-and-restart path. The maintainers rebased it onto main as their own commit with the authorship kept, which is why GitHub shows the pull request as closed, and backported it to 5.0.x.
The creator-parameter path passed the annotation value straight to DateTimeFormatter.ofPattern, including the ##time-in-millis and ##default markers, so records and @JsonbCreator parameters using them failed with "Pattern includes reserved character: '#'".
Fix
The creator path now treats the two markers the way the property path already did, creating a formatter without a pattern so the converters use epoch millis or their defaults. A record test round-trips a TIME_IN_MILLIS Instant, a locale-only LocalDate and a plain Instant.
The component matcher is documented to reuse a registered instance for the same type, but it compared the class of the binding wrapper with the serializer class, so the check never matched. Every annotated property got a new instance, and a stateful or specially constructed registered instance was ignored.
Fix
The two checks compare the class of the wrapped serializer or deserializer, as the adapter check already did. A test registers components whose output depends on how they were constructed and checks that annotated properties use them, and still get a new instance when none is registered.
Since Yasson 3, {"test": ["a", "b", "c"]} bound to a String property gave "c". The position checker advanced the parser by one event when the current event was unexpected, a step meant for user deserializers that stop just before a value. At START_ARRAY that step entered the array, so the first element was read as the value and the rest were read as further values for the same property.
Fix
The checker no longer steps into an array or object that is not expected, and the document is rejected with the existing exception. The skip still applies to every other event, so user-defined deserializers keep working, including the KEY_NAME case the reviewer pointed at.
Serialising List.of(annotated) wrote the element field by field, ignoring its class-level adapter. The element type of a raw collection is Object, so the runtime-type serializer passed the customisation of Object down and never looked at the class customisation of the actual type. The serializer built that way was cached for the class, so a later toJson of the object itself ignored the adapter too.
Fix
The runtime serializer chain keeps the given customisation only when it binds a serializer or an adapter, and otherwise uses the class customisation of the runtime type. Tests cover the adapter and serializer cases inside a collection and then directly, with a fresh Jsonb.
The message for a failing setter was built by concatenating the half-deserialised object, which called its toString(). If that failed, for example because a later field was still null, its exception replaced the setter's, and the real cause was lost.
Fix
The message uses the class name, as the getter path already did after an earlier fix, and keeps the setter's exception as the cause. It also copes with a null instance from a creator method. Two tests deserialise a class whose setter and toString() both throw.
The URL handler proxy invokes a stream handler from another class loader through reflection. Method.invoke wraps the handler's IOException in an InvocationTargetException, which a generic catch then wrapped in IllegalStateException("Stream handler unavailable due to: null"). A FileNotFoundException from URL.openConnection therefore escaped catch (IOException) blocks.
Fix
A small helper rethrows the handler's IOException on the four reflective paths, keeping the original type and stack trace. Other invocation failures are handled as before. A test opens a connection through the proxy with a handler that throws FileNotFoundException, with and without a Proxy.
Unescaping a quoted string shifted the bytes of the response buffer in place. The buffer is shared by every copy of the response, and several FetchResponse objects are built from the same bytes, so once one had read the string the others saw the shifted bytes and returned a corrupted value.
Fix
A quoted string with escape characters is unescaped into a new array and the buffer is left unchanged; one without escapes is still returned as a view without copying. Three tests read the string twice, from a copy, and as a ByteArray.
The initial response of an authentication mechanism was always sent on the AUTH command line. An XOAUTH2 access token of a thousand characters or more, as Google Workspace issues, pushed the line over the 512-octet limit of RFC 5321, and servers answered 500 or 555 'line too long'.
Fix
As RFC 4954 requires, an initial response that would not fit is sent in reply to the server's first empty 334 challenge, for both the built-in mechanisms and SASL. A response that fits is sent as before. Tests drive a fake server through both paths.
After enabling UTF8=ACCEPT, the SEARCH command was correctly sent without a CHARSET, but the search text was still written byte-wise as ASCII, keeping only the low byte of each character. The server rejected the invalid UTF-8, and the client fell back to a search on the client that fetched the whole folder, which took minutes on a large mailbox.
Fix
The search sequence is generated in UTF-8 when no charset is given and UTF-8 has been enabled, with non-ASCII text sent as a quoted string, which RFC 6855 allows. Without UTF-8 support nothing changes. The maintainer's own draft was abandoned in favour of this one after a discussion of the RFC's quoted-string rules.
Features added
Requests that had been open for years, built test-first with reference documentation.
Some compilers create non-final synthetic fields, for example a $SWITCH_TABLE$ field for every switch over an enum. haveOnlyFinalFields() reported them as violations although they appear nowhere in the source and cannot be declared final.
Fix
After discussing with the maintainer whether to change the existing condition, it was kept exactly as it is and an explicit alternative, haveOnlyFinalNonSyntheticFields(), was added and linked from its Javadoc. Because javac never emits such a field, the regression test generates a class file with ASM and imports it.
An FTP server behind a proxy could only be reached with the JVM's proxy system properties, which affect every connection of the application, or with a custom session factory. The SFTP factory had proxy support; the FTP one did not. The request had been open since 2012.
Fix
AbstractFtpSessionFactory gains setProxy(java.net.Proxy) for SOCKS and HTTP CONNECT proxies, failing fast unless the client is in passive mode, since a server cannot connect back through a proxy. A new FtpHttpProxySessionFactory covers HTTP proxies that require credentials. Reference documentation and a What's New entry are included, and the tests did not compile against main before the change.
The mapper could restrict which JMS properties were mapped inbound, but outbound it copied every eligible header. Keeping internal headers out of a JMS message needed a HeaderFilter in front of every JMS endpoint.
Fix
setOutboundHeaderNames takes the same simple wildcard and negation patterns as the inbound side; the first match decides, the default is everything, and the JMS API headers are always mapped. Documented in the JMS chapter and the What's New page.
RabbitAdmin already declared the context's exchanges, queues and bindings on one channel internally, but the programmatic API exposed one object per call, each on its own channel. The request had been open since 2018, with the maintainer open to it provided the exception handling was thought through.
Fix
declareExchanges, declareQueues and declareBindings are default methods on AmqpAdmin that declare one by one, so other implementations keep working, and RabbitAdmin overrides them to use a single channel. Each object follows the existing per-declaration exception handling, including ignoreDeclarationExceptions and DeclarationExceptionEvent, and manual declarables are recorded as for single declarations.
Documentation
Behaviour that was undocumented, pinned by a test where there was none.
An evaluator created with only a MavenSession failed with a NullPointerException for every expression, even ${project} or ${session}, because evaluation started by reading the mojo descriptor. The issue, MNG-6982, was reported against Maven 3.6.3.
Fix
The pull request proposed looking up the mojo descriptor only when there is a mojo execution, with a test that failed on master. A maintainer fixed the same code on master while it was under review, so the duplicate test and null check were removed and the Javadoc of the session-only constructor, which now explains the behaviour, is what landed.
The Environment Repository page explained precedence in general terms but not where a file named after both the application and a profile ranked, which the issue asked and users were relying on without knowing whether it was supported.
Fix
An ordered list for the page's sample client, from highest to lowest precedence, scoped to the native, Git and SVN backends. A test with the six files in one location asserts the order of the property sources, so the documentation is pinned to the behaviour.
The error 'Got bad greeting from SMTP host ... response: [EOF]' usually means the server closed the connection before greeting, because port 465 expects SSL from the start, and the FAQ did not cover it.
Fix
A FAQ entry after the STARTTLS one explains the cause, gives the properties to set for SSL, and the alternative of a STARTTLS port.
Tests and build
Tests that had silently stopped running, found across many projects, made to run again. Where a test then failed, it was fixed.
ParametersTest extended JUnit 3's TestCase until the JUnit 5 upgrade removed that without adding @Test, so the class had had no running test since. A third method in RegistryConfigTest lacked the annotation its twin had.
The test was ignored with 'Fails due to #1593'. That issue was closed in Mockito 5, where any(String.class) matches exactly one value passed to a varargs parameter, so the test had been passing silently ignored.
Fix
The @Ignore is removed and the test is kept as the regression test for that behaviour.
Four test methods had no @Test annotation, so they never ran. Once they did, each failed: two asserted null where the code returns String.class, one shared a log filter with another test and tripped on a record without a throwable, and one reopened a user database before the file writer was closed and overwrote a file shared with other tests.
Fix
Each test is fixed to assert what the code does, with its own log filter or its own temporary file. The log validation filter no longer fails on a record without a throwable.
The test was disabled when an Undertow upgrade exposed a NullPointerException in CXF's failover, tracked as CXF-9204. That was fixed in CXF 4.2.1 and Camel was on 4.2.4, but the test stayed disabled.
Fix
The @Disabled is removed; the test passed seven runs out of seven locally and in CI.
The test had been ignored on 2.x since it was found flaky. The fix on main replaced the sleeps between log events with a rollover listener and a latch, but was never ported to 2.x.
Fix
The approach from main is ported to the JUnit 4 test on 2.x: it registers as a rollover listener, logs without sleeping and waits for the rollovers, with fixed-size payloads so the count is deterministic.
CharsetsTest.testIsUTF8CharsetUTF8 and FileTimesTest.testIsUnixTimeFileTimeNull were written without the @Test annotation. JUnit therefore never executed them. They appeared in the source tree and passed compilation, but they provided no coverage and any regression they were meant to catch would go unnoticed.
Fix
The change adds @Test to both methods. With the annotation present, the Maven build runs them and both pass. Because there is no behavioural change to production code, no additional tests are needed; the methods themselves are the verification that the annotation is correct.
The Gradle task passed its options as strings, which the Antora plugin turned into three extra arguments, and the Node plugin it pulled in broke Gradle's configuration cache by starting a process during configuration.
Fix
The options are booleans, so they become flags, and a newer Node Gradle plugin that supports the configuration cache is declared. A second run reuses the cache.
When CSVFormat.Builder was introduced, the builder version of testEqualsSkipHeaderRecord in CSVFormatTest was written without the @Test annotation. JUnit therefore never ran it, and the builder path for comparing formats that differ in the skip header record setting had no running check. Its sibling tests, including testEqualsSkipHeaderRecord_Deprecated, all carried the annotation, so the gap was easy to miss: the method looked like a test but reported nothing.
Fix
The change adds the missing @Test annotation to testEqualsSkipHeaderRecord, so the method now runs with the rest of CSVFormatTest. No production code changes. The test, which builds a format with setSkipHeaderRecord(true) and checks that it is not equal to its counterpart in either direction, passes against the current code, and the default Maven build passes with it enabled.
The JUnit 5 migration added @Test to six methods of the class but missed the seventh, which sits under a @SuppressWarnings annotation. It is the regression test for restoring saved attached state twice.
Fix
@Test added; the class runs seven tests instead of six.