Authoring blueprints
Declarative input validation
The reusable validation rules a blueprint field can declare, where they run, and how they apply to nested records and multi-select inputs.
Blueprints select reusable validation rules in META-INF/blueprint.json. The optional validation property
belongs to an input field (including a child field):
{
"name": "serviceName",
"type": "STRING",
"validation": "JAVA_IDENTIFIER",
"description": "Java identifier for the service"
}
The framework checks the rule during interactive CLI capture, input-file and portal submission, and again
before blueprint hooks or any generated file write (the generic-input-validator step). The execution check
also covers inputs supplied by an aggregated blueprint. Failures identify the field
and rule, with indexed paths for nested records.
Built-in rules
| Rule | Accepted values |
|---|---|
JAVA_IDENTIFIER |
An ASCII letter followed by letters, digits or underscores; Java keywords are rejected. |
JAVA_TYPE |
A JAVA_IDENTIFIER starting with an uppercase letter. |
JAVA_PACKAGE |
Dot-separated JAVA_IDENTIFIER segments; no empty segments. |
PROJECT_NAME |
A letter followed by letters, digits, underscores or hyphens. |
VERSION |
Letters, digits, dots, underscores and hyphens. |
HTTP_URL |
An absolute HTTP or HTTPS URL with a host. |
KAFKA_TOPIC |
1–249 ASCII letters, digits, dots, underscores or hyphens; excludes . and ... |
Rule names are case-sensitive, and an unknown rule is a blueprint configuration error. Leaving validation out
preserves the existing field-type validation. Destination checks and the sinceVersion runtime check still run
independently. required, defaults, dropdown validValues and visibleWhen keep their meaning: hidden fields
are skipped, absent optional values are accepted, and resolved values are validated. Validation never rewrites
a name to make it valid — an invalid value fails rather than being silently corrected.
Rules on nested records and multi-selects
For a RECORD_ARRAY, put the rules on the child fields; record children are validated recursively:
{
"name": "fields",
"type": "RECORD_ARRAY",
"required": false,
"childFields": [
{ "name": "name", "type": "STRING", "validation": "JAVA_IDENTIFIER" }
]
}
A rule on a multi-select input applies to each selected value. Structured Maven inputs keep their SuperPOM and
POM_Dependencies normalization and coordinate validation (see Composable blueprints).
Validating a whole payload from code
BlueprintPayloadValidator (in jgen-base, 2.1.35) is the one validator every input path uses: jgen -f,
the portal, composed child blueprints through BlueprintInputService.buildInputMap, and external callers such
as ChenAIle. It resolves a payload as generation will (config defaults, ${...} substitution, visibleWhen,
boolean and Maven-coordinate normalization) and reports every problem, not just the first.
PayloadValidation result = BlueprintPayloadValidator.validate(blueprint, config, json); // config may be null
if (!result.valid()) result.errors().forEach(System.err::println); // e.g. "fields[0].name: Field fields[0].name must satisfy JAVA_IDENTIFIER"
Map<String, Object> inputs = result.orThrow(); // resolved inputs, or an exception listing all errors
Each PayloadIssue has a path ($, a field name, or a nested path such as fields[0].name), a code and a
message:
| Code | Kind | Meaning |
|---|---|---|
INVALID_JSON |
error | Not valid JSON, empty, or a key appears twice (with line and column). |
NOT_AN_OBJECT |
error | Valid JSON, but not an object of field values. |
REQUIRED |
error | A required field that is visible is missing or empty. |
INVALID_VALUE |
error | Wrong type or shape, a value outside validValues, or a broken validation rule. Booleans must be true/false/y/n; other text is no longer silently read as false. |
INVALID_INPUT |
error | A generic input every blueprint shares: com/company/org segments, the project name, a *Version, or the registry URL. |
UNKNOWN_FIELD |
warning | A key the blueprint does not declare; generation ignores it. The blueprint key of an input file is exempt. |
NOT_VISIBLE |
warning | A field supplied while its visibleWhen condition is false; generation ignores it. |
The validator never touches the filesystem. Two checks stay at the generation boundary: the destination folder
must not already exist, and a FILE field must name an existing file. validateFields(...) runs the field
checks without the generic ones; buildInputMap uses it because the generic checks run on the final, composed
map before generation. The older fail-fast helpers (InputValidation.validateBlueprint and validateField,
BlueprintValidationUtils.validateGenericInputs) are now built on the same collect-all checks and throw the
first problem.
With jgen -f, the CLI runs the validator on the input file before generating, prints warnings, and stops with
the full list of errors.
Where validation belongs
Put common scalar validation in these declarations. Leave relationship and domain-specific checks — process
hierarchy, cron payload contracts, and the like — to the blueprint’s init hook. To add a new shared rule, extend
ValidationType and InputValidation in jgen-base and document its contract.
How the built-ins declare rules
Service, headless-service, batch, interceptor, integration-test application, query-namespace, mini-monolith and
generated-blueprint names use JAVA_IDENTIFIER — their templates insert those names directly into Java
packages. Ecosystem child service/namespace/monolith names use the same rule, so an invalid child name fails
before ecosystem generation starts. The integration-test entity uses PROJECT_NAME to keep hyphenated names in
routes and feature filenames. Workflow service names keep PROJECT_NAME, as do the ecosystem outer project and
the process worker JAR name (these are Maven project names). Versions, URLs, Kafka topics and package arrays use
their matching rules; free-text instructions, runtime-property values, file paths, booleans and dropdowns keep
their field-type checks.