Development Guide

In this section, practical development steps are discussed for anyone interested in contributing to vlingo-xoom-starter.

Introduction to Application Generation feature

The following diagram gives us an Application Generation overview showing the interaction of its components:

As illustrated above, Application Generation can be run by two commands: xoom gen or xoom gui. Both are alternative ways for a quick start with VLINGO/XOOM, having exactly the same parameters list. The only difference is that the latter reads parameters from a properties file, while the first consumes parameters from web-based UI.

The second half of the diagram shows some tools that perform core actions. Maven Archetypes creates the project structure dynamically organizing the directory hierarchy, Maven configuration, also handling deployment resources as Dockerfile and K8s manifest file. Further, Apache FreeMarker takes care of class generation by processing preexisting code templates. That said, let's see how to add templates at the code level.

Create / Update Code Templates

The main constituent parts for every auto-generated class are:

Considering those parts, let's take RestResource class generation as an example and go through the implementation details, starting from the template file:

package ${packageName};
import io.vlingo.http.resource.Resource;
import static io.vlingo.http.resource.ResourceBuilder.resource;
public class ${resourceName} {
public Resource routes() {
return resource("${resourceName}" /*Add Request Handlers here as a second parameter*/);
}
}

As easy as it seems, the Rest Resource template file requires only two parameters values to generate a Rest Resource class: packageName and resourceName. The parameters handling and mapping are addressed by RestResourceTemplateData as follows:

public class RestResourceTemplateData extends TemplateData {
private final static String PACKAGE_PATTERN = "%s.%s";
private final static String PARENT_PACKAGE_NAME = "resource";
private final String packageName;
private final String aggregateName;
private final TemplateParameters parameters;
public RestResourceTemplateData(final String aggregateName,
final String basePackage) {
this.aggregateName = aggregateName;
this.packageName = resolvePackage(basePackage);
this.parameters = loadParameters();
}
private TemplateParameters loadParameters() {
return TemplateParameters
.with(REST_RESOURCE_NAME, REST_RESOURCE.resolveClassname(aggregateName))
.and(PACKAGE_NAME, packageName);
}
private String resolvePackage(final String basePackage) {
return String.format(PACKAGE_PATTERN, basePackage, PARENT_PACKAGE_NAME).toLowerCase();
}
@Override
public TemplateStandard standard() {
return REST_RESOURCE;
}
@Override
public TemplateParameters parameters() {
return parameters;
}
@Override
public String filename() {
return standard().resolveFilename(aggregateName, parameters);
}
}

RestResource classes should be placed under its own package. Hence, the resolvePackage method appends the project base package to the resource package. The full package name and the RestResource class name are mapped to the template parameters in loadParameters. Additionally, TemplateData requires the filename method implementation, which commonly uses the filename resolution logic in the corresponding TemplateStandard.

public class RestResourceGenerationStep extends TemplateProcessingStep {
@Override
protected List buildTemplateData(final TaskExecutionContext context) {
final String projectPath = context.projectPath();
final String basePackage = context.propertyOf(Property.PACKAGE);
final String restResourcesData = context.propertyOf(Property.REST_RESOURCES);
return RestResourceTemplateDataFactory.build(basePackage, projectPath, restResourcesData);
}
@Override
public boolean shouldProcess(final TaskExecutionContext context) {
return context.hasProperty(Property.REST_RESOURCES);
}
}

RestResourceGenerationStep implements the buildTemplateData method that passes parameter values, coming from the Web-based UI or properties file, to RestResourceTemplateData. In this particular scenario, RestResourceTemplateDataFactory is an additional and optional class that helps building RestResourceTemplateData. The shouldProcess method is also optional and useful when a TemplateProcessingStep subclass needs to be conditionally skipped.

Finally, TemplateProcessingStep has to be added in the Configuration steps list:

public static final List<CodeGenerationStep> CODE_GENERATION_STEPS = Arrays.asList(
new ModelGenerationStep(),
new ProjectionGenerationStep(),
new StorageGenerationStep(),
new RestResourceGenerationStep(),
new BootstrapGenerationStep(),
new ContentCreationStep()
);

Eventually, some peripheral points in the code are also involved. The following list is mainly related when a new template file is added:

1- Create an enum value in Template passing the template filename (without extension) in the constructor. Example:

public enum Template {
//Other template filenames
REST_RESOURCE("RestResource")
//Enum attributes
}

2- Map the new standard file to an existing TemplateStandard or create one. Sometimes there are multiple files for the same standard. For instance, there is one Aggregate template file for each Storage (Journal, State Store, Object Store). That means TemplateStandard is responsible for grouping template files by standard and helps the TemplateProcessor to find the proper file based on TemplateParameters such as StorageType. The examples below demonstrate the Aggregate and Rest Resource standards. The latter has only one related template file:

public enum TemplateStandard {
AGGREGATE(parameters -> AGGREGATE_TEMPLATES.get(parameters.from(STORAGE_TYPE))),
REST_RESOURCE(parameters -> CodeTemplateFile.REST_RESOURCE.filename),
//Other standards
}

3- In case it doesn't already exist, create an enum value in TemplateParameter for each template parameter.

In sum, those are the common steps regarding code template files on vlingo-xoom-starter. Our team is available to discuss and provide more information on Gitter.