Skip to content

CAMEL-22894: Refactor SimpleFunctionExpression: introduce function-factory dispatcher (part 1)#23263

Merged
davsclaus merged 5 commits into
apache:mainfrom
ammachado:CAMEL-22894
May 20, 2026
Merged

CAMEL-22894: Refactor SimpleFunctionExpression: introduce function-factory dispatcher (part 1)#23263
davsclaus merged 5 commits into
apache:mainfrom
ammachado:CAMEL-22894

Conversation

@ammachado
Copy link
Copy Markdown
Contributor

@ammachado ammachado commented May 17, 2026

Description

First incremental step toward CAMEL-22894: break up the 4,500-line SimpleFunctionExpression that today carries both runtime Simple language and compile-time CSimple code-generation responsibilities, with no behavior change.

This PR establishes the pattern and ports the first batch of small built-in families. Larger families (header, exchangeProperty, the rest) will follow in subsequent PRs once the dispatcher pattern lands.

Three commits, each independently reviewable:

  1. fc43e0d — Deprecate SimpleLanguageFunctionFactory#createCode. The createCode(...) method on the public SimpleLanguageFunctionFactory SPI (@since 4.10) exists solely to support csimple, which is already @Deprecated(since = \"4.19\") and slated for removal in 5.0. Marked the method @Deprecated(since = \"4.21\") and made it a default returning null, so implementations that don't care about csimple no longer have to override it. The three in-tree implementations (camel-attachments, camel-base64, camel-jsoup) keep their existing overrides and pick up the deprecation marker via inheritance.

  2. 4beafc9 — Centralize external-component function dispatch in SimpleFunctionDispatcher. SimpleFunctionExpression previously had six near-identical methods plus six gating predicate blocks dedicated to dispatching to the three external SimpleLanguageFunctionFactory implementations (camel-attachments, camel-base64, camel-jsoup). Moved the dispatch into a new internal SimpleFunctionDispatcher that owns the list of known component factories and their gating predicates, exposing tryCreate(...) / tryCreateCode(...) entry points. Behaviour preserved exactly: same factories, same order, same gating (so a missing component still surfaces the helpful "add this JAR" error via ResolverHelper.resolveMandatoryBootstrapService), and camel-jsoup remains excluded from the csimple createCode path because its createCode throws UnsupportedOperationException.

  3. 63d326e — Extract random, skip, collate, join into SimpleLanguageFunctionFactory classes. Moved four self-contained Simple functions out of SimpleFunctionExpression into their own factory implementations under org.apache.camel.language.simple.functions, registered as built-ins on SimpleFunctionDispatcher. The dispatcher consults built-ins before external component factories, preserving today's priority. Both createFunction (Simple) and createCode (csimple) live side by side in each class, instead of being ~2,000 lines apart in SimpleFunctionExpression.

Net structural impact so far:

  • SimpleFunctionExpression.java: 4,526 → ~4,255 lines (−270 LOC), with the new dispatcher (~150 LOC) and four small factory classes (~300 LOC combined).
  • Adding a new external-component factory is now a one-line edit in SimpleFunctionDispatcher instead of a six-block change spread across SimpleFunctionExpression.
  • Built-in extraction template established for follow-up PRs.

Out of scope for this PR (and the broader refactor, see plan locked in JIRA discussion):

  • No public-API additions (no new SPI, no addFunctionFactory(...) on SimpleFunctionRegistry).
  • No CSimple-side restructure: no visitor pattern, no type-safe code-generator, no parameterized cross-tests. The current generated csimple Java source is identical before/after.
  • No classloader auto-discovery of factories — would not work in Quarkus native; current explicit allowlist is the right shape.

Verification:

  • mvnd clean install -Dquickly -Dci.env.name=local from repo root: ✅ BUILD SUCCESS.
  • SimpleTest (227), CSimpleTest (1), SimpleCustomFunctionTest (5), CSimplePredicateParserTest (4), CSimpleExpressionParserTest (13), SimpleExpressionParserNodesTest (2), SimplePredicateParserNodesTest (3): all green.
  • camel-attachments SimpleAttachmentTest (10), camel-base64 SimpleBase64Test (2), full camel-jsoup test suite (3): all green.
  • mvn formatter:format impsort:sort clean.

Review follow-up after the initial proof-of-concept commits:

The first three commits were intentionally scoped to prove the extraction/dispatcher pattern and get reviewer feedback on the shape before applying it more broadly. Based on that review, this branch now also:

  • restores the original built-in evaluation order
  • extracts codeSplitSafe(...) into SimpleFunctionHelper
  • fixes the pre-existing skip(...) runtime/code inconsistency
  • moves the touched function coverage into dedicated factory unit tests

Target

  • I checked that the commit is targeting the correct branch (Camel 4 uses the main branch)

Tracking

  • If this is a large change, bug fix, or code improvement, I checked there is a JIRA issue filed for the change (usually before you start working on it).

Apache Camel coding standards and style

  • I checked that each commit in the pull request has a meaningful subject line and body.
  • I have run mvn clean install -DskipTests locally from root folder and I have committed all auto-generated changes.

Claude Code on behalf of Adriano Machado

ammachado added 3 commits May 16, 2026 21:44
The createCode(...) method exists solely to support csimple, which is
deprecated since 4.19 and slated for removal in 5.0. Mark the method
@deprecated(since = "4.21") and make it a default returning null so
implementations that no longer care about csimple don't need to
override it. The three in-tree implementations (camel-attachments,
camel-base64, camel-jsoup) keep their existing overrides and pick up
the deprecation marker via inheritance.

This is the first step toward refactoring the dual-responsibility
SimpleFunctionExpression class.

rh-pre-commit.version: 2.3.2
rh-pre-commit.check-secrets: ENABLED
…eFunctionDispatcher

SimpleFunctionExpression had six near-identical methods plus six gating
predicate blocks dedicated to dispatching function strings to the three
SimpleLanguageFunctionFactory implementations shipped by external
components (camel-attachments, camel-base64, camel-jsoup). Move the
dispatch into a new SimpleFunctionDispatcher, which owns the list of
known component factories and their gating predicates and exposes
tryCreate(...) / tryCreateCode(...) entry points.

Behavior preserved exactly:
- Same factories consulted in the same order.
- Same gating predicates (so a function string belonging to a missing
  component still surfaces the helpful "add this JAR" error via
  ResolverHelper.resolveMandatoryBootstrapService).
- camel-jsoup remains excluded from the csimple createCode dispatch
  (it has no csimple support and would throw).

Net: SimpleFunctionExpression shrinks by ~80 lines and adding a fourth
external factory is now a one-line edit in SimpleFunctionDispatcher
instead of a six-block change spread across SimpleFunctionExpression.

rh-pre-commit.version: 2.3.2
rh-pre-commit.check-secrets: ENABLED
…unctionFactory classes

Move four self-contained Simple functions out of SimpleFunctionExpression
into their own SimpleLanguageFunctionFactory implementations under
org.apache.camel.language.simple.functions, registered as built-ins on
SimpleFunctionDispatcher. The dispatcher consults built-ins before
external component factories, preserving today's priority.

Both createFunction (Simple) and createCode (csimple) are moved together
into each class, keeping the runtime and code-generation logic for the
same function side by side instead of ~2,000 lines apart in
SimpleFunctionExpression.

Behavior preserved: each factory short-circuits on its function prefix
and returns null on non-match, exactly mirroring the inline blocks they
replace. All Simple and CSimple tests continue to pass.

SimpleFunctionExpression shrinks by ~147 lines.

rh-pre-commit.version: 2.3.2
rh-pre-commit.check-secrets: ENABLED
@ammachado ammachado marked this pull request as ready for review May 17, 2026 02:19
@ammachado ammachado changed the title CAMEL-22894: Refactor SimpleFunctionExpression — introduce function-factory dispatcher (part 1) CAMEL-22894: Refactor SimpleFunctionExpression: introduce function-factory dispatcher (part 1) May 17, 2026
@Croway Croway requested a review from gnodet May 18, 2026 09:27
@github-actions
Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@github-actions github-actions Bot added the core label May 18, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 18, 2026

🧪 CI tested the following changed modules:

  • core/camel-api
  • core/camel-core-languages
  • core/camel-core

ℹ️ Dependent modules were not tested because the total number of affected modules exceeded the threshold (50). Use the test-dependents label to force testing all dependents.

⚠️ Some tests are disabled on GitHub Actions (@DisabledIfSystemProperty(named = "ci.env.name")) and require manual verification:

  • core/camel-core: 2 test(s) disabled on GitHub Actions
Build reactor — dependencies compiled but only changed modules were tested (3 modules)
  • Camel :: API
  • Camel :: Core
  • Camel :: Core Languages

⚙️ View full build and test results

@davsclaus
Copy link
Copy Markdown
Contributor

We are not keeping csimple until camel 5 in so many years. It will be removed sooner. Its not really in use by end users. And users who use it can easily use regular simple

Copy link
Copy Markdown
Contributor

@davsclaus davsclaus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — CAMEL-22894: Refactor SimpleFunctionExpression: introduce function-factory dispatcher (part 1)

Well-structured refactoring PR with clear scope boundaries. The three-commit structure is easy to follow, and the PR description is exemplary. CI is green and no blocking issues found.

Observations

1. (Low) Evaluation order change for random, skip, collate, join

In the original code these four functions are evaluated inside createSimpleExpressionMisc() which runs before createSimpleExpressionMath(). After this PR they are evaluated via the dispatcher after math. Since these function names (random(, skip(, collate(, join() have no overlap with math function patterns, this is harmless in practice. But the PR description states "Behaviour preserved exactly" — this is a safe ordering change rather than exact preservation, and worth acknowledging.

2. (Low) JoinFunctionFactory couples back to SimpleFunctionExpression.codeSplitSafe()

For the follow-up PRs, codeSplitSafe should move to a shared utility (e.g. SimpleParserHelper) as more factories are extracted. Not blocking for this PR.

3. (Low) No dedicated unit tests for individual factory classes

The existing SimpleTest (227 tests), CSimpleTest, etc. cover behavioral correctness. As more factories are extracted in follow-up PRs, per-factory unit tests would help catch regressions faster.

Verified Clean

  • SkipFunctionFactory correctly uses StringHelper.before() in createFunction and StringHelper.beforeLast() in createCode, matching the original.
  • The @Deprecated default createCode returning null is safe — existing overrides in camel-attachments/base64/jsoup continue working.
  • @Deprecated(since = "4.21") without forRemoval is consistent with existing csimple deprecation convention.
  • CI green, formatting clean.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Copy link
Copy Markdown
Contributor

@gnodet gnodet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code on behalf of Guillaume Nodet

Good first step toward decomposing the 4,500-line SimpleFunctionExpression. The commit structure is clean — deprecation, dispatcher, extraction are each independently reviewable. Two issues to address before merge (see inline comments), plus a few suggestions.

Summary of findings:

  1. (blocking) SkipFunctionFactory: before vs beforeLast inconsistencycreateFunction uses StringHelper.before() while createCode uses StringHelper.beforeLast(). This is a pre-existing bug faithfully ported from the original code, but since we're touching this code now, we should fix it.

  2. (blocking) Ordering change for extracted built-insrandom, skip, collate, join previously evaluated in createSimpleExpressionMisc() (before createSimpleExpressionMath()). Now they evaluate via the dispatcher (after math). Safe in practice since these prefixes don't collide with math patterns, but the PR description claims "behaviour preserved exactly" — this should be acknowledged or addressed.

  3. (major) Circular dependencyJoinFunctionFactory calls SimpleFunctionExpression.codeSplitSafe(...), creating a dependency from the extracted factory back into the monolith being decomposed.

  4. (minor) Javadoc/comment mismatches — The dispatcher Javadoc and inline comments don't reflect that it now handles both built-in and external functions.

@ammachado
Copy link
Copy Markdown
Contributor Author

The first three commits were intentionally scoped to prove the extraction/dispatcher pattern and get reviewer feedback on the shape before applying it more broadly. Based on that review, this branch now also:

  • restores the original built-in evaluation order
  • extracts codeSplitSafe(...) into SimpleFunctionHelper
  • fixes the pre-existing skip(...) runtime/code inconsistency
  • moves the touched function coverage into dedicated factory unit tests

ammachado added 2 commits May 19, 2026 14:33
- restore built-in function dispatch ordering relative to misc/math parsing
- move codeSplitSafe into SimpleFunctionHelper to decouple JoinFunctionFactory from SimpleFunctionExpression
- align SkipFunctionFactory runtime parsing with createCode using beforeLast
- move random/skip/collate/join coverage into dedicated factory unit tests

rh-pre-commit.version: 2.3.2
rh-pre-commit.check-secrets: ENABLED
`AbstractSimpleFunctionFactoryTestSupport.evaluate()` previously called
the factory directly, bypassing `SimpleFunctionExpression` and the
dispatcher entirely. The four factory tests therefore did not exercise
the dispatch ordering (built-ins before misc/math) that the review
flagged.

Route `evaluate()` through `context.resolveLanguage("simple")` so each
factory test exercises the full tokenizer -> parser -> dispatcher ->
factory chain. `createCode()` continues to call the factory directly
since there is no equivalent full pipeline for csimple.

The two error-message assertions in `RandomFunctionFactoryTest` are
updated to check `getCause().getMessage()`: the full pipeline wraps
the factory's `SimpleParserException` inside a
`SimpleIllegalSyntaxException` that appends location context.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

rh-pre-commit.version: 2.3.2
rh-pre-commit.check-secrets: ENABLED
Copy link
Copy Markdown
Contributor

@gnodet gnodet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All review feedback has been addressed:

  • SkipFunctionFactory before/beforeLast inconsistency fixed
  • Evaluation ordering restored (built-ins dispatched before misc/math)
  • Circular dependency removed (codeSplitSafe moved to SimpleFunctionHelper)
  • Javadoc and comments updated to reflect both built-in and external dispatch
  • record Entry replaced with conventional static final class
  • Dedicated per-factory unit tests added, routed through the full language pipeline

Claude Code on behalf of Guillaume Nodet

@davsclaus davsclaus merged commit d3a492c into apache:main May 20, 2026
6 checks passed
@davsclaus
Copy link
Copy Markdown
Contributor

thanks part 2 is welcome

@ammachado ammachado deleted the CAMEL-22894 branch May 20, 2026 13:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants