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);
});
// ...
}