Home » Docs » Design

Template Mechanism

Some artifacts (see Object Graph in Client) can need some customization before being provisioned, e.g. configuration files might need some information that is managed by one of the distributions.

The customization will be done when a new version is created, i.e., on call of approve() on a StatefulTargetObject. A customized version of the artifact (which is located somewhere in an OBR, reachable using a URL) is uploaded to the same OBR, and the URL to the customized one is stored in the DeploymentVersionObject.

Proposed design

In addition to the interfaces ArtifactHelper and ArtifactRecognizer, we introduce a ArtifactPreprocessor, which has a single method preprocess(ArtifactObject object, Properties props), in which Properties contains customization information (see below), and the method returns the URL of the altered artifact (or, if nothing has changed, the original artifact, or, if this changed artifact is identical to one that has already been created before, that old URL). This ArtifactPreprocessor can be published as a service (see the section on remoting below), but for local purposes, the ArtifactHelper interface gets an extra method getPreprocessor(), which returns an instance of the preprocessor to be used for the type of artifact this helper helps.

As an added service, we could create a basic preprocessor, VelocityBasedPreprocessor which uses the Velocity template engine to process an artifact and store it in a configured OBR; this preprocessor can be instantiated and returned by each ArtifactHelper that needs a basic processor (if no processing can be done for some type of artifact, getPreprocessor should return null).

Customization information

For each template that has 'holes' to fill in, it can 'reach' all RepositoryObjects that are reachable from the TargetObject this template will be provisioned to, leading to a tree of data. Inspired by Velocity's way of finding contextual data, we propose to store the for each RepositoryObject in its own Properties object, adding its attributes and tags to it as two Properties objects using the keys "attributes" and "tags", and a List summing up all children (so, for a target, all its distributions) using the key "children"; in the end, this becomes a tree of Properties objects.

This way, the Velocity template can use syntax like

#foreach( $license in $gateway.children)
    #if ($license.attributes.vendor=="luminis")
        Default license by luminis
    #else
        Custom license by $license.attributes.vendor
    #end
#end

Support for remoting

Some customers might want to keep all information hidden from us, only allowing us the metadata on the server. In this case, we can deploy a ArtifactPreprocessor on the customer's site, which is then responsible for doing everything a local ArtifactPreprocessor can do, and returning a URL to the altered artifact. Then, in stead of returning an instance of the ArtifactPreprocessor, the ArtifactHelper will return some RemoteArtifactPreprocessor which implements the ArtifactPreprocessor interface, but talks to a servlet on the customer's server.

On the 'needsApprove' state in the StatefulTargetObject

With the mechanism above, determineStoreState in StatefulTargetObject would need to create a full deployment version every time we need to know whether approval is necessary. This is undesirable, because, in a remoting scenario, it means we have to pass lots of data to a servlet, oftentimes only to find out that we created a version identical to the one we already had. So, in stead of this rigid semantics, the 'needsApprove' state will become more of a 'tainted' state, which becomes true when something happens that could have an impact on this StatefulTargetObject. We can quite easily determine what targets are affected by a given change in the model by following the associations from that object to the targets.