Development Guide

In this section, practical development steps are discussed for anyone interested in contributing to the VLINGO XOOM Designer project.

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.

For any development on XOOM Designer you must set an environment variable named VLINGO_XOOM_STARTER_HOME. Although you have likely already set this property in order to use the XOOM Designer, that was as an enduser, which has a different value. As a developer working on the Designer, you must set this to the absolute path of a directory relative to where the vlingo-xoom-starter repository has been cloned.

Using a *nix shell, such as bash, set like this:

$ VLINGO_XOOM_DESIGNER_HOME=[git-clone-path]/dist/designer
$ export VLINGO_XOOM_DESIGNER_HOME

On Windows you can use the System Properties > Advanced > Environment Variables... to set the property permanently. For a one-time setting before running the design tool you can use the command line:

C:\> set VLINGO_XOOM_DESIGNER_HOME=[git-clone-path]\dist\designer

After making changes, from the root project directory, build and run the Designer. On *nix run these commands:

$ mvn clean package -P frontend
...
$ java -jar target/xoom-designer-1.6.1-SNAPSHOT.jar gui

This works on Windows:

C:\[git-clone-path]> mvn clean package -P frontend
...
C:\[git-clone-path]> java -jar target\xoom-designer-1.6.1-SNAPSHOT.jar gui

For more details see README.md in the xoom-designer repository.

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.xoom.http.resource.Resource;
import static io.vlingo.xoom.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.

To sum up, those are the common steps regarding code template files on xoom-designer. Our team is available to discuss and provide more information on Gitter and our public Slack workspace.