Technical Reference
Preface
The initializer has many similarities to the expandersExpanderA unit of generation that combines a data tree instance (functional M1 model) with a template to generate an artifact., in that it generates some files and performs some actions based on a set of predefined arguments. Because of this, the initializer was upgraded to v2.x by replacing the templating systemSystemA set of interconnected components that work together to achieve a specific purpose or function. with actual expandersExpanderA unit of generation that combines a data tree instance (functional M1 model) with a template to generate an artifact. that were executed internally in the initializer at runtime. This systemSystemA set of interconnected components that work together to achieve a specific purpose or function. made extensive use of the expanderExpanderA unit of generation that combines a data tree instance (functional M1 model) with a template to generate an artifact. infrastructure, but also limited itself to a very specific subset of capabilities.
Version 3.x addresses this by completely migrating the initialization process into expander bundlesExpander BundleA set of expanders sharing a repository, packaged into an expansion resource or archive., making it fully use the expanderExpanderA unit of generation that combines a data tree instance (functional M1 model) with a template to generate an artifact. infrastructure. What remains of the initializer applicationApplicationThe overarching model container grouping all components, domain logic, and configurations for an enterprise solution. itself, is that it facilitates the input of arguments that are needed to run the initialization itself.
Initializer expansion bundles
The initializer expansion bundles are in essence the same as any other expansion resourcesExpansion Resource (ExpansionResource). A reusable resource bundle containing expanders and features that provide specific application capabilities, defined in the expansion settings and added to the classpath during expansion., with the exception that they contains data resources which represent the project type in a model, as well as some Java classes for dynamic behavior.
Model
The initializer applicationApplicationThe overarching model container grouping all components, domain logic, and configurations for an enterprise solution. itself is no longer needed for the initialization process, since it is just a regular expansion now. What is required to enable this is an NS model which describes all the arguments that are needed to initialize the project.
ProjectType
The ProjectType element in the initializer meta-model is an element which describes all the arguments that are required to generate the project. These are used to dynamically generate the command-line interface in the initializer applicationApplicationThe overarching model container grouping all components, domain logic, and configurations for an enterprise solution..
The ProjectType defines the name and potentially an alias for the project type. These are used on the command-line interface to select the appropriate dynamically generated subcommand. E.g.: nsinit <name> ... or nsinit <alias> ....
All arguments are defined as linked ProjectArgumentType and ProjectFlagType elements, which define arguments with a value or a simple boolean flag respectively. Each of these arguments or flags have a name and alias of their own, which is translated to kebab-case on command-line. E.g.: For a flag testFlag, the command-line parameter would be --test-flag. For an argument someValue, the command-line parameter would be --some-value <value>.
In the ProjectType element, it is possible to add ProjectDataResolver elements, which contains the fully-qualified class name of a class which can manipulate the Project model which contains the actual values associated with the type definitions. The goal of this is to provide dynamic behavior to set the values of particular arguments.
The flags and arguments can be set to implicit (default: false), which means that they will not be added as parameters on the command-line interface. These will be assigned the defaultValue initially and can also be manipulated with the ProjectDataResolver class.
The InteractiveModeHandler elements which can be added to the ProjectType contains the fully-qualified class name of the interactive mode handler, which is used to implement the interactive mode of the command-line interface for that project.
Project
The Project model represents an instance of ProjectType and is intended to couple values to the types, which are then used to generate the actual project. The initializer applicationApplicationThe overarching model container grouping all components, domain logic, and configurations for an enterprise solution. uses the command-line interface, interactive mode and some refinement steps to produce a Project model, which has a value associated for every argument and flag defines in the ProjectType model it is based on. This element is never part of the expansion resourceExpansion Resource (ExpansionResource). A reusable resource bundle containing expanders and features that provide specific application capabilities, defined in the expansion settings and added to the classpath during expansion., but is processed by the ProjectDataResolver and InteractiveModeHandler classes.
For each ProjectArgumentType and ProjectFlagType found in the ProjectType linked to a Project, the initializer applicationApplicationThe overarching model container grouping all components, domain logic, and configurations for an enterprise solution. will create a ProjectArgument and ProjectFlag respectively. Their name (as well as that of Project), is assigned the name of the type element. Other than the name fieldFieldA property of a DataElement expanded to a database column and user interface input element., the only other additional fieldFieldA property of a DataElement expanded to a database column and user interface input element. in value, which holds the value of the argument.
The Project element coupled to the InitProject program typeProgram TypeAn architectural style with predefined technologies/standards (e.g., 'JEE Application'). for expansion in the initializer model. This means that unlike typical applicationApplicationThe overarching model container grouping all components, domain logic, and configurations for an enterprise solution. expansion where the program typeProgram TypeAn architectural style with predefined technologies/standards (e.g., 'JEE Application'). Application is expanded, in this model the expandersExpanderA unit of generation that combines a data tree instance (functional M1 model) with a template to generate an artifact. directly expand the Project element. The program typeProgram TypeAn architectural style with predefined technologies/standards (e.g., 'JEE Application'). InitProject itself indicates to the expandersExpanderA unit of generation that combines a data tree instance (functional M1 model) with a template to generate an artifact. that Project is the root element for expansion.
Data resources
The ProjectType model is defined as part of the data resources in the expansion resourceExpansion Resource (ExpansionResource). A reusable resource bundle containing expanders and features that provide specific application capabilities, defined in the expansion settings and added to the classpath during expansion.. This is a set of models that can be added to an expansion resourceExpansion Resource (ExpansionResource). A reusable resource bundle containing expanders and features that provide specific application capabilities, defined in the expansion settings and added to the classpath during expansion., so it can be read out by the expandersExpanderA unit of generation that combines a data tree instance (functional M1 model) with a template to generate an artifact. ahead of the actual expansion.
The data resources are typically defined in a _data.xml file and list all models and their path in the expansion resourceExpansion Resource (ExpansionResource). A reusable resource bundle containing expanders and features that provide specific application capabilities, defined in the expansion settings and added to the classpath during expansion.. In the case of the initializer expansion resourcesExpansion Resource (ExpansionResource). A reusable resource bundle containing expanders and features that provide specific application capabilities, defined in the expansion settings and added to the classpath during expansion., this will always at least contains a reference to the file which contains the ProjectType model. The only exception when this is not the case, is when the expansion resourceExpansion Resource (ExpansionResource). A reusable resource bundle containing expanders and features that provide specific application capabilities, defined in the expansion settings and added to the classpath during expansion. is an addon for another initializer expansion resourceExpansion Resource (ExpansionResource). A reusable resource bundle containing expanders and features that provide specific application capabilities, defined in the expansion settings and added to the classpath during expansion. that already defines the ProjectType model and does not require any additions.
<dataResources>
<dataResource>
<path>data/initializer/projectTypes.xml</path>
<elementTypeCanonicalName>net.democritus.initializer.model.ProjectType</elementTypeCanonicalName>
</dataResource>
</dataResources>
The projectTypes.xml data resource can technically contain definitions for more than one ProjectType model, which means that it is possible to generate more than one ProjectType with a single expansion resourceExpansion Resource (ExpansionResource). A reusable resource bundle containing expanders and features that provide specific application capabilities, defined in the expansion settings and added to the classpath during expansion.. It is however recommended to only have one per expansion resourceExpansion Resource (ExpansionResource). A reusable resource bundle containing expanders and features that provide specific application capabilities, defined in the expansion settings and added to the classpath during expansion..
<projectTypes>
<projectType name="bestProjectType">
<description>Initialize the best project.</description>
<projectDataResolvers>
<projectDataResolver name="com.example.template.myinit.MyProjectDataResolver"/>
</projectDataResolvers>
<interactiveModeHandlers>
<interactiveModeHandler name="com.example.template.myinit.MyInteractiveModeHandler"/>
</interactiveModeHandlers>
<arguments>
<argument name="groupId">
<description>The groupId that will be used in the POM files.</description>
<alias>groupid</alias>
<default>com.example</default>
</argument>
<argument name="artifactId">
<description>The artifact identifier that will be used in the POM files.</description>
</argument>
</arguments>
<flags>
<flag name="git">
<description>If enabled, no git repository is initialized.</description>
<default value="true"/>
</flag>
<flag name="addKittens">
<description>If enabled, kittens will be added. This is not on command-line as it is implicit.</description>
<default value="true"/>
<implicit value="true"/>
</flag>
</flags>
</projectType>
</projectTypes>
Expanders
Files that should be initialized are created as expandersExpanderA unit of generation that combines a data tree instance (functional M1 model) with a template to generate an artifact., just like in any other expander bundleExpander BundleA set of expanders sharing a repository, packaged into an expansion resource or archive.. One notable difference is that the isApplicable fieldFieldA property of a DataElement expanded to a database column and user interface input element. of the expanderExpanderA unit of generation that combines a data tree instance (functional M1 model) with a template to generate an artifact. should always check the ProjectType that is being expanded, as the expandersExpanderA unit of generation that combines a data tree instance (functional M1 model) with a template to generate an artifact. for all available project types will be processed for any Project, regardless of its type.
<expander xmlns="http://nsx.normalizedsystems.org/20204/expander" name="SomeFileExpander">
<packageName>net.democritus.initializer.template.myinitializer</packageName>
<layerType name="ROOT"/>
<technology name="COMMON"/>
<sourceType name="SRC"/>
<elementTypeName>Project</elementTypeName>
<artifactModifiers/>
<artifactName>SomeFile.ext</artifactName>
<artifactPath>$expansionRoot.directory$</artifactPath>
<isApplicable>project.projectType.name eq 'myProjectType'</isApplicable>
<phase>expansion</phase>
<active value="true"/>
<anchors/>
<customAnchors/>
</expander>
ProjectDataResolver
Zero or more ProjectDataResolver classes can be defined for a ProjectType. If present, they will be called sequentially for various steps in the initialization process. The goal of the ProjectDataResolver class, is to allow initializers to implement dynamic behavior for setting default values and/or updating values provided by the user. This systemSystemA set of interconnected components that work together to achieve a specific purpose or function. is also required to set arguments and flags marked as implicit, since the user can only provide the values for these if they use the -D argument, or if an InteractiveModeHandler provides a prompt which sets the value.
As shown in the example below, a typical use-case is setting the artifactId for a Maven project to the name of the output directory as the default value. Note that the output directory is not yet known in the first step of the initialization process, so it is required to verify that it is present, as this class is called multiple times to refine the Project further. Though the phase of the initialization process is not known in this class, the implemented logic should consistently update the Project model based on its current contents whenever the class is called.
public class MyProjectDataResolver implements ProjectDataResolver {
@Override
public ProjectTree resolve(ParameterContext<ProjectTree> parameterContext) {
final Context context = parameterContext.getContext();
final ProjectTree projectTree = parameterContext.getValue();
final InitializationContext initializationContext = context.getContext(InitializationContext.class).getValue();
final File outputDirectory = initializationContext.getOutputDirectory();
// If the output directory has already been determined
if (outputDirectory != null) {
// Find the argument with the name artifactId if it is still empty
findProjectArgument(projectTree, argumentName("artifactId").and(argumentEmpty())).ifPresent(mainArg ->
// And assign it the name of the output directory as its value
mainArg.setValue(outputDirectory.getName()));
}
return projectTree;
}
}
InteractiveModeHandler
The InteractiveModeHandler handler class is used to define the interaction with the user during the interactive mode. This is provided by the expansion resourceExpansion Resource (ExpansionResource). A reusable resource bundle containing expanders and features that provide specific application capabilities, defined in the expansion settings and added to the classpath during expansion., as it allows for the most flexible way of implementing the interactive mode. An InteractionProvider object is provided, which has several methods that can facilitate communication with the user during the interactive mode phase:
message(InteractionMessage): Allows the handler to simply output any message to the user.requestBoolean(InteractionRequest<Boolean>): Allows the handler to prompt the user with a yes/no question to retrieve a boolean value. This is typically used to prompt for the value of aProjectFlag.requestString(InteractionRequest<String>): Allows the handler to prompt the user for a string value. This is typically used to prompt for the value of aProjectArgument. This is the only alternative to a boolean value, as allProjectArgumentvalues are strings.
The InteractionRequest class can take both a prompt (question) for the user, as well as a defaultValue, which is typically set to the current value of a ProjectArgument or ProjectFlag. It is advised to always set a defaultValue, but if it is not set, the user will be prompted to fill in a value to proceed.
The ProjectDataResolver classes will be called once more after the InteractiveModeHandler classes, so any post-processing of the data should happen in the ProjectDataResolver. The InteractiveModeHandler should simply store the data in the Project.
public class MyProjectInteractiveModeHandler implements InteractiveModeHandler {
@Override
public ProjectTree execute(InteractionProvider interactionProvider, ProjectTree projectTree) {
// Find the argument artifactId
findProjectArgument(projectTree, argumentName("artifactId")).ifPresent(arg ->
// Set the argument to the value returned by the user (or the default)
arg.setValue(interactionProvider.requestString(new InteractionRequest<String>()
// The question to ask the user
.setPrompt("Specify the artifactId for the project")
.setDefaultValue(arg.getValue()))
.getValue()));
return projectTree;
}
}
ExpansionSettings file
The initializer applicationApplicationThe overarching model container grouping all components, domain logic, and configurations for an enterprise solution. operates on a set expansion resourcesExpansion Resource (ExpansionResource). A reusable resource bundle containing expanders and features that provide specific application capabilities, defined in the expansion settings and added to the classpath during expansion. which are set in an expansionSettings.xml file:
<expansionSettings>
<expansionResources>
<expansionResource name="net.democritus.initializer:nsx-application-initializer" version="7.0.1"/>
<expansionResource name="net.democritus.initializer:nsx-expanders-initializer" version="7.0.1"/>
<expansionResource name="net.democritus.initializer:initializer-initializer" version="7.0.1"/>
</expansionResources>
</expansionSettings>
This file is identically structured as any other file with an ExpansionSettings model, but it only contains expansionResource data refsData Ref (DataRef). An object encapsulating a reference (such as a database ID, name, or functional key) to a specific DataElement instance. of all bundles with initializer projects which the initializer should load and process.
By default, the initializer comes with an internal file containing predefined initializer bundles that it will use. It is possible to override this with a different expansionSettings.xml file, by placing it in one of two locations (with the following priority:
- In a subfolder of the user folder:
~/.nsinit/expansionSettings.xml. - The same directory as the initializer jar file.
Initialization process
The initializer applicationApplicationThe overarching model container grouping all components, domain logic, and configurations for an enterprise solution. goes through various phases in order to create the Project model.
The steps as shown in the diagram are as follows:
- When starting, the initializer applicationApplicationThe overarching model container grouping all components, domain logic, and configurations for an enterprise solution. will create an instance of the
Projectmodel, that is linked to and mirrors perfectly eachProjectTypeit finds in the expansion resourcesExpansion Resource (ExpansionResource). A reusable resource bundle containing expanders and features that provide specific application capabilities, defined in the expansion settings and added to the classpath during expansion. listed in theexpansionSettings.xmlfile. For eachProjectTypeit finds, it will also dynamically create the command-line interface so the--helpis also populated for every availableProjectType. - If defined, the default values from each
ProjectArgumentTypeandProjectFlagTypeare copied to theProjectArgumentandProjectFlagelements. - For each available
ProjectType, theProjectinstance that was created is handed of to each of theProjectDataResolverclasses defined for thatProjectType. The class can then set some initial values dynamically if needed. At this stage the CLI processor has not yet been called, which means that the output directory has not yet been processed, as this is done only at the point when-o/--outputcan be processed. Because of this,getOutputDirectory()in theInitializationContextwill returnnull. - The command-line processor is invoked, which then selects the correct command-line interface (sub-command) for the requested
ProjectType. The sub-command will first process the-o/--outputargument, defaulting if not defined to the directory where the initializer was invoked. This sub-command contains all the command-line arguments for the specificProjectType, over which it iterates and if the arguments are supplied, it updates theProjectmodel with the supplied values. - Now that the command-line arguments have been processed, all
ProjectDataResolverclasses defined for theProjectTypeare called once again to refine theProjectmodel further. At this stage the output directory is known, sogetOutputDirectory()in theInitializationContextwill no longer returnnull. That value is often used to set the default of an argument likeartifactIdto the name of the output directory. - The initializer will check if the option
-y/--yes-to-allhas been set on command-line. If it has, step 7 and 8 are skipped entirely, if not it will start the interactive mode handling. - At this stage the initializer will invoke all
InteractiveModeHandlerclasses defined for theProjectType. These are intended to prompt the user with questions. AnInteractionProviderobject is provided to communicate with the user at this stage. For each prompt it is possible to define a default value. These are typically set to the current value of an argument in theProject, so the user can accept that current value or override it with a new value of their own choosing. It is not intended for this stage to actually do any processing of theProjectbeyond setting the arguments to values obtained from user-interaction. - The final step of the
Projectmodel refinement once again calls everyProjectDataResolverclass defined for theProjectType. This is a final opportunity to process the values, after the user has had the opportunity to input more data through the interactive mode. - With the entire
Projectmodel being finalized, the initializer will write this to a<project.name>.xmlfile, alongside anexpansionSettings.xmlfile with the output directory and expansion resourcesExpansion Resource (ExpansionResource). A reusable resource bundle containing expanders and features that provide specific application capabilities, defined in the expansion settings and added to the classpath during expansion., as well as apom-initializer.xmlMaven file to run the expansion for theProject. Unless--exportis defined, the initializer will now invoke Maven to run the expansion on thisProjectmodel to perform the actual initialization.