Bean services for post-processors

Overview

The Business Solution post-processors use external Spring bean resources to achieve specific client-customizable services.

For instance, the FX conversion post-processors use an FX conversion bean.

To be fully customizable, the services must show an interface with this kind of signature :

double getMyStuff(
        IActivePivot pivot,
        IDatastoreVersion datastoreVersion,
        IQueryCache cache,
        // Some service specific parameters,
        List<Object> leafCoordinates);

With the following parameters :

Name Type Content
pivot IActivePivot The Atoti Server instance used by the post-processor
datastoreVersion IDatastoreVersion The datastore version, must be retrieved by “getDatastoreVersion()” inside the post-processor context to get the right version
cache IQueryCache The query cache retrieved by “getQueryCache()”
All the useful parameters needed to achieve the service
leafCoordinates List<Object> A list of user-customizable values that mainly represent a meaningful location in the user context that will be used as extra parameters to achieve the service

Legacy Post-processors

Automatic injection

To inject the service in all the post-processors that require it, use the following setup (the tenorUtil bean as an example):

Bean creation

@Bean
public ITenorUtil tenorUtil() {
    // ...
    return new TenorUtils(storeDescriptionMap, TENOR_SET_DEFAULT);
}

Bean interface

public interface ITenorUtilAware {

String PROPERTY_NAME = "tenorUtil";

/**
 * Set the implementation of {@link ITenorUtil}
 * @param tenorUtil the implementation of {@link ITenorUtil}
 */
void setTenorUtil(ITenorUtil tenorUtil);

}

This interface is used as a vehicle to inject the defined bean singleton into all the QFS plugins that require it.

warning

Ensure that you name the different pieces of the interface correctly as this is crucial for it to work properly.

Post-processor and other Atoti plugins implementation

@QuartetExtendedPluginValue(intf = IPostProcessor.class, key = DynamicTenorsAndMaturitiesPostProcessor.PLUGIN_KEY)
public class DynamicTenorsAndMaturitiesPostProcessor extends AAdvancedPostProcessor<Double> implements ITenorUtilAware, ICustomParametersAware {

    protected ITenorUtil tenorUtil;

    @Override
    public void setTenorUtil(ITenorUtil tenorUtil) {
        this.tenorUtil = tenorUtil;
    }
}

Injection

Call this code inside the “startManager” bean :

@Autowired private ITenorUtil tenorUtil;

public void apManagerInitPrerequisitePluginInjections() {
    // ...
    PostProcessorUtils.injectAll(IPostProcessor.class, ITenorUtilAware.class, tenorUtil);
    // ...
}

This injects the tenorUtil in all plugins of type IPostProcessor. The injection also work with other kinds of plugin.

LeafCoordinates handling

To compute the leaf coordinates, a specific service has been developed that is intended to return something useful when used by the client.

Post-processor implementation

@QuartetExtendedPluginValue(intf = IPostProcessor.class, key = DynamicTenorsAndMaturitiesPostProcessor.PLUGIN_KEY)
public class DynamicTenorsAndMaturitiesPostProcessor extends AAdvancedPostProcessor<Double> implements ITenorUtilAware, ICustomParametersAware {

    protected ICustomParameters customParameters;
    protected ILocationFunction locationFunction;
    protected ITenorUtil tenorUtil; // Or another service

    @Override
    public void init(Properties properties) throws ActiveViamException {
        locationFunction = (customParameters != null) ? customParameters.getLeafCoordinatesFunction(pivot, properties, getType()) : null;
        var orgLeafs = PostProcessorUtils.registerExtraLeafs(locationFunction, pivot, properties);
        super.init(properties);

        // ...
        // orgLeaf contains the leaves passed to the post-processor in its properties

        // ...
    }

    @Override
    public Double evaluate(ILocation location, Object[] underlyingMeasures) {
        // ...
        final List<Object> leafCoordinates = locationFunction.apply(location, getQueryCache());
        // ...
        Set<String> bucketSets = tenorUtil.getTenorSets(getActivePivot(), getDatastoreVersion(), getQueryCache(), bucketType, leafCoordinates);
        // ...
    }

    // Or another service
    @Override
    public void setTenorUtil(ITenorUtil tenorUtil) {
        this.tenorUtil = tenorUtil;
    }

    @Override
    public void setCustomParameters(ICustomParameters customParameters) {
        this.customParameters = customParameters;
    }
}

Bean implementation

The implementation should be injected inside all post-processors as usual.

Default solution implementation

This implementation is extremely basic and does nothing:

@Bean
ICustomParameters customParameters() {
    return (pivot, properties, ppPluginKey) -> (location, cache) -> List.of();
}
User custom implementation
@Bean
ICustomParameters customParameters() {
    return (pivot, properties, ppPluginKey) ->
            switch(ppPluginKey) {
                case DynamicTenorsAndMaturitiesPostProcessor.PLUGIN_KEY -> {
                    final ILevelInfo extraLevelInfo = HierarchiesUtil.getLevel(pivot, properties.getProperty(EXTRA_NAME_LEVEL)).getLevelInfo();
                    yield (location, cache) -> List.of(LocationUtil.getCoordinate(location, extraLevelInfo));
                }
                default -> (location, cache) -> List.of();
            };
}

Copper

To call a service from the copper API we need to have a reference to Atoti Server, Datastore and Query Cache. So there is a specific development to expose these objects : “CopperToService”.

Example :

@Autowired private ITenorUtil tenorUtil;
@Autowired private ICustomParameters customParameters;

void copperMeasures(ICopperContext context, CopperMeasure underlyingMeasure) {
    CopperMeasure measureWithServiceCall = CopperToService.combine(underlyingMeasure)
        .withLeaf(activePivot -> customParameters.getLeafCoordinatesFunction(activePivot, extraProperties, PLUGIN_KEY))
        .map((activePivot, datastore, cache, leafCoordinates, reader, writer) -> {
            Object underlying = reader.read(0); // Underlying values can be retrieved as usual
            // Here is the service call
            Set<String> bucketSets = tenorUtil.getTenorSets(activePivot, datastore, cache, bucketType, leafCoordinates);
            // ...
            writer.write(result);
        });
// ...
}

See also