Skip to content

CAMEL-23757: Add Java DSL model writer for full-circle DSL transformation#24031

Draft
davsclaus wants to merge 14 commits into
mainfrom
feature/CAMEL-23757-java-dsl-model-writer
Draft

CAMEL-23757: Add Java DSL model writer for full-circle DSL transformation#24031
davsclaus wants to merge 14 commits into
mainfrom
feature/CAMEL-23757-java-dsl-model-writer

Conversation

@davsclaus

Copy link
Copy Markdown
Contributor

Summary

  • Add a new core/camel-java-io module that generates fluent Java DSL source code from Camel model definitions
  • Enables full-circle DSL transformation: XML/YAML → Model → Java DSL (and back via existing parsers)
  • Follows the same code-generation pattern as existing camel-xml-io and camel-yaml-io modules

Architecture

Layer XML YAML Java DSL (new)
Module core/camel-xml-io core/camel-yaml-io core/camel-java-io
Generated class ModelWriter YamlModelWriter JavaDslModelWriter
Support class BaseWriter YamlModelWriterSupport JavaDslModelWriterSupport
Package o.a.c.xml.out o.a.c.yaml.out o.a.c.java.out
Mojo XmlModelWriterGeneratorMojo YamlModelWriterGeneratorMojo JavaDslModelWriterGeneratorMojo
Template model-writer.vm model-yaml-writer.vm model-java-dsl-writer.vm

Example Output

from("direct:start")
    .routeId("myRoute")
    .choice()
        .when(simple("${header.type} == 'a'"))
            .to("mock:a")
        .when(simple("${header.type} == 'b'"))
            .to("mock:b")
        .otherwise()
            .to("mock:other")
    .end()
    .to("mock:result");

New Files

  • JavaDslModelWriterGeneratorMojo.java — Mojo extending ModelWriterGeneratorMojo for code generation
  • model-java-dsl-writer.vm — Velocity template producing the generated JavaDslModelWriter class
  • core/camel-java-io/ — New module with JavaDslModelWriterSupport (DSL rendering helpers) and generated JavaDslModelWriter
  • Test suite covering simple routes, choice/when/otherwise, filter, split, setHeader, setVariable, transform, wireTap, multicast, toD, removeHeader, multiple expression languages, and writer reusability

Test Plan

  • Unit tests for all major EIP patterns (12 tests)
  • Expected output files for exact string comparison
  • Writer reusability test (same instance, multiple routes)
  • Full root build passes (mvn clean install -DskipTests)

Claude Code on behalf of Claus Ibsen

🤖 Generated with Claude Code

…tion

Add a new camel-java-io module in core/ that generates fluent Java DSL
source code from Camel model definitions. This enables full-circle
transformation between XML, YAML, and Java DSL formats.

The implementation follows the same code-generation pattern as the
existing camel-xml-io and camel-yaml-io modules:
- JavaDslModelWriterGeneratorMojo extends ModelWriterGeneratorMojo
- Velocity template generates JavaDslModelWriter from model metadata
- JavaDslModelWriterSupport provides DSL rendering helpers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
@davsclaus davsclaus requested review from gnodet and oscerd June 15, 2026 13:16
@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 commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • catalog/camel-catalog
  • core
  • core/camel-api
  • core/camel-core-model
  • core/camel-java-io
  • core/camel-xml-io
  • tooling/maven/camel-package-maven-plugin
  • tooling/spi-annotations

ℹ️ 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: 2 test(s) disabled on GitHub Actions
Build reactor — dependencies compiled but only changed modules were tested (8 modules)
  • Camel :: API
  • Camel :: Catalog :: Camel Catalog
  • Camel :: Core Model
  • Camel :: Core Modules
  • Camel :: Java DSL IO
  • Camel :: Maven Plugins :: Camel Maven Package
  • Camel :: SPI Annotations
  • Camel :: XML IO

⚙️ View full build and test results

Reads XML route files from camel-xml-io test resources, converts
them to Java DSL via JavaDslModelWriter, wraps in a RouteBuilder
class, and compiles with javax.tools.JavaCompiler. 25 of 53 route
files compile successfully; 28 known failures are tracked for
constructs the writer doesn't yet handle (data formats, load
balancer subtypes, process refs, attribute chaining).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
@davsclaus davsclaus marked this pull request as draft June 15, 2026 14:22
davsclaus and others added 12 commits June 15, 2026 16:28
…test

Add 30 XML route definition files to camel-xml-io test resources covering
EIPs that were previously untested: aggregate, claimCheck, delay,
doTryCatchFinally, dynamicRouter, enrichAndPollEnrich, idempotentConsumer,
multicast, onCompletion, sample, sort, step, stop, threads, throttle,
toD, transacted, unmarshal, validate, wireTap, and more.

These files expand the compile validation test in camel-java-io from 44
to 83 parameterized tests, improving coverage of the Java DSL model writer
across the full EIP catalog.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
…let EIPs

Add XML test files for the three remaining untested EIPs:
- interceptFrom.xml in camel-xml-io (XML round-trip works)
- tokenizer.xml in camel-xml-io (langChain4j word tokenizer)
- kamelet.xml in camel-java-io (kept separate because the kamelet
  boolean attribute on RouteDefinition is a runtime transient that
  breaks XML round-trip in the ModelWriter)

Update JavaDslCompileTest to also scan its own test resources
directory. The kamelet EIP compiles successfully; interceptFrom
and tokenizer are added to known failures (they are RouteBuilder-
level methods, not chainable from the route).

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
…iscovery

Replace the hardcoded instanceof chain in beginStep() with Jandex-derived
metadata from ProcessorDefinition method signatures. The Mojo now discovers
at code-generation time which parameters each DSL method takes, matches them
to Definition class fields, and the Velocity template generates per-EIP
dispatch code. This fixes 8 previously failing compile tests (convertBody,
convertHeaderTo, convertVariableTo, rollback, barPolicyRoute,
enrichAndPollEnrich, routeWithCircuitBreaker, removeHeadersAndProperties).

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
…perties, and imports

- Use language("name", "expr") for expression languages without RouteBuilder
  helpers (groovy, ognl, juel, mvel, spel, python, hl7terser, wasm)
- Map process(String) to ref field so process("myRef") is generated
- Handle route properties as .routeProperty("key", "value") in writeRoute()
- Add ExchangePattern and Builder.language imports to compile test wrapper
- Add handledAttributes check to doWriteChildList to prevent duplicates

10 more compile tests now pass (62 total, 24 remaining).

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
Detect DataFormatDefinition instances in doWriteChildElement and emit
the data format method name (e.g. .json(), .csv(), .hl7()) on
DataFormatClause. Data formats without no-arg methods (bindy, flatpack,
xmlSecurity) remain as known failures.

7 more compile tests now pass (69 total, 17 remaining).

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
…rmDataType

- Generalize findRicherOverload to find multi-param overloads (log+LoggingLevel, poll+timeout, transformDataType+fromType)
- Add LoggingLevel enum mapping to mapSingleParam
- Add long param mapping for poll timeout
- Prefer correlationExpression fallback for aggregate over Jandex expression
- Use _first boolean flag in template for proper multi-arg comma separation
- Add long renderType to Velocity template
- Reject richer overloads with duplicate field names

Score: 73/86 compile tests passing (85%)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <davsclaus@apache.org>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
…sts and ExpressionSubElementDefinition

- Add classList renderType for exception class arguments in onException/doCatch
- Handle ExpressionSubElementDefinition in doWriteChildElement (handled, retryWhile, correlationExpression)
- Add Predicate cast for handled/continued/retryWhile to resolve ambiguity with ValueBuilder
- Fix classLiteral to use FQN for non-java.lang classes
- Fix doWriteStringList to respect handledAttributes

Score: 75/86 (87%), 11 known failures remaining.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
…va DSL writer

Add @DslArg field-level annotation to mark Definition class fields as
primary arguments for Java DSL method calls. This replaces ~250 lines
of Jandex-based method signature introspection with ~30 lines of
annotation-based reflection. Annotate ~30 model Definition classes.

Also supports type-level @DslArg(exclude = ...) to suppress inherited
annotations for subclasses where the parent's primary arg doesn't apply
(e.g., EnrichDefinition extends ExpressionNode but enrich() takes a
URI string, not an expression).

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
…terns (80/86)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
…ull option support

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
Hybrid approach: compact form (e.g., simple("${body}")) when no options,
builder pattern (e.g., expression().simple("${body}").resultTypeName("String").end())
when expression-specific options like resultType, saxon, suppressExceptions are set.
Uses LanguageBuilderFactory methods - no imports needed, all options available.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
… (86/87)

Handle interceptFrom, intercept, interceptSendToEndpoint as separate
RouteBuilder-level statements. Support inlined error handler with
deadLetterChannel/defaultErrorHandler and typed redelivery policy
options. Handle batch resequencer config with .batch().size().timeout().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant