1. Add atoti-server-extension-parent to your pom.xml
For a correct use of Atoti Server Extension, you need to add the atoti-server-extension-parent as a parent in the pom.xml file of your server extension project.
In this section, we show you how to create a custom measure and register it in the Atoti application. The steps are the same for every custom measure not requiring an underlying post-processor.
Create a class that implements the empty interface com.activeviam.atoti.application.api.measures.IMeasureDefinition, this class should contain the arguments needed to compute the measure and optionally, the measure’s plugin key.
Arguments can be of any Java primitive type, or any types of identifier defined in Atoti (e.g. com.activeviam.activepivot.core.intf.api.cube.metadata.LevelIdentifier, com.activeviam.activepivot.core.intf.api.cube.metadata.HierarchyIdentifier, etc.).
/** Arguments for a measure increment its underlying measure. */public record IncrementArguments( String underlyingMeasureName, LevelIdentifier perLevel, long increment) implements IMeasureDefinition { public static final String PLUGIN_KEY = "MY_INCREMENT"; @Override public String getPluginKey() { return PLUGIN_KEY; }}
The arguments passed to the custom measures inside a IMeasureDefinition from Python are of the following types:
Any Java primitive type (e.g. int, double, boolean, etc.)
Once an argument class is defined, you are now able to create a com.activeviam.atoti.server.common.api.plugins.MeasureRegistration object specific to your measure, that will then be used to register the measure in the Atoti application.
You can also specify a formatter when creating your custom measure formula by overriding IFormulaFactory.computeFormatter()
Example
/** Arguments class for multiply by two measure. */public record MultiplyByTwoArguments(String underlyingMeasureName, LevelIdentifier perLevel) implements IMeasureDefinition { public static final String PLUGIN_KEY = "INCREMENT_BY_ONE"; @Override public String getPluginKey() { return PLUGIN_KEY; }}private MeasureRegistration<MultiplyByTwoArguments> createMultiplyByTwoRegistration() { return MeasureRegistration.<MultiplyByTwoArguments>builder() .measureKey(MultiplyByTwoArguments.PLUGIN_KEY) .definitionClass(MultiplyByTwoArguments.class) .factory( new IFormulaFactory<>() { @Override public CopperMeasure buildFormula( final MultiplyByTwoArguments definition, final IFormulaFactoryEnvironment environment) { return environment .getMeasure(definition.underlyingMeasureName()) .getFormula() .multiply(Copper.constant(2)) .per(Copper.level(definition.perLevel())) .sum(); } @Override public Optional<String> computeFormatter( final MultiplyByTwoArguments definition, final IFieldType outputType) { if (outputType.isPrimitive() && outputType.getContentType() == ContentType.DOUBLE) { return Optional.of("DOUBLE[#,###.###]"); } else { throw new IllegalStateException( "Underlying value should be of type double, but got " + outputType); } } }) .build();}
2.3 Register your measure in the Atoti application
Once the measure registration class is ready, you can register your measure in the Atoti application using the com.activeviam.atoti.server.common.api.plugins.IPluginSetup bean.
You can do this in a Spring configuration class by defining a bean that returns a com.activeviam.atoti.server.common.api.plugins.PluginMeasureRegistrations object.
If you need to register multiple measures, they can all be added to the PluginMeasureRegistrations object returned by the contributeMeasures() method of the IPluginSetup bean.Example:
Once this is done, the measure is available on the start of your application.
To know how to use it with Atoti Python SDK, head to the Atoti Python SDK {@doc: “documentation|python://”}.
Don’t forget to import your measures configuration class (the class containing the @Bean method from step 2.3) in your @AutoConfiguration class using the @Import annotation. See section 4 for details.
You can create a custom post-processor by extending any class which already extends AAdvancedPostProcessor.
For example:
Creation of a basic post-processor
/** A post processor that adds one to the aggregated value. */@AtotiExtendedPluginValue(intf = IPostProcessor.class, key = AddOnePostProcessor.PLUGIN_KEY)public class AddOnePostProcessor extends ABasicPostProcessor { public static final String PLUGIN_KEY = "ADD_ONE"; public AddOnePostProcessor( final String name, final IPostProcessorCreationContext creationContext) { super(name, creationContext); } @Override public void init(final Properties properties) throws ActiveViamException { properties.setProperty(OUTPUT_TYPE_PROPERTY, ILiteralType.DOUBLE); super.init(properties); } @Override public void evaluate( final ILocation location, final IRecordReader aggregatedMeasures, final IWritableCell resultCell) { resultCell.writeDouble(aggregatedMeasures.readDouble(0) + 1); } @Override public String getType() { return PLUGIN_KEY; }}
3.2 Create a custom measure relying on the created post-processor
Once the post-processor is created, you can create a measure that relies on it by using very similar code to the one used in the Basic Custom Measure section:
You need to create an argument class that implementing IMeasureDefinition.
/** Arguments */public class AddOneAndMultiplyArguments implements IMeasureDefinition { @Getter private final String underlyingMeasureName; @Getter private final double multiplier; public static final String PLUGIN_KEY = "ADD_ONE_AND_MULTIPLY"; /** Creates a new instance of the arguments. */ public AddOneAndMultiplyArguments(final String underlyingMeasureName, final double multiplier) { this.underlyingMeasureName = underlyingMeasureName; this.multiplier = multiplier; } @Override public String getPluginKey() { return PLUGIN_KEY; }}
You need to create a MeasureRegistration object specifying the new post-processor that will be used to register the measure in the Atoti application, for example:
One the post-processor and the measure are created, you need to manually register the created post-processor in the Atoti application registry:
/** Registers the percentage post processor. */public void registerAddOnePostProcessor() { Registry.getExtendedPlugin(IPostProcessor.class) .add(AddOnePostProcessor.PLUGIN_KEY, "Add one post processor", AddOnePostProcessor.class);}
Finally, you can register the measure in the Atoti application using the IPluginSetup bean as shown in the Basic Custom Measure section. The post-processor must also be registered in the PluginMeasureRegistrations bean definition.
4. Import your measures configuration in your project’s AutoConfiguration
Once you have created your measures configuration class (e.g., ServerExtensionMeasuresConfig), you must import it in your extension’s @AutoConfiguration class to make it available to the Atoti application see.
@AutoConfiguration@Import({ ServerExtensionMeasuresConfig.class, // Import your measures configuration // Add other configuration classes as needed})public class ExtensionAutoConfiguration {}
Without this import, Spring Boot will not register your measures even if they are correctly configured.Once this is done, the measure will be available on the start of your application,
to know how to use it with Atoti Python SDK, head to the Atoti Python SDK {@doc: “plugin_measure documentation|python://api/atoti.function.plugin_measure.html”}.