Consolidate the hardening internals per JAXP type - #28
Open
ppkarwasz wants to merge 6 commits into
Open
Conversation
Add two static helpers to HardeningException so every hardener and every resolver floor shares one message format: - settingFailed(kind, name, target, cause) replaces JaxpSetters' inline "Failed to set ..." construction. - forbidden(...) replaces the private Resolvers.forbiddenMessage, so the five floors no longer route their message through the outer Resolvers class. Behavior-preserving: the messages are byte-identical. This is the shared core for the per-hardener shade-footprint reduction that follows. Add ShadingFootprintTest, which uses jdependency (the library maven-shade minimizeJar uses) to pin each hardener entry point's transitive class closure and print its size as a share of the full library, so later phases can show the footprint shrinking. Assisted-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the setFeature/setOptionalFeature/trySetProperty/trySetAttribute/ setOptionalAttribute helpers out of the shared JaxpSetters class and into the hardener that uses them, as private methods, so shading one hardener no longer drags in the setters (and dead overloads) of the others. Only the shared "Failed to set ..." message stays central, on HardeningException.settingFailed. Delete JaxpSetters. Cuts the DOM/SAX/StAX shade closures by the whole JaxpSetters class (7946 bytes): DocumentBuilderHardener 26225 -> 19265, SAXParserHardener 32508 -> 25721, StaxHardener 28173 -> 20547 bytes. Update ShadingFootprintTest's expected sets accordingly. Assisted-By: Claude Opus 4.8 <noreply@anthropic.com>
Promote the five nested Resolvers.Fallback* floors to top-level, package-private classes and delete the Resolvers container. The floors were bound together only by the shared forbiddenMessage, now on HardeningException (Phase 1), and by being nested in one class, which maven-shade minimizeJar keeps together. Splitting them means shading one hardener pulls only the floor(s) it uses: - DocumentBuilderHardener: 10 -> 5 classes, 19265 -> 12161 bytes - SAXParserHardener: 13 -> 8 classes, 25721 -> 18242 bytes - StaxHardener: 10 -> 6 classes, 20547 -> 13433 bytes The EntityResolver2 floor is named FallbackDenyEntityResolver2; it also carries the shared "floor" overview the container used to hold. Update ShadingFootprintTest's expected sets and exclude that JVM-only test (jdependency) from the Android test compile. Assisted-By: Claude Opus 4.8 <noreply@anthropic.com>
The TrAX, schema and Saxon wrappers re-harden their sub-parsers by calling XmlFactories.harden(Source/XMLReader); because those helpers live on XmlFactories, whose class also news up every hardener, referencing them dragged the whole library into each of those shade closures. Move harden(Source)'s body to package-private SAXParserHardener. hardenSource (it needs only the SAX path) and repoint the internal callers: the four Source callers to hardenSource, SaxonProvider to SAXParserHardener.hardenReader. The public XmlFactories.harden(Source)/ harden(XMLReader) stay as thin delegates, so the API is unchanged, but no hardener transitively reaches XmlFactories anymore. Each heavy entry point now pulls only its own wrappers, its floor and the shared SAX path: - TransformerHardener: 32 -> 17 classes, 75204 -> 37366 bytes - XPathHardener: 32 -> 13 classes, 75204 -> 26471 bytes - HardeningSchemaFactory: 32 -> 13 classes, 75204 -> 32763 bytes Pin the three closures in ShadingFootprintTest and assert only the public XmlFactories entry still pulls all 32 classes. Assisted-By: Claude Opus 4.8 <noreply@anthropic.com>
Give schema validation the same shape as the other JAXP types: a dedicated hardener class that XmlFactories delegates to, instead of newSchemaFactory wrapping HardeningSchemaFactory directly. The class stays package-private like the other hardeners; it will back a public entry point if the hardeners are exposed later. Pin SchemaHardener as the schema entry point in ShadingFootprintTest. Assisted-By: Claude Fable 5 <noreply@anthropic.com>
The test reads the compiled .class files from the code-source location, which only exists on a regular JVM: a native image carries no bytecode (and nobody shades one), so every closure lookup fails there. Disable it with @DisabledInNativeImage, matching its exclusion from the Android test compile. Assisted-By: Claude Fable 5 <noreply@anthropic.com>
garydgregory
force-pushed
the
feature/reduce-shade-footprint
branch
from
August 2, 2026 19:16
cd67911 to
0fcd0a1
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Relationship to COMMONSXML-3
This PR is adjacent to COMMONSXML-3 (Reduce shading footprint), but deliberately stops short of it: it does not add alternative public entry points. It is too soon to commit to a per-hardener API, so
XmlFactoriesremains the only public class and its API is unchanged.What it does instead is consolidate methods by moving each shared helper to its only caller, so that the internals no longer form one tangle in which every class transitively reaches every other. Each JAXP type (DOM, SAX, StAX, TrAX, XPath, schema) now has a self-contained hardener whose transitive class closure contains only what that type actually uses. If COMMONSXML-3 is taken up later, exposing an entry point becomes a visibility change instead of a refactoring.
Changes
One commit per step, each behavior-preserving:
HardeningException. Two static helpers,settingFailed(...)andforbidden(...), replace the message construction that previously coupled the setters and the resolver floors to their container classes. Messages are byte-identical.setFeature/setOptionalFeature/trySetPropertyhelpers move out of the sharedJaxpSettersclass into the hardener that uses them, as private methods;JaxpSettersis deleted. Shading one hardener no longer drags in the setter overloads of the others.Resolvers.Fallback*floors become top-level, package-private classes (FallbackDenyEntityResolver2,FallbackDenyXMLResolver,FallbackIgnoreXMLResolver,FallbackDenyURIResolver,FallbackDenyLSResourceResolver) and theResolverscontainer is deleted. Nested classes are kept together byminimizeJar; top-level classes are pulled in per floor.XmlFactoriescycle for TrAX, XPath and schema. The wrappers re-hardened their sub-parsers by calling back intoXmlFactories.harden(Source/XMLReader), and sinceXmlFactoriesnews up every hardener, that one call dragged the whole library into their closures. The bodies move to package-privateSAXParserHardener.hardenSource/hardenReader; the publicXmlFactories.hardenmethods stay as thin delegates.SchemaHardeneras the schema entry point. Package-private like the other hardeners; it gives schema validation the same shape as the other types (XmlFactoriesdelegates to a dedicated hardener) and would back a public entry point later.Effect on the shade closures
Before this PR the TrAX, XPath and schema entry points transitively reached
XmlFactoriesand therefore pulled the whole library (32 classes each), while the DOM, SAX and StAX hardeners pulled the shared setter class and all five resolver floors whether they used them or not. After:DocumentBuilderHardenerStaxHardenerSAXParserHardenerXPathHardenerSchemaHardenerTransformerHardenerXmlFactories(public)TrAX, XPath and schema build on the shared SAX closure (8 classes), since they re-harden their sub-parsers through it.
The new
ShadingFootprintTestpins each closure with jdependency (the librarymaven-shade-plugin'sminimizeJaruses), so a hardener silently regaining a dependency on a sibling floor or another hardener fails the build. The test is JVM-only and excluded from the Android test compile.Testing
Full surefire matrix (stock JDK, Xerces, Xalan, Xalan+Xerces, Saxon, Saxon+Xerces, Woodstox): all green. No public API change, no behavior change; exception messages are byte-identical.
🤖 Generated with Claude Code