Contact Free trial Login

Multi-Level Metadata

Imagine the case where your end user will use an operation that invokes an action provided by a service. To know the metadata that the action requires as arguments or the metadata of what it returns, you need to differentiate the service from the action to be executed. You do this with a metadata key id. For these cases, the metadata key id does not need to be a single String. It can instead contain multiple parts bundled within a complex parameter that holds the information needed to resolve the metadata.

Implementing a Multi-Level Metadata Key

There must be an order in which parameters of the metadata key are specified, starting with 1. Returning to the example above, the end user will first specify the service field first, then the action to invoke. So, the service parameter will have order 1 and the action parameter order 2.

All the parameters of the POJO representing the MetadataKeyId must be annotated with a @MetadataKeyPart annotation that sets the order value for it. These annotated fields must be of type String.

Here is an example of a metadata key id class:

public class ActionIdentifier{

  @Parameter
  @MetadataKeyPart(order = 1) (1)
  private String service;

  @Parameter
  @MetadataKeyPart(order = 2) (1)
  private String action;

  @Override
  public String getService() {
    return service;
  }

  @Override
  public String getAction() {
    return action;
  }

  @Override
  public void setService(String service) {
    this.service = service;
  }

  @Override
  public void setAction(String action) {
    this.action = action;
  }
}

The next example shows an operation that uses a MetadataKeyId that is a complex type:

@OutputResolver(output = OutputOperationTypeResolver.class)
public Object invoke( @ParameterGroup(name = "Operation") @MetadataKeyId(OperationTypeKeysResolver.class) ActionIdentifier identifier,
                      @TypeResolver(InputOperationTypeResolver.class) Map<String, Object> args){
  return invokeAction(identifier.getService(), identifier.getAction(), args);
}

Next is an example of a TypeKeysResolver that returns each available MetadataKey:

public class OperationTypeKeysResolver implements TypeKeysResolver {

  @Override
  public Set<MetadataKey> getKeys(MetadataContext context) throws MetadataResolvingException, ConnectionException {
    Set<MetadataKey> keys = new HashSet<>();
    for(String service : getServices()){
      MetadataKeyBuilder key = MetadataKeyBuilder.newKey(service);
      for(String action : getActions(service)){
        key.withChild(MetadataKeyBuilder.newKey(action).build());
      }
      keys.add(key.build());
    }
    return keys;
  }
}
public class OutputOperationTypeResolver implements OutputTypeResolver<ActionIdentifier>{

  @Override
  public MetadataType getOutputType(MetadataContext context, ActionIdentifier key)
     throws MetadataResolvingException, ConnectionException {
     // Resolve the output metadata using the ActionIdentifier
  }
}
public class InputOperationTypeResolver implements {

  @Override
  public MetadataType getInputMetadata(MetadataContext context, ActionIdentifier key)
    throws MetadataResolvingException, ConnectionException {
    // Resolve the input metadata using the ActionIdentifier
  }

}

Partial Fetching

Available since version 1.1

The process of getting each MetadataKey can be very time consuming. In the example that gets all the possible services and the actions of each service, the information you are fetching might even be on another server.

Partial fetching enables you to resolve one level of the MetadataKey tree at the time.

The getKeys method from the TypeKeysResolver will only return the first level of each tree, not the complete tree of each MetadataKey.

In this case, the amount of information needed is greatly reduced. You only need to get the possible services. Once a service is selected, you need to provide the next level of the MetadataKey tree.

To make this possible, your key resolver must implement PartialTypeKeysResolver, for example:

public class OperationTypeKeysResolver implements PartialTypeKeysResolver<ActionIdentifier> {

  @Override
  public Set<MetadataKey> getKeys(MetadataContext context) throws MetadataResolvingException, ConnectionException {
    Set<MetadataKey> keys = new HashSet<>();
    for(String service : getServices()){ (1)
      MetadataKeyBuilder key = MetadataKeyBuilder.newKey(service);
      keys.add(key.build());
    }
    return keys;
  }

  @Override
  public MetadataKey resolveChilds(MetadataContext metadataContext, ServiceOperation key)
      throws MetadataResolvingException, ConnectionException {

    if(key.getService() == null){
      throw new MetadataResolvingException("Missing Service name. Cannot resolve Actions without a service",
                                         FailureCode.INVALID_METADATA_KEY);
    }

    MetadataKeyBuilder key = MetadataKeyBuilder.newKey(key.getService()); (2)
    for(String action : getActions(key.getService())){
      key.withChild(MetadataKeyBuilder.newKey(action).build()); (3)
    }
    return key;
  }

}
1 Only the services are retrieved. The actions of a service will be retrieved on demand.
2 Build a single MetadataKey tree with a new, complete level of metadata, in this case, the actions level.
3 Add the actions of that service as children.

Was this article helpful?

💙 Thanks for your feedback!

Edit on GitHub