Skip to content

The Tentackle Wizard Maven Plugin

The tentackle-wizard-maven-plugin is a developer productivity tool. While the tentackle-maven-plugin and the wurbelizer take care of generating the persistence layer from an existing model, the wizard plugin helps you author the source files that carry that model in the first place.

It scaffolds the Java source files for new PDOs and operations, filling in the boilerplate (interfaces, implementations, the remote layer, the model comment block, class IDs, package declarations, wurblet anchors) so that all you have to do afterward is describe the attributes and write the business logic. Most of its goals open an interactive JavaFX wizard, but the pdo goal can also run unattended in batch mode.

All goals share the prefix tentackle-wizard, so they can be invoked directly from the command line, e.g. mvn tentackle-wizard:pdo.

Prerequisites

The plugin requires the same JDK and Maven versions as the rest of the framework (JDK >= 25, Maven >= 3.9.0). Because the wizards are JavaFX applications, the interactive goals (pdo, operation, browse) need a graphical desktop; the browse goal additionally needs a reachable Tentackle backend (database or remote server). The init and the batch variant of pdo run headless.

All goals are aggregator goals and operate on the whole reactor. They are therefore best invoked from the directory of the project's maven parent (the aggregator pom), so that the wizard sees every module and can place the generated files into the correct ones.

Overview of Goals

Goal Mode Purpose
pdo interactive / batch Create the source files for a new Persistent Domain Object.
operation interactive Create the source files for a new Operation (behavior without an entity).
browse interactive Connect to a backend, browse persisted PDOs and generate test code.
init headless (Re)install the default code-generation templates into the template directory.
help Print plugin usage information (generated by the maven-plugin-plugin).

How the Wizard Places Files: the Model and Profiles

To know where a generated file belongs, the plugin needs two kinds of information.

The model. All goals load the application's model so the wizard knows which entities, class IDs, and table names already exist (and can prevent collisions). By default, the model is read from the dependencies on the plugin classpath (loadModelFromDependencies = true) and from ${project.build.directory}/wurbel/model. The relevant parameters, shared by all goals through the common base, are:

  • modelName (tentackle.modelName): the model to load. Defaults to the standard model name.
  • modelDir (tentackle.modelDir): directory holding the model files. Defaults to ${project.build.directory}/wurbel/model. Ignored if filesets is given.
  • filesets: explicit file sets holding the model, overriding modelDir.
  • modelDefaults (tentackle.modelDefaults) and entityAliases (tentackle.entityAliases): model defaults and entity aliases to apply while loading, in the same syntax as in the model source.
  • loadModelFromDependencies: also load the model from the resources of the plugin's dependencies (default true).

Profiles. A profile tells the wizard the target packages and naming for a class of objects. Almost every Tentackle application groups its entities (e.g., master data vs. transactional data), and each group typically lives in its own set of packages and its own range of class IDs. You declare one <profile> per group in the <profiles> configuration; the wizard offers them for selection (or, in batch mode, picks one per entity by stereotype). Profile names must be unique.

All profiles (for both pdo and operation) share these elements:

  • name (required): the unique profile name.
  • domainPackage, persistencePackage, domainImplPackage, persistenceImplPackage (required): the target packages for the domain/persistence interfaces and their implementations. Each package must be mapped to exactly one module of the reactor, otherwise the goal fails. This is how the wizard decides which module a file goes into (and a requirement for JPMS since split packages are not allowed in modular projects).
  • domainInterface, persistenceInterface, domainImplementation, persistenceImplementation (optional): override the default super types (DomainObject, PersistentObject, AbstractDomainObject, AbstractPersistentObject) for the generated types.

PdoProfile adds:

  • pdoPackage (required): the package of the PDO interface.
  • pdoInterface (optional): the super PDO interface, defaults to PersistentDomainObject.
  • minClassId (required) / maxClassId (optional): the range of class IDs reserved for this profile. IDs below 100 are reserved for Tentackle and rejected. See Class IDs below.
  • tablePrefix (optional): a prefix prepended to table names entered in the wizard.

OperationProfile adds:

  • operationPackage (required): the package of the operation interface.
  • operationInterface (optional): the super operation interface, defaults to Operation.

The pdo Goal

This is the main goal. It creates the set of source files for a new PDO:

  • the PDO interface (carrying the model comment block),
  • the domain interface and the persistence interface,
  • the domain implementation and the persistence implementation,
  • and, if remoting is enabled, the remote interface and remote implementation.

Each file is written into the directory of the module that owns its package, as derived from the selected profile.

Interactive mode (the default) opens the PDO Wizard, a JavaFX dialog. You pick a profile, enter the entity name, choose the inheritance type and the super entity (if any), the table name, whether caching and remoting are enabled, and a description. The wizard validates the input live against the model — it refuses entity names, class IDs, and table names that already exist, and enforces the rules that follow from the chosen inheritance type (for example, an embedded entity gets no class ID and no caching). The next free class ID is pre-filled from the profile's range.

Batch mode is enabled by configuring batchFilesets. Instead of opening the UI, the goal reads a secondary model from those file sets and generates the code for every entity it contains that does not yet exist in the project model. This is handy for bootstrapping a project from an existing schema (e.g., a model dumped by the tentackle-sql-maven-plugin). Batch parameters:

  • batchFilesets: the file sets holding the secondary model, in plain model format (as used in the wurbelizer here-docs, or as dumped with dumpAsComment and dumpVariables both false). Directories default to the project base directory.
  • If more than one PdoProfile is configured, each entity must select its profile via a stereotype matching the profile name (case-insensitive), e.g. #MASTERDATA.
  • batchModelDefaults: model defaults applied to the batch model. Defaults to !ROOT, !ROOTID, !ROOTCLASSID, UNTRACKED to suppress unwanted entity options, since the dumped model usually already has the project defaults baked in.
  • dumpColumnGap (default 2): minimum number of spaces between columns in the generated attribute section of the model source.
  • dumpAnnotationsAsOptions: which annotations to render as attribute options in the generated model source (e.g. @NotNull|, @NotZero; @* matches all, a trailing | matches only parameterless annotations).

At least one PdoProfile must be configured or the goal fails.

The operation Goal

The counterpart to pdo for operations — domain behavior that is not bound to a persistent entity. It opens the Operation Wizard and generates the operation interface, the domain and persistence interfaces, their implementations, and, where applicable, the remote layer. Configuration mirrors pdo but uses OperationProfiles; at least one must be configured. There is no batch mode and no class-ID handling (operations have no class IDs).

The browse Goal

Opens the PDO Browser, which connects to a running backend, lets you navigate the PDOs persisted in the database along their relations, and generates test code (Java fixtures and the corresponding SQL) from the selected objects. It is the tool of choice for building realistic integration-test data from existing records. Parameters:

  • url (required): the backend URL.
  • user (required) / password: the backend credentials.
  • maxLinesInStringLiteral: caps the number of lines emitted in generated multi-line string literals.
  • mapSchemas (tentackle.mapSchemas): map schema names to flat table names in the generated SQL SELECT statements.

If no backend can be reached, the goal fails with a clear message.

The init Goal

Copies the bundled default templates into the template directory, overwriting any existing files there. The wizard installs the templates automatically the first time it runs (when the template directory does not yet exist), so you normally only need init to reset customized templates back to their defaults.

Templates

All generated source is produced from FreeMarker templates. They live under the templateDir parameter (default ${project.basedir}/templates), organized into two categories matching the goals: pdo/ and operation/. Each category holds one *.ftl per generated artifact, e.g. pdo/PdoInterface.ftl, pdo/DomainImplementation.ftl, operation/OperationInterface.ftl, and so on.

Because the templates are plain files in your project, you can customize them — adjust the license header, add house-style javadoc, change default imports — and every subsequently generated file will follow your conventions. Run tentackle-wizard:init to restore the originals.

Class IDs

Every root PDO needs a unique, stable class ID used by the persistence layer. The pdo goal manages these automatically:

  • Each PdoProfile declares a minClassId (and optionally a maxClassId). When maxClassId is omitted, the plugin derives the boundaries from the other profiles' ranges, ensuring they do not overlap.
  • Before each run, the wizard scans the model for the highest class ID already used per profile and pre-selects the next free one.
  • To stay consistent across several PDOs generated without an intermediate mvn clean/wurbel run, the last allocated ID per profile is recorded in a status file under ${project.build.directory}/wizard/<profile>.classid. Because this lives under target, it is cleaned by mvn clean.

If a profile runs out of free class IDs, the goal warns; reserved IDs below 100 are rejected.

Example Configuration

<plugin>
  <groupId>org.tentackle</groupId>
  <artifactId>tentackle-wizard-maven-plugin</artifactId>
  <version>${project.version}</version>
  <configuration>
    <profiles>
      <profile>
        <!-- a PdoProfile -->
        <name>masterdata</name>
        <pdoPackage>com.example.app.pdo</pdoPackage>
        <domainPackage>com.example.app.domain</domainPackage>
        <persistencePackage>com.example.app.persistence</persistencePackage>
        <domainImplPackage>com.example.app.domain.impl</domainImplPackage>
        <persistenceImplPackage>com.example.app.persistence.impl</persistenceImplPackage>
        <minClassId>1000</minClassId>
        <maxClassId>1999</maxClassId>
        <tablePrefix>md_</tablePrefix>
      </profile>
    </profiles>
  </configuration>
</plugin>

With this in place:

# scaffold a new PDO interactively
mvn tentackle-wizard:pdo

# scaffold a new operation interactively
mvn tentackle-wizard:operation

# browse a backend and generate test code
mvn tentackle-wizard:browse -Durl=jdbc:postgresql://localhost/appdb -Duser=app

# restore the default templates
mvn tentackle-wizard:init

Run these from the aggregator (parent) directory so the wizard can place files into all modules.

How It Fits Into the Workflow

The wizard is a one-shot authoring tool, not part of the regular build lifecycle:

  1. tentackle-wizard:pdo / :operation — scaffold the source files for a new domain object.
  2. You fill in the attributes in the model comment block and add the business logic.
  3. The normal build (tentackle:analyze → wurbelizer → compiler) then generates the persistence mappings and SQL from that model, exactly as described in the Tentackle Maven Plugin documentation.

Further Reading