Accepted
The cdk-common library needs a templating engine to process YAML configuration files with dynamic values from CDK context. The engine must support variable substitution, be lightweight, and integrate well with Java ecosystems.
We will use Mustache (mustache.java library) for template processing.
Key Implementation:
// Template.java:98-100
factory.compile(new InputStreamReader(stream, StandardCharsets.UTF_8), template)
.execute(writer, values)
.flush();Template Syntax:
name: {{deployment:id}}-vpc-cni
account: {{deployment:account}}
tags:
"{{deployment:domain}}:component": {{deployment:id}}-component- Principle: Separation of logic and presentation
- Benefit: Templates remain readable and maintainable
- Example: No complex conditionals cluttering YAML structure
- Variables:
{{variable}} - Sections:
{{#section}}...{{/section}} - Comments:
{{! comment }} - Minimal learning curve for teams
- Library:
com.github.spullara.mustache.java:compiler - Performance: Compiled templates with good performance
- Stability: Well-established library with long track record
- No Syntax Conflicts: Mustache syntax doesn't interfere with YAML
- Preserves Structure: YAML remains valid after processing
- Tool Support: YAML editors/validators work with templates
// 1. Load template from classpath
var stream = Template.class.getClassLoader().getResourceAsStream(template);
// 2. Create Mustache factory and compile template
var factory = new DefaultMustacheFactory();
var mustache = factory.compile(new InputStreamReader(stream), template);
// 3. Execute with context variables
mustache.execute(writer, contextVariables);
// 4. Parse result as YAML into POJO
var result = Mapper.get().readValue(processedYaml, TargetClass.class);- Readable Templates: YAML structure preserved, easy to understand
- Type Safety: Templates produce valid YAML for strong typing
- Performance: Compiled templates execute efficiently
- Maintainability: Logic-less design prevents template complexity
- Ecosystem: Good Java tooling and IDE support
- Limited Logic: Complex conditionals require Java-side processing
- Learning Curve: Teams need to learn Mustache syntax
- Debugging: Template processing errors can be hard to trace
name: ${hosted.id}-vpc-cni
<#if production>
retainOnDelete: true
</#if>Rejected: Too powerful, encourages logic in templates, complex syntax
name: ${hosted.id}-vpc-cni
#if($production)
retainOnDelete: true
#endRejected: Legacy technology, complex setup, security concerns
String.format("name: %s-vpc-cni", hostedId)Rejected: No template files, poor maintainability, mixing code/config
name: "[(${hosted.id})]-vpc-cni"Rejected: Primarily web-focused, overkill for YAML processing
// Template.java:71-88 - Default variables
Map.entry("platform:id", scope.getNode().getContext("platform:id").toString())
Map.entry("deployment:account", scope.getNode().getContext("deployment:account").toString())// Launch.java:49-52 - Additional context
Template.parse(app, "conf.mustache",
Map.ofEntries(
Map.entry("deployment:eks:druid:release", "v24.0.0"),
Map.entry("deployment:tags", tags(app))
));- Missing variables: Mustache renders empty string
- Missing templates: RuntimeException with path details
- Invalid YAML: Jackson parsing exceptions