Move DSL class loading under core/dsl, de-duplicate class definition, and name the classify branches - #13974
Merged
Conversation
wu-sheng
force-pushed
the
fix/runtime-rule-explicit-engine-branch
branch
from
August 11, 2026 01:58
ee51344 to
83fd1a0
Compare
… and name the classify branches Three pieces of DSL-internals cleanup. No behaviour change. core/classloader held only DSL types, two of which say so in their names, and nothing outside the DSL and rule world imported it. It is now core/dsl/classloader. Catalog moves to core/dsl instead: a rule-file taxonomy is not a class-loading concern, and it is the package's most-used type, mostly as a RuleKey component in dsl-debugging. Nothing names this package in a string literal, a FreeMarker template or an SPI file, so the compiler catches every reference; ClassLoaderGc builds its unload-probe resource path from UnloadProbePayload.class.getName() rather than a constant, so it follows the move. Three copies of "define this generated class into the right loader" -- MAL's and LAL's generators and MeterSystem -- collapse into a static BytecodeClassDefiner.define. It lives on the interface rather than in a class of its own because the whole decision is whether the loader implements that interface; a second similarly-named type in the same package would be one concept with two names. The JDK 17 --add-opens rationale, previously restated in four places, now has one home and the other three point at it. MeterSystem's multi-catch drops | IOException, which define() wraps into CannotCompileException -- already an arm of the same catch, so the rollback and UnexpectedException wrap are unchanged. That arm was unreachable regardless: CtClass.toBytecode() writes to a ByteArrayOutputStream whose close() is a documented no-op, so the checked exception is a declaration artefact. Three of the four copies of the SW_DYNAMIC_CLASS_ENGINE_DEBUG dump-directory lookup become DslGeneratedFileWriter.resolveClassDumpDir. OAL keeps its own: its openEngineDebug flag is settable independently of the environment variable and two tests call setOpenEngineDebug(true), which the shared resolver cannot express without a second variant invented for one caller. /addOrUpdate chose its delta classifier with a MAL/LAL ternary, written when those were the only two engines, so its else meant LAL and would hand a third engine's rule to the LAL classifier. The decision is now three-valued and named (engineKindFor). An unknown catalog cannot reach it -- validate() rejects that earlier with invalid_catalog, using the same registry lookup -- so the third case means a registered engine with no delta classifier, which should fail loudly rather than parse as LAL. DSLScriptKey.isMalCatalog and isLalCatalog go with it: one caller between them. RuntimeRuleServiceEngineKindTest covers the dispatch, which had no test because it lived inside a private handler needing a DSLManager, storage and locks. DslSourceRef records a decision not to add a shared producer interface for rule coordinates. Two of the four DSLs could not implement one -- OAL's dispatcher deliberately wants a lineless ref, and Hierarchy has no per-rule object -- so the narrow entrance, a file and a line, stays the contract and each DSL keeps what it knows about its own rules. Also drops a changelog sentence merged with #13973 claiming dsl-debugging declares its server-core dependency directly. It does not; that pom change was proposed, rejected on review and reverted, but the sentence was not removed with it.
wu-sheng
force-pushed
the
fix/runtime-rule-explicit-engine-branch
branch
from
August 11, 2026 03:22
7d7b98d to
6f397f4
Compare
mrproliu
approved these changes
Aug 11, 2026
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.
Move DSL class loading under
core/dsl, de-duplicate class definition, and name the classify branchesThree related pieces of DSL-internals cleanup plus one changelog correction. No behaviour change.
1.
core/classloader→core/dsl/classloaderEverything in that package was DSL-specific — two types say so in their names — and nothing outside
the DSL/rule world imported it.
Cataloggoes tocore/dslinstead: a rule-file taxonomy is not aclass-loading concern, and it is the package's most-used type (21 references, mostly
RuleKeycomponents in dsl-debugging).
Verified before moving: no FQCN string literals, no
.ftlreference, no SPI file names anythingin this package, so the compiler catches every reference.
ClassLoaderGcbuilds its unload-proberesource path from
UnloadProbePayload.class.getName(), not a hardcoded string, so it follows themove automatically.
2. Three copies of "define this generated class" → one
MAL's and LAL's
defineClasswere char-identical except the package anchor;MeterSystemwas anear-twin. All three now call a static
BytecodeClassDefiner.define(ctClass, loader, anchor).It lives on the interface, not in a class of its own, because the whole decision is whether the
loader implements that interface — a second similarly-named type in the same package would be one
concept with two names. The JDK 17
--add-opensrationale, previously restated in four places, nowhas one home and the other three point at it.
MeterSystem's multi-catch drops| IOException, whichdefine()wraps intoCannotCompileException— already an arm of the same catch, so the rollback andUnexpectedExceptionwrap are unchanged. Review established that arm was unreachable regardless:
CtClass.toBytecode()writes to a
ByteArrayOutputStreamwhoseclose()is a documented no-op, so the checked exceptionis a declaration artefact.
3. Dump-directory lookup: three of four copies
MAL, LAL and Hierarchy adopt
DslGeneratedFileWriter.resolveClassDumpDir. OAL keeps its own —its
openEngineDebugflag is settable independently of the environment variable, andRuntimeOALGenerationTestandDSLClassGeneratorTestboth callsetOpenEngineDebug(true), whichthe shared resolver cannot express without a second variant invented for one caller.
Equivalence checked: all three fields were
private File classOutputDir;with no initialiser, sothe unconditional assignment of a possibly-null value matches the old conditional assignment exactly.
4. Name the classify branches in
RuntimeRuleService/addOrUpdatechose its delta classifier with a MAL/LAL ternary, written when those were the onlytwo engines, so its
elsemeant LAL — and would hand a third engine's rule to the LALclassifier. The decision is now three-valued and named (
engineKindFor→MAL/LAL/NONE).Scoped honestly: an unknown catalog cannot reach this branch.
validate()rejects it earlierwith
invalid_catalog, using the very same registry lookup (isValidCatalogisforCatalog(catalog) != null). SoNONEmeans a catalog whose engine is registered but has nodelta classifier — impossible today, and something that should fail loudly rather than silently
parse as LAL once a third engine exists. It returns
unsupported_engine.An earlier revision of this PR advertised this as a user-facing unknown-catalog fix and carried a
changelog entry saying so. That was wrong — the path is guarded upstream — and the entry is removed.
DSLScriptKey.isMalCatalogandisLalCatalogare deleted with it: the first had one caller, thesecond none.
Tests
RuntimeRuleServiceEngineKindTest, 4 cases. The dispatch had no test because it lived inside aprivate handler needing a
DSLManager, storage and locks; extractingengineKindFormakes ittestable. Mutation-checked: restoring the old two-branch shape fails two cases with
expected: <NONE> but was: <LAL>.A decision recorded, not implemented
DslSourceRefnow documents why there is no shared producer interface for rule coordinates.Two of the four DSLs could not implement one — OAL's dispatcher deliberately wants a lineless ref,
and Hierarchy has no per-rule object — so the narrow entrance, a file and a line, stays the contract
and each DSL keeps what it knows about its own rules.
Also in this PR: one merged changelog line corrected
Unrelated to the fix above, and folded in only because it is a single sentence in a file this PR
already touches — split it out if you would rather.
The #13973 changelog entry says "
dsl-debuggingalso now declares itsserver-coredependencydirectly instead of inheriting it transitively." It does not. That pom change was proposed,
rejected on review as unnecessary —
server-coreis foundational and never leaves the dependencytree — and reverted, but the sentence written before the revert was not removed with it.
dsl-debugging/pom.xmlcontains noserver-coredeclaration. The sentence is removed rather thanthe dependency added, because the review's conclusion stands.