Skip to content

Algorithm Chaining

The extensible algorithm framework allows algorithms to instantiate and call other algorithms. This is useful to allow for the composition and reuse of algorithm behaviors. This feature is referred to as algorithm chaining.

Calling Other Algorithms

In order to make use of this feature, the caller algorithm must acquire an object of the algorithm class it wishes to call by requesting it by instance name using the getAlgorithmByName method of the ComponentService object. This is done during the execution of the algorithm's setup method.

This method requires that the caller specify two values:

  1. A reference to the algorithm instance. This must be stored in an AlgorithmInstanceReference object whose value is the name of the algorithm instance. This is algorithmName in the Masking API, occasionally referred to as "algorithmCd" or "algorithm code". The AlgorithmInstanceReference object must be referenced in a public field in the algorithm object.
  2. The type of data the returned algorithm object should mask, selected from the core types supported by the extensible algorithm framework. Type adaptation is not currently supported in this context, so the algorithm's native type must be the type requested using getAlgorithmByName.

Once an algorithm object has been obtained using getAlgorithmByName, a reference to the algorithm object maybe kept and that algorithm's mask method called as needed.

Examples:

  • You are creating algorithm instance A via the Masking API Client Algorithm endpoint, and algorithm A uses getAlgorithmByName to find algorithm B during setup. For the creation of algorithm A to succeed, algorithm B must already exist on the Delphix Masking Engine.
  • You are installing a plugin that would create the same algorithm A as a static instance. This will fail if algorithm instance B is not also provided by an algorithm class in the same plugin.

Because it is difficult to predict what algorithm names exist on a Delphix Masking Engine, it is advised that the names of any algorithms used for chaining be supplied in the algorithm's JSON configuration. Hard-coding names of algorithms passed to getAlgorithmByName directly in the Java source creates dependencies that are not visible except in the error message that results when the caller algorithm fails to initialize, as described in the second example scenario above. Hard-coded references to other algorithms provided by the same plugin should have the value ":algorithmName". The ":" character tells the API to fill in this plugin's name when searching for the instance.

Note

Algorithm instances provided by plugins (via the getDefaultIntances method) are prohibited from having dependencies on algorithm instances provided by other plugins. A way to safely implemented this kind of dependency may be added in the future.

Example Algorithm

public class RandomizedStringMasking implements MaskingAlgorithm<String> {
   private List<MaskingAlgorithm<String>> algorithmList = new ArrayList<>();
   private Iterator<Integer> randomStream;

   @JsonProperty(value = "algorithmNames", required = true)
   public List<AlgorithmInstanceReference> algorithms;

   @Override
   public String getName() {
       return "Randomized Masking";
   }
   @Override
   public Collection<MaskingComponent> getDefaultInstances() {
       RandomizedStringMasking myInstance =
               new RandomizedStringMasking() {
                   @Override
                   public String getName() {
                       return "Randomized Redaction";
                   }
                   @Override
                   public String getDescription() {
                       return "Apply a random redaction algorithm from { X, Y, Z }";
                   }
               };
       myInstance.algorithms =
               Arrays.asList(
                       new AlgorithmInstanceReference(":Redaction X"),
                       new AlgorithmInstanceReference(":Redaction Y"),
                       new AlgorithmInstanceReference(":Redaction Z"));

       return Collections.singletonList(myInstance);
   }

   @Override
   public void validate() throws ComponentConfigurationException {
       if (algorithms == null || algorithms.isEmpty()) {
           throw new ComponentConfigurationException(
                   "Value for field algorithmNames is missing or empty");
       }
       for (AlgorithmInstanceReference ref : algorithms) {
           GenericReference.checkRequiredReference(ref, "algorithms");
       }
   }

   @Override
   public void setup(@Nonnull ComponentService serviceProvider) {
       for (AlgorithmInstanceReference algorithm : algorithms) {
           algorithmList.add(serviceProvider.getAlgorithmByName(algorithm, MaskingType.STRING));
       }
       randomStream = new Random().ints(0, algorithmList.size()).iterator();
   }

   @Override
   public String mask(@Nullable String s) throws MaskingException {
       return algorithmList.get(randomStream.next()).mask(s);
   }

Some methods have been omitted for brevity.

This algorithm is configured with a list of other String masking algorithms and masks by calling another algorithm from that list at random. This randomization is not based on the algorithm key, so results will not be consistent across masking runs. In addition, this framework defines a default instance that chooses randomly between algorithms "Redaction X", "Redaction Y" or "Redaction Z" included in the same plugin.

The algorithm's public fields include a list of AlgorithmInstanceReference objects, made configurable by the JsonProperty annotation.

This algorithm's setup method does the following:

  • For each algorithm name, it calls getAlgorithmByName to instantiate a usable algorithm object, saving them in algorithmList.
  • It initializes a random number generator to produce integers corresponding to each index in algorithmList.

This algorithm's mask method selects an algorithm at random from algorithmList and calls it's mask method on the input value, returning the result.

This algorithm's getDefaultInstances method creates a single instance that chooses between three algorithms. Each algorithm reference begins with ':', indicating that these algorithms should be found in the same plugin as this algorithm. The getName and getDescription methods of the returned object are overridden to provide values different from those of the framework itself.