Skip to content

Move DSL class loading under core/dsl, de-duplicate class definition, and name the classify branches - #13974

Merged
wu-sheng merged 1 commit into
masterfrom
fix/runtime-rule-explicit-engine-branch
Aug 11, 2026
Merged

Move DSL class loading under core/dsl, de-duplicate class definition, and name the classify branches#13974
wu-sheng merged 1 commit into
masterfrom
fix/runtime-rule-explicit-engine-branch

Conversation

@wu-sheng

@wu-sheng wu-sheng commented Aug 11, 2026

Copy link
Copy Markdown
Member

Move DSL class loading under core/dsl, de-duplicate class definition, and name the classify branches

Three related pieces of DSL-internals cleanup plus one changelog correction. No behaviour change.


1. core/classloadercore/dsl/classloader

Everything in that package was DSL-specific — two types say so in their names — and nothing outside
the DSL/rule world imported it. Catalog goes to core/dsl instead: a rule-file taxonomy is not a
class-loading concern, and it is the package's most-used type (21 references, mostly RuleKey
components in dsl-debugging).

Verified before moving: no FQCN string literals, no .ftl reference, no SPI file names anything
in this package, so the compiler catches every reference. ClassLoaderGc builds its unload-probe
resource path from UnloadProbePayload.class.getName(), not a hardcoded string, so it follows the
move automatically.

2. Three copies of "define this generated class" → one

MAL's and LAL's defineClass were char-identical except the package anchor; MeterSystem was a
near-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-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. Review established 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.

3. Dump-directory lookup: three of four copies

MAL, LAL and Hierarchy adopt DslGeneratedFileWriter.resolveClassDumpDir. OAL keeps its own
its openEngineDebug flag is settable independently of the environment variable, and
RuntimeOALGenerationTest and DSLClassGeneratorTest both call setOpenEngineDebug(true), which
the shared resolver cannot express without a second variant invented for one caller.

Equivalence checked: all three fields were private File classOutputDir; with no initialiser, so
the unconditional assignment of a possibly-null value matches the old conditional assignment exactly.

4. Name the classify branches in RuntimeRuleService

/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 (engineKindForMAL / LAL / NONE).

Scoped honestly: an unknown catalog cannot reach this branch. validate() rejects it earlier
with invalid_catalog, using the very same registry lookup (isValidCatalog is
forCatalog(catalog) != null). So NONE means a catalog whose engine is registered but has no
delta 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.isMalCatalog and isLalCatalog are deleted with it: the first had one caller, the
second none.

Tests

RuntimeRuleServiceEngineKindTest, 4 cases. The dispatch had no test because it lived inside a
private handler needing a DSLManager, storage and locks; extracting engineKindFor makes it
testable. Mutation-checked: restoring the old two-branch shape fails two cases with
expected: <NONE> but was: <LAL>.

A decision recorded, not implemented

DslSourceRef now 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-debugging also now declares its server-core dependency
directly instead of inheriting it transitively."
It does not. That pom change was proposed,
rejected on review as unnecessary — server-core is foundational and never leaves the dependency
tree — and reverted, but the sentence written before the revert was not removed with it.
dsl-debugging/pom.xml contains no server-core declaration. The sentence is removed rather than
the dependency added, because the review's conclusion stands.

@wu-sheng wu-sheng added the enhancement Enhancement on performance or codes label Aug 11, 2026
@wu-sheng wu-sheng added this to the 11.0.0 milestone Aug 11, 2026
@wu-sheng
wu-sheng force-pushed the fix/runtime-rule-explicit-engine-branch branch from ee51344 to 83fd1a0 Compare August 11, 2026 01:58
@wu-sheng wu-sheng changed the title Reject an unknown runtime-rule catalog instead of classifying it as LAL Move DSL class loading under core/dsl, de-duplicate class definition, and name the classify branches Aug 11, 2026
… 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
wu-sheng force-pushed the fix/runtime-rule-explicit-engine-branch branch from 7d7b98d to 6f397f4 Compare August 11, 2026 03:22
@wu-sheng
wu-sheng merged commit 75a07d8 into master Aug 11, 2026
455 of 457 checks passed
@wu-sheng
wu-sheng deleted the fix/runtime-rule-explicit-engine-branch branch August 11, 2026 05:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Enhancement on performance or codes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants