Release 10.0.0
Date: unreleased (in development)
Juneau 10.0.0 is a major version release representing a significant milestone for the Apache Juneau project. This release folds in the never-shipped 9.5 development line and also delivers a broad code-quality and maintainability uplift via an extensive SonarQube/SonarLint cleanup pass across the entire codebase.
Major version bump note
This is the first release in the 10.x line. The jump from 9.x to 10.0 reflects accumulated breaking changes in the API surface and internal architecture that warranted a major version boundary, following Semantic Versioning conventions.
Consumers upgrading from 9.4.x or earlier should consult the V10.0 Migration Guide for a complete list of breaking changes.
Code quality improvements
This release includes an extensive SonarQube / SonarLint cleanup pass across the entire Juneau codebase, addressing hundreds of code-quality findings including:
- Removed dead/unreachable code and unused variables.
- Fixed resource-leak issues (try-with-resources, explicit close calls).
- Resolved potentially null-dereference paths and improved null-safety.
- Replaced raw types with proper generic parameterization.
- Addressed improper exception handling patterns (swallowed exceptions, overly broad catch blocks).
- Cleaned up redundant casts, unnecessary object creation, and other minor inefficiencies.
- Suppressed or resolved remaining SonarQube rule violations with documented rationale.
No functional behavior was changed as part of this cleanup; all changes were purely mechanical code-quality improvements.
Module index
The new features and changes are grouped by module. Each page below holds one section per module (or module combination).
Marshall, commons and config
juneau-marshall, juneau-commons, juneau-config, juneau-core, juneau-bct, juneau-bean.
- juneau-marshall / juneau-commons
- juneau-config / juneau-commons
- juneau-marshall
- juneau-marshall
- juneau-commons
- juneau-config
- juneau-core
- juneau-bct (Bean-Centric Testing)
- juneau-bean
REST server
juneau-rest-server and its auth, metrics, tracing and view modules; juneau-rest-common.
- juneau-commons / juneau-rest-server
- juneau-rest-common / juneau-rest-server
- juneau-console-widgets / juneau-console-views
- juneau-rest-common
- juneau-rest-server
- juneau-rest-server-springboot
- juneau-rest-server-auth-jwt (new module)
- juneau-rest-server-auth-saml (new module)
- juneau-rest-server-auth-oauth (new module)
- juneau-rest-server-auth-oidc-rp (new module)
- juneau-rest-server-metrics-micrometer (new module)
- juneau-rest-server-tracing-otel (new module)
- juneau-rest-server-view-jsp (new module)
- juneau-rest-server-view-thymeleaf (new module)
- juneau-rest-server-view-mustache (new module)
- juneau-rest-server-view-freemarker (new module)
REST client
juneau-rest-client.
Microservice
juneau-microservice, the Jetty and Tomcat microservices, juneau-microservice-test.
- juneau-microservice-jetty
- juneau-microservice-jetty / juneau-microservice-tomcat
- juneau-microservice-test (new module)
- juneau-microservice (formerly juneau-microservice-core)
- juneau-microservice-jetty
Other modules
bean libraries, secrets, bean query, petstore, build and packaging.
- juneau-commons / juneau-secret-macos-keychain
- juneau-commons / juneau-beanquery-sql / juneau-beanquery-postgres
- Build & packaging (BOM + curated bundles)
- juneau-petstore (new module family)
- juneau-examples-core / juneau-petstore-core
- juneau-bean-rfc7807 (new module)
- juneau-bean-hal (new module)
- juneau-bean-jsonapi (new module)
- juneau-bean-jsonpatch (new module)
Bug Fixes
-
Prototext compact output now separates fields after a closing
}, and repeated/nested fields round-trip.PrototextSerializer.DEFAULT(no.ws()) wrote nothing after a message's closing brace, giving}ins {and}nums:. Every message now ends with a newline, so compact output reads e.g.ins {n: 1\nname: "a"\n}\nnums: [4]\n(visible output change for compact mode;.ws()output is unchanged and old compact output still parses).PrototextParsernow also accepts a single occurrence of a repeated field for a list/array property (a one-elementList<Bean>previously failed). Lists of lists were written as strings (["[1]", "[2, 3]"]); since protobuf text format has no nested-list syntax, each inner list is now a_valuemessage (grid {_value: [2, 3]}), and the parser unwraps it when the target type is a list or array. Without a declared target type (e.g. parsing into aJsonMap) the wrapper stays a map with a_valuekey. -
Fixed
RemoteClienttreatingCalendar/GregorianCalendararguments as beans.Calendarvalues are now sent as scalar parts, alongsideDateand thejava.timetypes. -
Fixed
JavaHttpTransportsending replayable request bodies chunked even when their length is known. Repeatable bodies with a known content length (StringBody,ByteArrayBody, etc.) are now sent with fixed-length framing (Content-Length) instead ofTransfer-Encoding: chunked, which some servers and proxies handle poorly. Unknown-length streaming bodies (e.g.StreamBody) remain chunked. -
Fixed RRPC method calls never dispatching over POST. Every HTTP POST to an
@RestOp(method="RRPC")operation previously returned a 404 instead of reaching the target method.RrpcRestOpSessionderived the RRPC method key by splitting the request path on the last/, but RRPC keys are of the formmethodName/(paramTypes)and themselves contain a/, so the method name was stripped off and the lookup always fell through toNotFound. The key is now derived from the already-computed URL-path-match remainder, so RRPC (REST-RPC) calls over POST dispatch correctly. -
Fixed
Json5Map/Json5Listfrozen (.unmodifiable()) instances still being mutable through several paths. TheUnmodifiablevariants ofJson5MapandJson5List(juneau-marshall) only overrode a subset of the mutator surface —put(String,Object)/remove(Object)for the map,add(int,·)/remove(int)/set(int,·)for the list — so a caller holding a supposedly-frozen instance could still mutate it through the remaining paths:clear(),putAll/addAll,compute*/merge/replace*, thekeySet()/entrySet()/values()views and their iterators, and (forJson5List) the fullDequesurface inherited fromLinkedList. Both classes now override the complete mutator surface, mirroring the siblingMarshalledMap/MarshalledListimmutable-beans convention, so mutating a frozenJson5Map/Json5Listalways throwsUnsupportedOperationExceptionas documented. Migration: none for correctly-written callers; this only affects code that was inadvertently relying on the freeze-bypass to mutate a "frozen" instance. -
Fixed the JSON serializer dropping carriage returns and emitting other control characters raw.
JsonSerializer,Json5Serializerand the otherJsonWriter-based serializers (juneau-marshall) previously silently dropped\rfrom string values and attribute names, and wrote every other control character (U+0000-U+001F except\n,\t,\b,\f, plus U+007F) unescaped, which is invalid JSON per RFC 8259 and is rejected by strict parsers such as Python'sjson.loads. The JSON serializer now escapes carriage returns as\rand all other control characters as\u00xx(lowercase hex) instead of dropping or emitting them raw.JsonTokenWriterlikewise now escapes U+007F. Migration: output for strings containing\ror control characters changes (\ris now preserved as\r); consumers that depended on\rbeing stripped must normalize line endings themselves. -
Fixed
ReadinessStateincorrectly shared across multiple default-configured microservices in one JVM.JettyServerComponent/TomcatServerComponentpreviously flippedReadinessState.resolve(ms.getBeanStore())on start/stop without ever registering a bean back into that store, andHealthAggregatorresolved readiness from the health-probe servlet's own (different)RestContextbean store — so on the default (no app@Bean ReadinessState) path, both sides silently fell back to the single JVM-wideReadinessState.shared()singleton. Stopping any one default-configured microservice therefore flipped/readyzto503for every other default-configured microservice running in the same JVM — a rolling-deployment hazard for any process embedding more than one microservice. EachJettyServerComponent/TomcatServerComponentnow constructs (or reuses an app-supplied@Bean) one per-serviceReadinessStateinstance and publishes that identical instance into bothms.getBeanStore()and the auto-mountedHealthServlet's ownRestContextbean store, so/readyzreflects only its own service's lifecycle.ReadinessState.shared()remains as the fallback for non-microservice embeddings (a plain servlet deployment, or any caller that never registers a bean) — it is no longer the microservice bridge. Migration: none for normal usage; a process embedding only one microservice per JVM is unaffected either way. -
Present-but-empty
RestContext.*boolean settings no longer throw (leniency improvement). Landed alongside the newRestContextProperties@ConfigPropertiesbean (see New Features above): previously, a present-but-empty value for a booleanRestContext.*setting (e.g.RestContext.eagerInit=) threwBeanCreationExceptionat injection time, because the old@Value booleanfield's strict conversion had no tolerance for an empty string. As of 10.0.0 this is resolved leniently tofalseinstead, for all 8 affected settings:disableContentParam,renderResponseStackTraces,problemDetails,virtualThreads,responseTraceparent,mdcAsyncPropagation,eagerInit, andlazyChildren. Migration: none for normal usage (a set-but-blank boolean setting is an unusual configuration to begin with); code that relied on the old throw to catch a misconfigured blank value must add its own explicit check. -
SVL variables in a
RestContext.*boolean setting now resolve through the resource's own var resolver (capability widening). Landed alongside the sameRestContextPropertieschange: a$C{...}/$R{...}/$S{...}-style variable embedded in a booleanRestContext.*value (e.g.RestContext.virtualThreads=$C{MyConfig/virtualThreads}) is now resolved through the owning resource'sVarResolverrather thanVarResolver.DEFAULT, so resource-scoped variables like$C{...}(config) and$R{...}(request) now work where they previously didn't. Migration: none for normal usage; this only expands which variables resolve successfully. -
Fixed Jetty aborting keep-alive connections after a chunked request body whose terminating chunk arrives late.
RequestContentnow drains any unread remainder of the request body after parsing (juneau-rest-server), so Jetty no longer aborts the connection. Previously this surfaced client-side as "header parser received no bytes" /NoHttpResponseExceptionon the next request over the same keep-alive connection. -
Fixed
BoundedServletInputStreamreturning0instead of-1at end of stream. Everyreadoverload (juneau-rest-server) now honors theInputStreamcontract:-1at end of input, including when the body is exactly@Rest(maxInput)bytes; a body larger than the limit still throwsIOException.
Breaking Changes
-
ApacheHc45Transportstrips every credential header on a cross-origin redirect (behaviour change). On a redirect to a different scheme, host or port, theCookiefrom the client's cookie store and theAuthorizationfrom its credentials provider, preemptive authentication or401challenge answer are now removed along with the headers you set on the request. This holds for transport-built and caller-supplied clients alike; before, a caller-supplied client still sent its cookie-storeCookieand credentials-providerAuthorizationto the new origin. Same-origin redirects are unchanged, and a chain that returns to the first origin restores that origin's credentials: in an A to B to A chain (an SSO round trip, say) the final hop back to A carries the headers you set, the cookie-storeCookieand the credentials-providerAuthorizationagain, while every hop that is not at the first origin stays stripped. The SSRF-guarded@Remoteredirect path (PolicyEnforcedRedirects) follows the same first-origin rule for every transport that uses it (JDK, OkHttp, HC4.5, HC5, mock) and now refuses a redirectLocationwith user info (the transport-built HC4.5 client does too). A returning hop is restored only forGET/HEAD; a307/308replay of another method after leaving the first origin stays stripped.Proxy-Authorizationis now removed on a tunnelled route, andhttpClient(...)refuses a legacyAbstractHttpClientsuch asDefaultHttpClient. This also changes the J0823 behaviour: a retry at the returning hop carries the restored credentials. The SSRF-guarded@Remotepath now strips the cookie-storeCookieon a cross-origin hop too; before, only the headers set on the request were removed there. See REST Security. -
juneau-my-jetty-microserviceremoved. The template-project module has been retired in favor of the newJettyMicroservicefacade + bundled defaults injuneau-microservice-jetty. See the New Features section above for migration guidance. -
juneau-examples-rest{,-jetty,-springboot,-jetty-ftest}removed. The four-module legacy example family has been retired in favor of the newjuneau-petstore-{core,jetty,springboot}showcase family. The legacy modules carried an unpatched Hibernate ORM 5.6.x SQL-injection vulnerability (CVE-2026-0603) that had no upstream fix because Hibernate 5.6.x is end-of-life; deletion closes that exposure surface. The accompanyingscripts/start-examples-rest-jetty.pyandscripts/start-examples-rest-springboot.pylaunchers are replaced byscripts/start-petstore-jetty.pyandscripts/start-petstore-springboot.py. The five legacy18.0x.JuneauPetstore*topic pages (which described the retired externalapache/juneau-petstorerepository) and the three19.0x.JuneauExamplesRest*topic pages are replaced by the single new juneau-petstore Overview page. -
Public
ObjectRestclass removed. Theorg.apache.juneau.objecttools.ObjectRestclass is gone; its URL-addressed GET/PUT/POST/DELETE traversal engine survives only as an internalPathTraversalhelper (not a supported public API). Per-consumer migration:ResponseContent.asObjectRest()/asObjectRest(Class)(juneau-rest-client) — removed. Replace withresponse.getContent().as(JsonMap.class).getAt(path, type), or the RFC 6901 formresponse.getContent().as(JsonMap.class).at(pointer).- Reflective helpers —
ObjectRest.invokeMethod(...)/getPublicMethods(...)were dropped; useObjectIntrospectordirectly if you need that capability. - Unaffected —
MarshalledMap/MarshalledList(andJsonMap/JsonList)getAt/putAt/postAt/deleteAtare unchanged (now backed byPathTraversal); the companion exception is nowPathTraversalException(public, renamed to matchPathTraversal); theTraversableREST response converter and Swagger/OpenAPI$ref(#/) resolution behavior are unchanged. - New replacement APIs — the new
MarshalledNodetyped tree façade andJsonPointer(see New Features above) provide typed navigation and RFC 6901 JSON-Pointer addressing over the same collections model.
-
REST debug/logging system fully redesigned (
juneau-rest-server) (hard break, no deprecation shim). The entire multi-signal debug/logging mechanism has been replaced by a single JUL-logger-level-driven model (see New Features above). Removed with no replacement annotation, attribute, or config key:@Rest(debug=...)/@RestOp(debug=...), the nested@Debugannotation and itsvalue()/on()/format()/level()/config()members,debugDefault/debugEnablement/debugOn; thedebug/package (DebugEnablement,BasicDebugEnablement,DebugConfig,DebugRule,DebugFormat, and friends); theCallLoggerlogging SPI (CallLogger,CallLoggerRule,CallLoggingDetail,BasicCallLogger,BasicTestCallLogger, and friends) includingRest#callLogger(); and thejuneau.restLogger.*config keys. Replacement: set the resource's ownjava.util.logging.Loggerlevel (INFO/FINE/FINEST) and, if you need custom formatting, implement the newRestDebugFormatterSPI.RestRequest.isDebug()is kept but redefined as derived/read-only. See the V10 Migration Guide for the full mapping, including a footgun callout about raising a resource's logger level for unrelated application logging. -
Next-generation
RestClientdebug logging redesigned around JUL levels +RichLogger(hard break, no shim). The NG client (org.apache.juneau.rest.client.RestClient) removesRestLogger,RestLogEntry,RestLogLevelResolver,BasicRestLogger, builderlogger(...), and per-requestRestRequest.debug(). Replacement is single-signal, level-driven debug on the configured logger name:INFO(basic),FINE(headers),FINEST(headers + bounded bodies). New builder entry points aredebugLoggerName(String)(defaultRestClient.class.getName()) anddebugFormatter(RestClientDebugFormatter)(defaultBasicRestClientDebugFormatter). For tests, capture emitted records withRichLogger.getLogger(name).captureEvents(Level)and assert againstLogRecords. -
Marshallerinstance API kept asread/write; staticof/toshortcuts reintroduced on concrete classes. An interim plan to rename the top-level convenience methods onMarshaller,CharMarshaller, andStreamMarshallertoto/ofwas not carried through — the instance methods remainread/write, andto/ofship instead as static shortcuts on the concrete marshaller classes (e.g.Json,Xml,Protobuf), delegating to each class'sDEFAULTinstance:- Instance (unchanged names):
Marshaller.read(Object, Class<T>)/read(Object, Type, Type...);Marshaller.write(Object, Object).CharMarshalleradds aString-basedread(String, ...)and aString-returningwrite(Object);StreamMarshalleradds abyte[]-basedread(byte[], ...)and abyte[]-returningwrite(Object). - Static shortcuts (new, on each concrete class):
Xxx.to(input, type, ...)parses viaXxx.DEFAULT.read(...);Xxx.of(object)serializes viaXxx.DEFAULT.write(...)— e.g.Json.to(json, MyBean.class)/Json.of(myBean). - Streaming/record cursors (capability-gated) — only available when the underlying serializer/parser implements the corresponding marker interface (
TokenReadable/TokenWritable,RecordReadable/RecordWritable,ArrayRecordReadable/ArrayRecordWritable): instancereadTokens/writeTokens,readRecords/writeRecords,readArrayRecords/writeArrayRecords; and their static counterpartstoTokens/ofTokens,toRecords/ofRecords,toArrayRecords/ofArrayRecords.
- Instance (unchanged names):
-
Marshaller readable/compact instance constants removed in favor of variant classes. The
Json5.DEFAULT_READABLE,Ini.DEFAULT_READABLE, andHjson.DEFAULT_COMPACTpublic constants have been removed. Use the new first-class variant marshaller classes instead (see New Features above):Json5R(readable JSON5),IniR(readable INI), andHjsonC(compact Hjson). Each variant is a subclass of its base facade with its ownDEFAULTand the full staticof(...)/to(...)shortcut surface, so migration is typically a direct swap:Json5.DEFAULT_READABLE.write(x)→Json5R.of(x)(orJson5R.DEFAULTfor an instance).Ini.DEFAULT_READABLE.write(x)→IniR.of(x).Hjson.DEFAULT_COMPACT.write(x)→HjsonC.of(x).
The in-tree call sites (
BasicSwaggerProviderSession,BasicOpenApiProviderSession, and theAtomJsonExample) were migrated to the variant classes.Note: the
RecordReader.read()andRecordWriter.write()cursor methods are a separate, unrelated low-level cursor contract — they were never part of this rename discussion. If you adopted the earlierto/of-as-instance-methods preview, revert those call sites back to.read(/.write(on the marshaller instance, or switch to the static shortcut on the concrete class (Json.to(...)/Json.of(...)) if you don't need a custom-configured instance. -
SerializerSet/ParserSetlookup methods now returnOptional<...>. Every media-type lookup method onSerializerSetandParserSetnow returns ajava.util.Optionalinstead ofnullon no-match, aligning these classes with theOptional-returning idiom already used by their consumers (RestResponse.getSerializerMatch(),RequestContent.getParserMatch()). Changed signatures:SerializerSet:getSerializer(MediaType)/getSerializer(String)→Optional<Serializer>;getSerializerMatch(MediaType)/getSerializerMatch(String)→Optional<SerializerMatch>;getStreamSerializer(MediaType)/getStreamSerializer(String)→Optional<OutputStreamSerializer>;getWriterSerializer(MediaType)/getWriterSerializer(String)→Optional<WriterSerializer>.ParserSet:getParser(MediaType)/getParser(String)→Optional<Parser>;getParserMatch(MediaType)/getParserMatch(String)→Optional<ParserMatch>.- The
getSerializers(),getParsers(), andgetSupportedMediaTypes()collection getters are unchanged (they were nevernull). - Both
MediaTypeoverloads now guardnulland returnOptional.empty()(previouslyParserSet.getParserMatch(MediaType)threw an NPE on anullargument). - Migration: unwrap with
.map(...)/.orElseThrow(...)/.ifPresent(...), or.orElse(null)to preserve a prior null contract. For the HTTP-server direction, throwing the status-correct exception is idiomatic —NotAcceptable(406) for a failedAccept/serializer lookup andUnsupportedMediaType(415) for a failedContent-Type/parser lookup. The next-genRestClient.getSerializerForMediaType(String),getParserForMediaType(String), andgetMatchingParser(String)lookup methods likewise now returnOptional<...>.
-
RestOpContextmatcher getters changed from arrays toList.RestOpContext.getPathMatchers(),getOptionalMatchers(), andgetRequiredMatchers()previously returned raw arrays (UrlPathMatcher[]/RestMatcher[]respectively); as of 10.0.0 they return an immutable, cachedListinstead (List<UrlPathMatcher>/List<RestMatcher>), closing a mutable-state-exposure hole where a caller holding the returned array could corrupt the op context's routing/matcher state. This is source-incompatible for any caller that assigned the result to an array-typed variable or indexed it with[i]. Migration: switch toList-based access (.get(i)/.size()/ iteration), or call.toArray(new UrlPathMatcher[0])(ornew RestMatcher[0]) if an array is specifically required. -
Next-gen
RestClientno longer falls back to JSON implicitly (behavioral change). The next-generationorg.apache.juneau.rest.client.RestClientpreviously treated a fully-unconfigured client as "JSON in / JSON out" and also used a lone registered parser/serializer regardless of media type. Both implicit behaviors are removed: content negotiation now resolves a parser/serializer only via an exact media-type match or an explicitly-configured default, otherwise it resolves to nothing.- New opt-in builder knobs restore the old behavior deliberately:
RestClient.Builder.defaultParser(Parser)andRestClient.Builder.defaultSerializer(Serializer)(e.g..defaultParser(JsonParser.DEFAULT).defaultSerializer(JsonSerializer.DEFAULT)). - Resolution precedence is now: exact media-type match → explicitly-configured default → none.
- A response whose
Content-Typematches no registered parser (and with no default parser configured) now fails withUnsupportedMediaType(HTTP 415) instead of silently parsing as JSON — affectingResponseBody.as(...)and remote-proxy (@Remote) response deserialization. On the request/serializer side, serializing a POJO body with no matching or default serializer now throws a clear client-sideIllegalStateExceptionrather than defaulting to JSON. - Migration: if you relied on the out-of-the-box JSON behavior, add
.defaultParser(JsonParser.DEFAULT)and/or.defaultSerializer(JsonSerializer.DEFAULT)to yourRestClient.Builder. The classicorg.apache.juneau.rest.client.classic.RestClientis unaffected.
- New opt-in builder knobs restore the old behavior deliberately:
-
Test modules merged into
juneau-bct.juneau-assertionsandjuneau-junit5have been folded intoorg.apache.juneau:juneau-bct, the 9.2 Bean-Centric Testing coordinate, which is now one commons-only module that also carries the fluent assertions and the JUnit 5 extensions. The two old artifacts are removed (no relocation POMs), and the BCT packages moved (see below). Migration:- Dependency: replace any
juneau-assertions/juneau-junit5dependency with a singlejuneau-bctdependency (dedupe where you used more than one). An existing 9.2juneau-bctdependency keeps its coordinate; only its version and imports change. - Package renames:
org.apache.juneau.assertions→org.apache.juneau.bct.assertions;org.apache.juneau.junit5→org.apache.juneau.bct.junit;org.apache.juneau.junit.bct(and its…bct.annotationssub-package) →org.apache.juneau.bct. Update imports accordingly.
- Dependency: replace any
-
New 10.0 modules named after their projects. The modules added during 10.0 development were renamed before release, so code built against a 10.0.0 snapshot needs the new artifactIds and packages:
- Juneau Terminal:
juneau-run→juneau-terminal;juneau-run-report→juneau-terminal-report(org.apache.juneau.terminal.report);juneau-rest-server-terminal→juneau-terminal-region(org.apache.juneau.terminal.region);juneau-rest-server-terminal-bus→juneau-terminal-bus(org.apache.juneau.terminal.bus). TheJUNEAU_RUN_*environment variables are nowJUNEAU_TERMINAL_*. - Juneau Console:
juneau-rest-server-console-ui→juneau-console(org.apache.juneau.console);juneau-rest-server-console-ui-freemarker→juneau-console-freemarker(org.apache.juneau.console.freemarker);juneau-rest-server-views→juneau-console-viewsandjuneau-rest-server-views-markdown→juneau-console-views-markdown(org.apache.juneau.console.views);juneau-rest-server-widgets→juneau-console-widgets(org.apache.juneau.console.widgets);juneau-rest-server-datatables→juneau-console-datatables(org.apache.juneau.console.datatables);juneau-rest-server-bus→juneau-console-busandjuneau-rest-server-bus-websocket→juneau-console-bus-websocket(org.apache.juneau.console.bus). - Juneau BCT:
juneau-test→juneau-bct(org.apache.juneau.test.*→org.apache.juneau.bct.*). - Removed JSON-assertion methods on
FluentObjectAssertion:asJson(),asJsonSorted(),isJson(String),isSameJsonAs(Object),isSameSortedJsonAs(Object),asString(WriterSerializer), andisSameSerializedAs(Object, WriterSerializer)are gone (the assertions module no longer depends onjuneau-marshall). Where you still want JSON/serialized comparisons, pass a serializer explicitly:assertObject(x).asString(Json5Serializer.DEFAULT).is("{…}").WriterSerializernow implementsThrowingFunction<Object,String>andOutputStreamSerializerimplementsThrowingFunction<Object,byte[]>directly, so a serializer instance can be passed anywhere aFunction/ThrowingFunctionis expected (no bridge method needed). TheasString(Function<T,String>)transform is retyped toasString(ThrowingFunction<? super T,String>), and a newisSameSerializedAs(Object, ThrowingFunction<? super T,String>)overload replaces the removedWriterSerializeroverload.
- Juneau Terminal:
-
Microservice demo resources moved out of the shipping jars into a new unpublished example module. Seven low-value demo
@Restresources —LogsResource,LogParser,DirectoryResource,ShutdownResource,SampleRootResource,ConfigResource(formerlyorg.apache.juneau.microservice.resourcesinjuneau-microservice) andDebugResource(formerlyorg.apache.juneau.microservice.jetty.resourcesinjuneau-microservice-jetty) — have relocated to the neworg.apache.juneau.microservice.examplespackage in a new sibling module,juneau-microservice-examples.juneau-microserviceandjuneau-microservice-jettyno longer ship these demo resources, shrinking both their public API surface and their file-system-touching security footprint. The new module is deliberately unpublished — it is not listed injuneau-bomor any-bundleaggregator, and exists only to build/test in the reactor — so it is not a Maven Central dependency you can pull in; treat the classes as copy-and-adapt reference samples (juneau-petstore-jettyis the one in-tree consumer, now depending on the module directly). Migration: update any import of the old FQCNs (org.apache.juneau.microservice.resources.*/org.apache.juneau.microservice.jetty.resources.*) toorg.apache.juneau.microservice.examples.*. Separately, the two classes that stayed in core —LogEntryFormatterandTraceContext— moved out of the now-removed...microservice.resourcespackage intoorg.apache.juneau.microservice.logging(non-breaking behaviorally, but a package change for any code that imported them directly).- Admin samples hardened against cross-site requests.
ShutdownResourcenow answersPOSTonly (aGETreturns 405).LogsResourceno longer accepts themethodquery parameter: its view, parse and download actions are plainGETroutes (GET /logs/view/{path},/logs/parse/{path}and/logs/download/{path}, replacing theVIEW,PARSEandDOWNLOADmethods), its listing no longer offers a delete link, and a delete must be a realDELETE.ConfigResourcedrops its HTML edit form (GET /config/editand the formPOST); update the config withPUT. A newAdminBoundary.registration(prefix, allowedHosts...)returns aServletFilterRegistrationthat puts all three behind aCsrfBoundary. With no host names it answers onlylocalhost,127.0.0.1and[::1], so any otherHostis refused with 421, reads included. The Jetty petstore registers it, so a cross-site write to/config,/logsor/shutdownthere is refused with 403 and anX-Request-Boundaryheader. Behavior change: the petstore's admin resources now answer loopback host names only; set the newpetstore.admin.allowedHostssetting (comma-separated, no ports; independent ofpetstore.api.allowedHosts) to reach them under any other name. Command-line callers are unaffected when they send aPUTorDELETE, or aPOSTwith a JSON content type.ShutdownResourcetakes how it exits as a constructor argument (new ShutdownResource(exit), defaultSystem.exit). See Protecting the admin resources.
- Admin samples hardened against cross-site requests.
-
String formatting helpers
f()/fs()(andStringUtils.format) are now printf-only. The general-purpose formatting helpers onorg.apache.juneau.commons.utils.Shorts—f(String,Object...)andfs(String,Object...)— together withStringUtils.format(...), previously accepted bothjava.util.Formatterprintf syntax (%s,%d,%1$s) andjava.text.MessageFormatsyntax ({0},{1,number},''-quoting). As of 10.0.0 they are printf-only: a pattern is passed straight through toString.format(...)semantics, so{0}-style placeholders are emitted literally and single quotes are no longer treated as MessageFormat escapes (a doubled''now renders as'', not').- Drop-in MessageFormat replacement: new
mf(String,Object...)/mfs(String,Object...)helpers onShorts(backed byStringUtils.mformat(...)) render the full MessageFormat grammar ({0},{1,number},{2,date},''-quoting). Any caller that genuinely relied on MessageFormat behavior should migratef(...)→mf(...)andfs(...)→mfs(...). - Migration: for the common case, change MessageFormat patterns to printf —
f("Value {0} of {1}", a, b)→f("Value %s of %s", a, b); for out-of-order/reused indices use positional printf —{1} {0}→%2$s %1$s,{0}…{0}→%1$s…%1$s. Any literal%in a pattern now needs escaping as%%. Where you truly need the MessageFormat engine (typed{0,number}/{0,date}, externally-authored i18n patterns), switch that call tomf()/mfs(). - i18n unaffected:
Messages.getString(key, args)(resource-bundle i18n) andLogRecord.getMessage()(logging) continue to render MessageFormat-style.properties/log patterns — they now route throughmf()internally, so{0}-style entries in your resource bundles keep working exactly as before. Juneau'sRichLoggerMessageFormat-style logging is likewise unchanged. - The dual-syntax
StringFormatengine still exists and remains directly usable (StringFormat.of(...)) for callers that want both grammars in one pattern; only thef()/fs()/StringUtils.formatentry points were narrowed to printf.
- Drop-in MessageFormat replacement: new
-
AssertionUtilsargument/state checks removed; useShorts.req*(IAE) /Shorts.chk*(ISE) viaimport static org.apache.juneau.commons.utils.Shorts.*;.assertArg*,assertType,assertClassArrayArgIsType,assertStateandAssertionUtils.assertNotNullare deleted fromorg.apache.juneau.commons.utils.AssertionUtils; onlyassertOneOfremains. Exception types and messages are unchanged. See the V10.0 Migration Guide for the full rename table. -
Shorts.or(boolean...)renamed toShorts.any(boolean...); new null-safeShorts.nm(x, X::f).or(boolean...)(true if any argument is true) madeor(Boolean, boolean)ambiguous with the first-non-nullor(T...), so it is nowany(boolean...)and the boolean overload is gone with no alias. A primitiveor(a, b)call that used to be an any-true now resolves toor(T...)and returns the first non-null (alwaysa), so migrate such calls toany.and(boolean...)is unchanged.nm(x, mapper)(backed byObjectUtils.mapOrNull) returnsnullfor anullinput without calling the mapper, replacingx == null ? null : f(x). -
juneau-commonslogging API rename:Logger->RichLogger(hard break, no shim). The public commons logging typeorg.apache.juneau.commons.logging.Loggeris removed and replaced byorg.apache.juneau.commons.logging.RichLoggerwith no compatibility alias. Migration is mechanical for most call sites: update imports and static factories (Logger.getLogger(...)->RichLogger.getLogger(...)). The rename ships with new logging capabilities:MessageGenerator-driven formatting (printf or MessageFormat), canonical-vs-view logger identities, weak-valued logger retention with capture pinning, hierarchical parent-chain capture propagation, and generalized JUL-record capture/listener APIs. As part of this landing, five previously silentWARNINGemission sites in the REST logging path now publish through JUL parent/root handlers instead of being dropped by delegate wiring. -
REST debug test capture consolidated onto
RichLogger(juneau-rest-server/juneau-rest-mock).org.apache.juneau.rest.server.logging.CapturingRestDebugFormatterand its dedicated rest-server Mockito unit test are removed. The replacement capture path is logger-name based: capture on the resource logger withRichLogger.getLogger(MyResource.class).captureEvents(Level.FINEST)and assert against capturedLogRecords. This captures both op-routed records (<hostResourceClass>.<methodName>) and 404/no-op records (<resourceClass>) via the new hierarchical capture propagation. -
LogRecord.formatted(...)no longer supports source-detection placeholders (behavioral change).org.apache.juneau.commons.logging.LogRecordremoved its unreliable JUL-style stack-walk source detection — thefindSource()walk and thegetSourceClassName()/getSourceMethodName()overrides — along with the{source},{class}, and{method}named placeholders informatted(String). As a result, positional%N$sFormatter-style specifiers passed toformatted(...)shift down by one index, since the removed source argument no longer occupies a slot (e.g. what was%3$sfor the logger name is now%2$s). Migration: remove any{source}/{class}/{method}placeholders from format strings (no replacement — source detection was unreliable and is not coming back), and renumber any positional%N$sspecifiers down by one. -
Serializer/Parser I/O narrowed to streams —
FileandStringBuilderno longer accepted. The core serialize/parse I/O surface now accepts onlyReader/Writer/InputStream/OutputStream(plus the existing scalarString/byte[]/CharSequenceshortcut forms). TheFileandStringBuilderinstanceofbranches were removed from the internalSerializerPipe/ParserPipeI/O plumbing, so passing aFileorStringBuildertoSerializer.write(Object, Object),Parser.read(Object, ...), or any sessioncreatePipe(...)now throws"Cannot convert object of type ... to a Writer/OutputStream/InputStream"at runtime. (Note: this is a runtime contract change — the methods are typedObject, so callers passingFile/StringBuilderstill compile.)-
The same narrowing applies to the token/record streaming surface:
writeTokens(...)/readTokens(...)/writeRecords(...)/writeArrayRecords(...)(and theMarshallerofTokens/ofRecords/ofArrayRecordsshortcuts) no longer acceptFile/StringBuildereither — the*TokenWriter.forOutput(...)factories (JSON / JSONL / CBOR / MsgPack) are stream-only. -
Migration —
File: wrap it yourself.// Serialize to a file:
try (Writer w = Files.newBufferedWriter(path)) { // char-based formats
JsonSerializer.DEFAULT.write(bean, w);
}
try (OutputStream os = Files.newOutputStream(path)) { // byte-based formats
MsgPackSerializer.DEFAULT.write(bean, os);
}
// Parse from a file:
try (Reader r = Files.newBufferedReader(path)) { // char-based formats
var bean = JsonParser.DEFAULT.read(r, MyBean.class);
} -
Migration —
StringBuilder: serialize to aStringWriterand read.toString()afterward (or just call theString-returning shortcutJson.of(bean)/serializer.write(bean)).var sw = new StringWriter();
JsonSerializer.DEFAULT.write(bean, sw);
String json = sw.toString(); -
The
SerializerPipe/ParserPipeclasses are retained (they still own the stream close/flush lifecycle, charset decoding, debug capture, and position tracking for the surviving stream types) — only theirFile/StringBuilderhandling and the now-deadfileCharsetpipe-constructor argument were removed. ThefileCharset(...)builder setting was subsequently removed as well (see the next entry).
-
-
fileCharset(...)serializer/parser builder setting andgetFileCharset()getter removed. With the stream-only I/O change (above), the file-charset setting became a dead no-op — the narrowedSerializerPipe/ParserPipeplumbing no longer reads it. It has now been removed entirely:- Builder methods removed:
WriterSerializer.Builder#fileCharset(Charset),ReaderParser.Builder#fileCharset(Charset), and the session-builder variantsWriterSerializerSession.Builder#fileCharset(Charset)/ReaderParserSession.Builder#fileCharset(Charset). - Getters removed:
WriterSerializer#getFileCharset()/ReaderParser#getFileCharset()(protected) andWriterSerializerSession#getFileCharset()/ReaderParserSession#getFileCharset()(public). - Annotation attributes removed:
@SerializerConfig(fileCharset=...)and@ParserConfig(fileCharset=...). - Session property keys removed: the
"fileCharset"(and qualified"WriterSerializerSession.fileCharset"/"ReaderParserSession.fileCharset") sessionproperty(...)keys are no longer recognized. - Migration: none needed for correctness — the setting had no effect after the stream-only I/O change. Remove any lingering
.fileCharset(...)builder calls or@SerializerConfig/@ParserConfigfileCharsetattributes. If you relied onFile-charset behavior, decode theReader/InputStreamyourself before passing it in (see theFilemigration snippet above).
- Builder methods removed:
-
Version.equals()/hashCode()/compareTo()are now strict (exact); prefix matching moved toVersion.matches(Version).org.apache.juneau.commons.lang.Versionpreviously used prefix-tolerant equality — a shorter version was considered equal to any longer version sharing its prefix (Version.of("1.2").equals(Version.of("1.2.3"))→true), whilehashCode()(Arrays.hashCode(parts)) andcompareTo(...)were length-sensitive. That combination violated theequals/hashCode/compareTocontract and could corruptHashSet/HashMap/TreeSet/TreeMapmembership. As of 10.0.0:equals(Object)is now exact — two versions are equal only when their full component sequences are identical, soVersion.of("1.2"),Version.of("1.2.0"), andVersion.of("1.2.3")are now all unequal.hashCode()(unchanged,Arrays.hashCode(parts)) andcompareTo(...)(unchanged, already strict) are now mutually consistent withequals.- The removed
boolean equals(Version)overload's prefix semantics live on as a newboolean matches(Version)method —Version.of("1.2").matches(Version.of("1.2.3"))→true. Usematches(...)wherever you previously relied on prefix/compatibility comparison. Note thatmatches(...)is intentionally not consistent withhashCode()and must never be used for hash-/tree-based collection membership. - Unaffected:
isAtLeast(...)/isAtMost(...)(and thereforeVersionRange, client-version negotiation, and theFluentVersionAssertioncomparison methods, which usecompareTo) are unchanged. - Migration: replace any
v1.equals(v2)call on twoVersionvalues that depended on prefix matching withv1.matches(v2). Exact equality (assertEquals,Set/Mapkeys) now behaves per the corrected contract with no code change.
-
juneau-beanDTO collection/array getters no longer expose live internal state (behavioral change). Getters on the DTO beans that previously returned the bean's live internal collection or array now return a defensive, immutable view so that caller-side mutation can no longer corrupt bean state:- Collection /
Map/Setgetters injuneau-bean-jsonapi(JsonApiResource,JsonApiDocument,JsonApiResourceIdentifier,JsonApiLink,JsonApiVersion,JsonApiRelationship,JsonApiError),juneau-bean-jsonschema(JsonSchema),juneau-bean-mcp-v20250618(ListPromptsResult,ListToolsResult,ListResourcesResult,ReadResourceResult,CallToolResult,GetPromptResult,Prompt,CallToolRequest,GetPromptRequest,ServerCapabilities,ClientCapabilities,JsonSchema),juneau-bean-openapi-v3(OpenApi,Callback,Parameter,Components,PathItem,SchemaInfo),juneau-bean-hal(HalResource),juneau-bean-rfc7807(Problem), andjuneau-bean-swagger-v2(ParameterInfo,ResponseInfo) now return an unmodifiable view. Attempting toadd/put/remove/clearon a returned collection now throwsUnsupportedOperationException. The unset-vs-empty distinction is preserved exactly (a getter that returnednullwhen the property was never set still returnsnull, so serialization output is unchanged); use the bean'ssetX(...)/addX(...)methods to mutate. - Array getters in
juneau-bean-atom(CommonEntry.getAuthors()/getCategories()/getContributors()/getLinks(),Feed.getEntries()) and the mutable-CalendargetterCommonEntry.getUpdated()now return a defensive copy (arrayclone()/Calendarclone) rather than the live field, and the corresponding setters store a defensive copy of their argument. Mutating a returned array/Calendar(or an array previously handed to a setter) no longer affects the bean. Getter return types are unchanged (X[]staysX[]). - Migration: callers that mutated a value returned from one of these getters must instead go through the bean's setter/adder API. No source changes are required for read-only usage.
- Collection /
-
BasicHeader.hashCode()is now consistent withequals(Object)(behavioral change).org.apache.juneau.http.classic.header.BasicHeader(juneau-rest-common-classic) previously computedequals(Object)fromname+ value whilehashCode()returned the identity hash. That inconsistency violated theequals/hashCodecontract and brokeHashMap/HashSetmembership for equal-but-distinctBasicHeaderinstances. As of 10.0.0hashCode()is derived fromname+ value, so two headers that compare equal now share a hash bucket. Migration: none for normal usage; only affects code that relied on identity-hash bucketing ofBasicHeader(none found in the codebase). -
Fluent request/response header & param assertions are now null-tolerant (behavioral change).
FluentRequestHeaderAssertion,FluentRequestQueryParamAssertion, andFluentRequestFormParamAssertion(juneau-rest-server) andFluentResponseHeaderAssertion(juneau-rest-client-classic) previously threw aNullPointerExceptionwhen constructed against anullheader/part (e.g. asserting on a header that was never sent). As of 10.0.0 anullvalue is tolerated: the assertion is built with a null test value instead, so it behaves as if testing a part with no value —isNull()passes, and transform methods (asString(),asInteger(),asBoolean(),as(Class), etc.) yieldnull-valued assertions rather than throwing. Migration: none for normal usage; code that relied on the constructor throwing for a missing header/param must now check the resulting assertion (e.g.isNull()) explicitly. -
HTTP request/response/entity/part getters in
juneau-rest-common,juneau-rest-common-classic, andjuneau-rest-client-classicno longer expose live internal state (behavioral change). To prevent cross-caller/cross-thread state corruption (several of the affected objects are shared singletons — e.g. theOk.OKresponse and the ~33/~22 pre-built exception/responseINSTANCEs), the following now return defensive copies / unmodifiable views rather than the live field:byte[]getters (ByteArrayEntity/StreamEntity/FileEntity/ReaderEntity/StringEntity.asBytes()),HeaderElement[]getters (BasicHeader.getElements(),ResponseHeader.getElements()), and CSV token-array getters (BasicCsvHeader/BasicCsvArrayPart/HttpCsvHeader/HttpCsvArrayPart.orElse(...)) now return aclone()/copy. Mutating a returned array no longer affects the source object or any shared constant.- The cached remote-interface metadata (
RrpcInterfaceMeta/RrpcInterfaceMethodMeta/Policy, andRemoteMeta's header list) now hands out copies/unmodifiable views instead of the live cached array/list. - Migration: none for read-only usage; callers that mutated a returned array/list must instead rebuild the object through its builder/setter API.
-
juneau-petstore-corecollection getters no longer expose live store state (behavioral change). For consistency with the already-hardenedPetStore.getPets(),PetStore.getOrders()/getUsers()now return an immutable snapshot (List.copyOf(...)), andPet.getTags()returns an unmodifiable view whilePet.setTags(...)stores a defensive copy. Migration: none for read-only usage (demo/sample domain code). -
juneau-marshallpublic classorg.apache.juneau.marshall.httppart.Constantsremoved. This obsolete public constants holder — exposing theCF_*(collection-format),TYPE_*(data-type), andFORMAT_*(data-format)Stringconstants — was unreferenced anywhere in the framework and has been deleted. It is distinct from the still-supportedorg.apache.juneau.marshall.Constants(which is unaffected). Migration: none expected for normal usage; any external code that referenced these constants should inline the equivalent literal values (e.g."csv","string","int32") or use theHttpPartSchemaAPI, which is the supported way to express HTTP-part collection formats / types / formats. -
juneau-bean-openapi-v3copy-constructors now deep-copy nested copyable beans (bug fix). Severalorg.apache.juneau.bean.openapi3copy-constructors previously copied a bean-typed field shallowly (this.x = copyFrom.x;), so a copy and its original shared one mutable nested-bean instance and a mutation to one silently corrupted the other. These fields are now deep-copied via the type's null-safecopyOrNull(...):OpenApi.components/externalDocs,Operation.externalDocs/requestBody,PathItem.get/put/post/delete/options/head/patch/trace,OAuthFlows.implicit/password/clientCredentials/authorizationCode,SecuritySchemeInfo.flows,Parameter.schema,MediaType.schema,HeaderInfo.schema, andSchemaInfo.discriminator. The equivalentjuneau-bean-swagger-v2copy-constructors were already deep-copying these fields and are unchanged. Migration: none;copy()now returns a fully independent deep copy as documented. -
juneau-rest-serverThrownStats.copy(ThrownStats)static method removed; replaced by null-safecopyOrNull(ThrownStats). Theorg.apache.juneau.rest.server.stats.ThrownStatsdeep-copy factorypublic static ThrownStats copy(ThrownStats)has been removed and replaced bypublic static ThrownStats copyOrNull(ThrownStats value), which returns the same deep copy but additionally returnsnullfor anullargument (the oldcopy(null)threw aNullPointerException). This aligns the method with the new null-safecopyOrNull(T)convenience convention (see API Changes). Migration: replace anyThrownStats.copy(x)call withThrownStats.copyOrNull(x). -
Classic HTTP bean families move to a "funnel + nested
Unmodifiablesnapshot" immutability model;setUnmodifiable()removed. Thejuneau-rest-common-classicbean hierarchies —BasicHttpResponse(+ ~22 leaves),BasicHttpException(+ ~33 leaves),BasicHttpEntity(+ 6 leaves:ByteArrayEntity,FileEntity,ReaderEntity,SerializedEntity,StreamEntity,StringEntity),BasicStatusLine,BasicResource(+ 5 leaves:ByteArrayResource,FileResource,ReaderResource,StreamResource,StringResource), and theHeaderList/PartListlists — replace the old internal mutable-flag idiom (asetUnmodifiable()latch plus a per-mutatorassertModifiable()guard) with a single mutation funnel per class and a nestedX.Unmodifiablesnapshot type.-
setUnmodifiable()is removed. It froze the receiver itself in place. Migrate tox.unmodifiable(), which instead returns a new, frozen point-in-time snapshot and leaves the receiver mutable — unless the receiver is already anX.Unmodifiablesnapshot, in which case it returnsthis(idempotent).// Before (9.x):
Ok ok = new Ok();
ok.setUnmodifiable(); // ok itself is now frozen
// After (10.0.0):
Ok ok = new Ok();
Ok frozen = ok.unmodifiable(); // ok is still mutable; frozen is a new, separate snapshot -
isUnmodifiable()is retained, source-compatible, now implemented as a type check against a newUnmodifiableBeanmarker interface rather than a backingbooleanfield. -
BasicHttpResponseandBasicHttpEntitybecome generic and effectively abstract (X<SELF extends X<SELF>>, CRTP), which also drops each leaf's covariant setter overrides. Raw-type callers (BasicHttpResponse r = ...) keep compiling with at most an unchecked-raw warning; leaf-typed callers (Ok o = ...) are unaffected; leaf-typedINSTANCEconstants (e.g.Ok.INSTANCE,NotFound.INSTANCE) keep their exact leaf types — no caller churn. Any direct instantiation of the root (new BasicHttpResponse(...)/new BasicHttpEntity(...)) must be rebased onto a concrete leaf. -
BasicHttpExceptionstays non-generic — the JLS (§8.1.2) forbids a genericThrowablesubclass — so only thesetUnmodifiable()removal applies there; exception leaves keep their covariant setter overrides. -
equals()/hashCode()become content-based on these beans (previously identity-based) — a semantic change for any code relying on identity equality. For exceptions, equality is computed over the status line, headers, and message (excluding the value-less content wrapper). -
ControlledArrayListis removed.HeaderList/PartListare reparented directly ontoArrayList, with a nestedUnmodifiablesubclass overriding the full collection-mutator set (add/remove/set/clear/addAll/… plus iterator mutation) to throw. Listequals()/hashCode()are now content-only, ignoring mutability. -
Two bug fixes bundled with this migration: the
Thrownresponse header now reports the logical (declared) exception type rather than a frozenINSTANCEsnapshot's synthetic$Unmodifiablesubclass name, so client-side exception reconstruction from a frozen shared instance (e.g.BadRequest.INSTANCE) no longer breaks; andBasicStatusLine's copy constructor now faithfully copiesreasonPhraseCatalog(previously dropped on copy). -
Migration: replace every
x.setUnmodifiable()call withx = x.unmodifiable()(or use the returned snapshot directly), and audit any directnew BasicHttpResponse(...)/new BasicHttpEntity(...)construction to instantiate a concrete leaf instead. -
BasicResourcebecomes an abstract self-typed (CRTP) root.BasicResourceis nowBasicResource<SELF extends BasicResource<SELF>>(mirroringBasicHttpEntity/BasicHttpResponse) and can no longer be instantiated directly — use a concrete leaf (StreamResource,ByteArrayResource,StringResource,FileResource,ReaderResource). The breaking surface is narrow:.as(BasicResource.class)and@Remotemethods declared to returnBasicResource/HttpResourcestill work, becauseRestClientresponse bodies are now materialized into aStreamResource(which IS-ABasicResource);StreamResourcegained anHttpResponseconstructor for this. Only direct/reflectivenew BasicResource(...)instantiation breaks. -
Canonical
juneau-rest-commonHTTP stack brought to the immutable-beans paradigm.BasicHttpResponseis now an abstract self-typed (CRTP) rootBasicHttpResponse<SELF extends BasicHttpResponse<SELF>>;BasicHttpExceptionis a funnel-only root (no CRTP — a genericThrowableis illegal per JLS §8.1.2). The oldsetUnmodifiable()/assertModifiable()flag idiom is removed on these public types and on theHttpHeaderList/HttpPartListcollections — useunmodifiable()(a point-in-time snapshot) andisUnmodifiable(). Mutating a frozen instance now throwsUnsupportedOperationException(wasIllegalStateException).equals/hashCodeon the list twins are content-only. (Mirrors the classicjuneau-rest-common-classicmigration.)
-
-
jsonschemabean family converted to CRTP self-typing.JsonSchemaandJsonSchemaPropertyare now self-typed roots (JsonSchema<SELF extends JsonSchema<SELF>>,JsonSchemaProperty<SELF extends JsonSchemaProperty<SELF>>); the ~185 covariant setter-narrowing overrides onJsonSchemaProperty/JsonSchemaPropertySimpleArray/JsonSchemaRefwere removed. Fluent setters return the leaf type. Direct instantiation uses the diamond form (new JsonSchema<>()); marshalling is unchanged. Source-visible generification of these exported beans is a 10.0.0 change (raw declarations may warrant a<?>wildcard). -
atombean family converted to CRTP self-typing.CommonEntryis now a self-typed rootCommonEntry<SELF extends CommonEntry<SELF>>; the 42 covariant setter-narrowing overrides onFeed/Entry/Sourcewere removed (fluent setters return the leaf type). Marshalling is unchanged. Source-visible generification of this exported bean is a 10.0.0 change (raw declarations may warrant a<?>wildcard). -
html5bean family converted to CRTP self-typing.HtmlElementand its 5 intermediates (HtmlElementVoid/Text/RawText/Mixed/Container) are now abstract self-typed roots (HtmlElement<SELF extends HtmlElement<SELF>>, etc.); ~7,282 covariant setter-narrowing overrides were removed across the 108 leaf element beans, whose fluent setters now return the leaf type. Leaves stay concrete/non-generic, soHtmlBuilder.a()/new Div()and marshalling are unaffected. Caveat: external code that chains the generic accessorsgetAttr(Class,…)/getChild(Class,…)off a rawHtmlElement/intermediate must switch toHtmlElement<?>or a concrete leaf (raw subtyping now erases inherited generic members). -
SerializerWriterfamily converted to CRTP self-typing.SerializerWriteris now an abstract self-typed root (SerializerWriter<SELF extends SerializerWriter<SELF>> extends Writer); its fluent methods — including thejava.io.Writerappend(...)overloads — returnSELF, and ~330 covariant narrowing overrides were removed across the 13 format writers. The concrete-and-base writersJsonWriter/XmlWriter/HtmlWriterbecame abstract generic bases, each with a new concrete leaf (BasicJsonWriter/BasicXmlWriter/BasicHtmlWriter) used at instantiation sites;JcsWriter/SimpleHtmlWriterretain only their real overrides. Serializer output is byte-identical. Impact is internal tojuneau-marshall(no downstream module references these writer types); external code that declared a rawJsonWriter/XmlWriter/HtmlWritervariable and chained fluent calls should switch to the wildcard form (JsonWriter<?>) or the newBasic*leaf. -
Schema.exclusiveMaximum()/Schema.exclusiveMinimum()boolean annotation members removed. These@Deprecated(forRemoval)legacy Swagger 2.0/OpenAPI 3.0/JSON Schema Draft 04 boolean forms onorg.apache.juneau.commons.Schemaare gone. Migration: use the existing short-form boolean aliasesemax()/emin()instead (unchanged); for JSON Schema Draft 2020-12 numeric semantics,exclusiveMaximumValue()/exclusiveMinimumValue()(also unchanged) remain available and continue to take precedence when set. -
HttpPartSchema.T_OBJECT_CSV/T_OBJECT_PIPES/T_OBJECT_SSV/T_OBJECT_TSVconstants removed, along with their sole-purpose builder methodstObjectCsv()/tObjectPipes()/tObjectSsv()/tObjectTsv(). AcollectionFormaton anobject-type part is not a valid OpenAPI 2.0 concept (collectionFormatis defined only forarraytypes), and these presets had zero usage in the framework. The array-type equivalents (T_ARRAY_CSV,T_ARRAY_PIPES,T_ARRAY_SSV,T_ARRAY_TSV) are unaffected. Migration: none expected for normal usage; code relying on these invalid presets should build the schema viaHttpPartSchema.create().tObject()directly (with no collection format). -
XmlSerializer.DEFAULT_XS_NAMESPACEremoved. This unusedprotectednamespace constant (xs→http://www.w3.org/2001/XMLSchema) had no consumers anywhere in the framework.DEFAULT_JUNEAU_NAMESPACEis unaffected. Migration: none expected for normal usage; any external subclass that referenced the constant should declare its own namespace viaNamespace.of("xs", "http://www.w3.org/2001/XMLSchema"). -
@ParentPropertythrough collections/maps now injects the enclosing bean (behavioral change). Previously, when a bean annotated with@ParentPropertywas nested inside aList,Set, array, orMap— rather than being a direct bean property — the parser injected the immediately-containing container (e.g. theJsonList/JsonMap) into the@ParentPropertymember. As of 10.0.0, the parser instead injects the nearest enclosing bean, skipping all intermediate containers regardless of nesting depth; a bean parsed at the document root or directly inside a top-level collection now gets anullparent instead of throwing an exception. This makes the canonicalAddressBook/List<Person>example round-trip correctly, and makes the behavior consistent across all formats (JSON, XML, HTML, UON, URL-encoding, YAML, MsgPack, CBOR, BSON, HOCON, HJSON). Migration: if a collection/map-nested@ParentPropertymember was typed to accept the container (e.g. typed asList/JsonList), retype it to the enclosing bean type (e.g.AddressBook) — members already typed as the enclosing bean now work where they previously threwIllegalArgumentExceptionat parse time. Round-tripping a bean that holds a back-reference to its parent still requiresignoreRecursions()on the serializer. -
QueryableREST response converter rebuilt on the BeanQuery engine (juneau-rest-server). TheQueryableconverter — which parsed the Juneau-native&s=/&v=/&o=/&p=/&l=parameters and ranObjectSearcher/ObjectSorter/ObjectViewer/ObjectPaginator— now reads&search=/&view=/&sort=/&position=/&limit=/&opts=and runs the BeanQuery engine. The intermediateQueryProtocolSPI (QueryProtocol/QueryArgs/QueryResult/NativeQueryProtocol/ProtocolQueryable) from an earlier 10.0 milestone never shipped in a release and is removed;QueryableSettingsis now the BeanQuery tuning bean (noprotocol(...)). Migration: rename the request parameters and move searches to the BeanQuery grammar; see the V10 Migration Guide. Behavior changes to note: the default page size is now 100 (was "all rows") and is capped at 1000;$regexis on by default; the vestigial&i=(ignore-case) parameter is gone (case sensitivity is per operator);BeanQueryRequestbindsopts, notopt; theQueryMenuItemwidget form fields were renamed to match. -
RequestQueryParamListnative-arg accessors removed (juneau-rest-server).getSearchArgs()/getSortArgs()/getViewArgs()/getPageArgs()are removed, and so is theNativeQueryProtocol.INSTANCE.parse(req)helper that briefly replaced them. Migration: take aBeanQueryRequestmethod argument, or callreq.getRequest(BeanQueryRequest.class); its getters return the raw request strings/numbers. To get rows, open aBeanQuerySessionand callfind(query)/findValues(query). -
@Requestbean setters and constructor are verified when the operation table is built (juneau-rest-server). A concrete (non-interface)@Requestmethod-argument class with an annotated getter that has no matching public setter, or with no public no-arg constructor, used to fail withNoSuchMethodException(HTTP 500) on the first request that supplied that part. It now throwsIllegalArgumentException(for exampleRequest bean org.example.MyQuery has @Query getter getFoo() with no matching setter setFoo(String).) when the resource's operation table is assembled: at startup with@Rest(eagerInit=true), otherwise on the first request to that resource. Interface@Requestbeans are unaffected. Migration: add the missing setter / public no-arg constructor.
API Changes
-
Null-safe static
copyOrNull(T)convenience methods added to copyable bean/builder types. A newpublic static X copyOrNull(X value)method was added next to the existing instancecopy()on each type below. It returns a deep copy of the argument, ornullif the argument isnull, replacing thex == null ? null : x.copy()null-check-ternary boilerplate that appeared throughout the bean copy-constructors (all in-tree call sites were swept to use it). The name is deliberately distinct from the instancecopy(), and iscopyOrNull(rather thancopyOf) specifically to avoid shadowing the pervasively static-importedorg.apache.juneau.commons.utils.CollectionUtils.copyOf(...)collection/array overloads used inside many of these same types and across theContext.Buildersubclass hierarchy (a member namedcopyOfwould hide the wildcard static import). A future clean-break will move the copy helpers into a subclassableCopyUtilsand unify thesecopyOrNullmethods back under thecopyOfname via ordinary overload resolution. Types receivingcopyOrNull:juneau-bean-swagger-v2(org.apache.juneau.bean.swagger):Contact,License,Info,Items,SchemaInfo,Xml,ExternalDocumentation(7).juneau-bean-openapi-v3(org.apache.juneau.bean.openapi3):Contact,License,Info,Items,SchemaInfo,Xml,ExternalDocumentation,Server,Components,Discriminator,OAuthFlow,Operation,RequestBodyInfo(13). The last five were added by the shallow-copy correctness sweep (see behavioral changes below).juneau-microservice:LogConfig.juneau-marshallbuilder types:MarshallingContext.Builder(static MarshallingContext.Builder copyOrNull(MarshallingContext.Builder)) andContext.Builder(static Context.Builder<?> copyOrNull(Context.Builder<?> value), wildcard-typed because the builder uses a self-referentialSELFtype parameter that a static method cannot name).
-
New subclassable
CopyUtils;CollectionUtils.copyOfandObjectUtils.cloneOfmoved to it (clean break). ThecopyOf(...)family (all collection / map / array overloads) has been moved out oforg.apache.juneau.commons.utils.CollectionUtilsand the generic null-safecloneOf(T)has been moved out oforg.apache.juneau.commons.utils.ObjectUtilsinto a neworg.apache.juneau.commons.utils.CopyUtilsclass (with aprotectedconstructor so it can be subclassed). This is a hard move with no deprecation shim —CollectionUtilsno longer declares (or references)copyOf, andObjectUtilsno longer declarescloneOf. Two per-module subclasses add domain bean overloads that coexist with the inherited collection overloads via ordinary Java overload resolution, retiring the interimcopyOrNullmethods on those beans and unifying them back under thecopyOfname:juneau-bean-swagger-v2:org.apache.juneau.bean.swagger.SwaggerCopyUtils extends CopyUtils—copyOf(...)forContact,ExternalDocumentation,Info,Items,License,SchemaInfo,Xml(7).juneau-bean-openapi-v3:org.apache.juneau.bean.openapi3.OpenApiCopyUtils extends CopyUtils—copyOf(...)forComponents,Contact,Discriminator,ExternalDocumentation,Info,Items,License,OAuthFlow,Operation,RequestBodyInfo,SchemaInfo,Server,Xml(13).- The four single-site one-off types intentionally keep their
copyOrNull(T)and are unchanged:ThrownStats(juneau-rest-server),LogConfig(juneau-microservice), and theMarshallingContext.Builder/Context.Builder<?>marshall builders. - Migration: replace any
CollectionUtils.copyOf(...)withCopyUtils.copyOf(...)and anyObjectUtils.cloneOf(...)withCopyUtils.cloneOf(...). For files that resolvedcopyOf/cloneOfthrough aimport static ...CollectionUtils.*;/...ObjectUtils.*;wildcard, addimport static org.apache.juneau.commons.utils.CopyUtils.*;(keep the existing wildcard for the other helpers you still use). Swagger-v2 / openapi-v3 call sites that need a beancopyOf(<BeanType>)overload should static-import the module subclass (org.apache.juneau.bean.swagger.SwaggerCopyUtils/org.apache.juneau.bean.openapi3.OpenApiCopyUtils) instead, which supplies both the inherited collection overloads and the bean overloads.
-
RestRequest.isDebug()redefined as derived/read-only (juneau-rest-server). The method signature is unchanged, but as part of the REST debug/logging redesign (see Breaking Changes) it now returnstruewhen the resolved JUL logger for the current request isFINE-or-finer, computed on read rather than backed by a settable flag.RestRequest.setDebug()/setDebug(Boolean)and the correspondingRestResponsesetter are removed. -
One tone vocabulary:
StatusTone(juneau-console-widgets/juneau-console-views/juneau-console) (hard break, no shim). The duplicateToneenum is removed; every status/colour tone now usesStatusTone(INFO,SUCCESS,WARNING,ERROR,NEUTRAL), whose wire tokens areinfo/success/warning/error/neutral. Everything that spoke the older ok/warn/danger/accent/idle dialects was renamed to match:Badge.tonetakesStatusTone. ReplaceTone.WARNwithStatusTone.WARNING,Tone.DANGERwithStatusTone.ERRORandTone.ACCENTwithStatusTone.INFO;SUCCESSandNEUTRALare unchanged. The rendereddata-juneau-badge-toneattribute values change accordingly:accentbecomesinfo,warnbecomeswarning,dangerbecomeserror(successandneutralare unchanged). Update any custom CSS or JS selectors such as.jc-badge[data-juneau-badge-tone="danger"]to="error".- Java stat beans removed.
StatTile,StatBar,SegmentedBadge(and itsSegment),StatItemand the avatar-presenceStatusare gone fromorg.apache.juneau.console.widgets. The client-sidequickStatsenvelope, its JavaScript painter and the.jc-stat-*CSS are unchanged, so emit the same JSONquickStatstiles (for example from your own record type) instead of these beans. Stat-tile tones are now restricted to the fiveStatusTonewire tokens; any other tone string paints the tile with nois-tone class. - Probe status classes and tokens.
jc-probe-ok/-warn/-failbecomejc-probe-success/-warning/-error(jc-probe-neutralis unchanged), and the theme tokens--jc-probe-{ok,warn,fail}-{bg,text,dot}become--jc-probe-{success,warning,error}-{bg,text,dot}. Update any page script that maps a verdict to a probe class, and any stylesheet that overrides the tokens. - Helpers pill.
pill(label, tone)injuneau-helpers.jsnow takessuccess/warning/error/neutral(previouslyok/warn/error/idle), and its dot classjuneau-view-helper-pill-dot--<old>becomesjuneau-view-helper-pill-dot--<new>. - Progress bars. The state classes
is-ok/is-warn/is-exceedsbecomeis-success/is-warning/is-error, and the tokens--jc-progress-{ok,warn,exceeds}become--jc-progress-{success,warning,error}. - QuickStats tile colours now follow the badge tag palette (
--jc-tone-*aliases--jc-tag-*-text) instead of a grey ramp, so theme overrides of the--jc-tag-*tokens recolour stat tiles too. - New typed overload:
Render.pill(StatusTone)renders a status pill from aStatusTonedirectly, so callers need not pass a tone string.