...
- Major version backward compatibility : can 9.0.0 ACM-runtime has to be backward compatible with 8.0.0? How many versions? can ACM-runtime 9.0.0 has to be backward compatible with ACM-runtime 7.0.0?
- Minor version backward compatibility: as example, any 8.x.0 ACM-runtime has to be backward compatible with all ACM-runtime 8.y.0 versions.
- Decouple between ACM-runtime and intermediary library: ACM-runtime 8.x.0 version has to be backward compatible with all intermediary library 8.y.0 versions.
- is not recommended.
What impacts backward compatibility?
...
About the regression tests, the only flows that could be tested will be based on what functionalities support the older version installed for the test.
Java code backward compatibility
Example of refactoring of the interface "AutomationCompositionElementListener " in New Delhi release.
Code Block | ||||
---|---|---|---|---|
| ||||
public record CompositionDto(UUID compositionId,
Map<ToscaConceptIdentifier, Map<String, Object>> inPropertiesMap,
Map<ToscaConceptIdentifier, Map<String, Object>> outPropertiesMap) {
}
public record CompositionElementDto(UUID compositionId, ToscaConceptIdentifier elementDefinitionId,
Map<String, Object> inProperties, Map<String, Object> outProperties) {
}
public record InstanceElementDto(UUID instanceId, UUID elementId, ToscaServiceTemplate toscaServiceTemplateFragment,
Map<String, Object> inProperties, Map<String, Object> outProperties) {
}
/**
* This interface is implemented by participant implementations to receive updates on automation composition elements.
* Valid since New Delhi release.
*/
public interface AutomationCompositionElementListener {
void deploy(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException;
void undeploy(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException;
void lock(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException;
void unlock(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException;
void delete(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException;
void update(CompositionElementDto compositionElement, InstanceElementDto instanceElement,
InstanceElementDto instanceElementUpdated) throws PfModelException;
void prime(CompositionDto composition) throws PfModelException;
void deprime(CompositionDto composition) throws PfModelException;
void handleRestartComposition(CompositionDto composition, AcTypeState state) throws PfModelException;
void handleRestartInstance(CompositionElementDto compositionElement, InstanceElementDto instanceElement,
DeployState deployState, LockState lockState) throws PfModelException;
void migrate(CompositionElementDto compositionElement, CompositionElementDto compositionElementTarget,
InstanceElementDto instanceElement, InstanceElementDto instanceElementMigrate) throws PfModelException;
void migratePrecheck(CompositionElementDto compositionElement, CompositionElementDto compositionElementTarget,
InstanceElementDto instanceElement, InstanceElementDto instanceElementMigrate) throws PfModelException;
}
|
Note: "migratePrecheck" is just an example and it will be not delivered in Q1. That example shown how we can handle new functionality.
Wrapper class "AcElementListenerV1" for participant that was developed in Montreal release
Code Block | ||||
---|---|---|---|---|
| ||||
/** * Wrapper of AutomationCompositionElementListener. * Valid since 7.1.1 release. */ public abstract class AcElementListenerV1 implements AutomationCompositionElementListener { protected final ParticipantIntermediaryApi intermediaryApi; protected AcElementListenerV1(ParticipantIntermediaryApi intermediaryApi) { this.intermediaryApi = intermediaryApi; } @Override public void deploy(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException { var element = new AcElementDeploy(); element.setId(instanceElement.elementId()); element.setDefinition(compositionElement.elementDefinitionId()); element.setToscaServiceTemplateFragment(instanceElement.toscaServiceTemplateFragment()); element.setProperties(instanceElement.inProperties()); Map<String, Object> properties = new HashMap<>(instanceElement.inProperties()); properties.putAll(compositionElement.inProperties()); deploy(instanceElement.instanceId(), element, properties); } public abstract void deploy(UUID instanceId, AcElementDeploy element, Map<String, Object> properties) throws PfModelException; @Override public void undeploy(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException { undeploy(instanceElement.instanceId(), instanceElement.elementId()); } public abstract void undeploy(UUID instanceId, UUID elementId) throws PfModelException; @Override public void lock(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException { lock(instanceElement.instanceId(), instanceElement.elementId()); } public abstract void lock(UUID instanceId, UUID elementId) throws PfModelException; @Override public void unlock(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException { unlock(instanceElement.instanceId(), instanceElement.elementId()); } public abstract void unlock(UUID instanceId, UUID elementId) throws PfModelException; @Override public void delete(CompositionElementDto compositionElement, InstanceElementDto instanceElement) throws PfModelException { delete(instanceElement.instanceId(), instanceElement.elementId()); } public abstract void delete(UUID instanceId, UUID elementId) throws PfModelException; @Override public void update(CompositionElementDto compositionElement, InstanceElementDto instanceElement, InstanceElementDto instanceElementUpdated) throws PfModelException { var element = new AcElementDeploy(); element.setId(instanceElementUpdated.elementId()); element.setDefinition(compositionElement.elementDefinitionId()); element.setProperties(instanceElementUpdated.inProperties()); update(instanceElementUpdated.instanceId(), element, element.getProperties()); } public abstract void update(UUID instanceId, AcElementDeploy element, Map<String, Object> properties) throws PfModelException; private List<AutomationCompositionElementDefinition> createAcElementDefinitionList(CompositionDto composition) { List<AutomationCompositionElementDefinition> elementDefinitionList = new ArrayList<>(); for (var entry : composition.inPropertiesMap().entrySet()) { elementDefinitionList.add(createAcElementDefinition(entry.getKey(), entry.getValue(), composition.outPropertiesMap().get(entry.getKey()))); } return elementDefinitionList; } private AutomationCompositionElementDefinition createAcElementDefinition( ToscaConceptIdentifier toscaConceptIdentifier, Map<String, Object> property, Map<String, Object> outProperties) { var acElementDefinition = new AutomationCompositionElementDefinition(); acElementDefinition.setAcElementDefinitionId(toscaConceptIdentifier); var toscaNodeTemplate = new ToscaNodeTemplate(); toscaNodeTemplate.setProperties(property); acElementDefinition.setAutomationCompositionElementToscaNodeTemplate(toscaNodeTemplate); acElementDefinition.setOutProperties(outProperties); return acElementDefinition; } @Override public void prime(CompositionDto composition) throws PfModelException { prime(composition.compositionId(), createAcElementDefinitionList(composition)); } public abstract void prime(UUID compositionId, List<AutomationCompositionElementDefinition> elementDefinitionList) throws PfModelException; @Override public void deprime(CompositionDto composition) throws PfModelException { deprime(composition.compositionId()); } public abstract void deprime(UUID compositionId) throws PfModelException; @Override public void handleRestartComposition(CompositionDto composition, AcTypeState state) throws PfModelException { handleRestartComposition(composition.compositionId(), createAcElementDefinitionList(composition), state); } //public abstract void handleRestartComposition(UUID compositionId, // List<AutomationCompositionElementDefinition> elementDefinitionList, // AcTypeState state) throws PfModelException; public void handleRestartComposition(UUID compositionId, List<AutomationCompositionElementDefinition> elementDefinitionList, AcTypeState state) throws PfModelException { switch (state) { case PRIMING -> prime(compositionId, elementDefinitionList); case DEPRIMING -> deprime(compositionId); default -> intermediaryApi.updateCompositionState(compositionId, state, StateChangeResult.NO_ERROR, "Restarted"); } } @Override public void handleRestartInstance(CompositionElementDto compositionElement, InstanceElementDto instanceElement, DeployState deployState, LockState lockState) throws PfModelException { var element = new AcElementDeploy(); element.setId(instanceElement.elementId()); element.setDefinition(compositionElement.elementDefinitionId()); element.setProperties(instanceElement.inProperties()); Map<String, Object> properties = new HashMap<>(instanceElement.inProperties()); properties.putAll(compositionElement.inProperties()); handleRestartInstance(instanceElement.instanceId(), element, properties, deployState, lockState); } //public abstract void handleRestartInstance(UUID instanceId, AcElementDeploy element, // Map<String, Object> properties, DeployState deployState, LockState lockState) throws PfModelException; public void handleRestartInstance(UUID instanceId, AcElementDeploy element, Map<String, Object> properties, DeployState deployState, LockState lockState) throws PfModelException { if (DeployState.DEPLOYING.equals(deployState)) { deploy(instanceId, element, properties); return; } if (DeployState.UNDEPLOYING.equals(deployState)) { undeploy(instanceId, element.getId()); return; } if (DeployState.UPDATING.equals(deployState)) { update(instanceId, element, properties); return; } if (DeployState.DELETING.equals(deployState)) { delete(instanceId, element.getId()); return; } if (LockState.LOCKING.equals(lockState)) { lock(instanceId, element.getId()); return; } if (LockState.UNLOCKING.equals(lockState)) { unlock(instanceId, element.getId()); return; } intermediaryApi.updateAutomationCompositionElementState(instanceId, element.getId(), deployState, lockState, StateChangeResult.NO_ERROR, "Restarted"); } @Override public void migrate(CompositionElementDto compositionElement, CompositionElementDto compositionElementTarget, InstanceElementDto instanceElement, InstanceElementDto instanceElementMigrate) throws PfModelException { var element = new AcElementDeploy(); element.setId(instanceElement.elementId()); element.setDefinition(compositionElement.elementDefinitionId()); element.setProperties(instanceElement.inProperties()); migrate(instanceElementMigrate.instanceId(), element, compositionElementTarget.compositionId(), element.getProperties()); } public abstract void migrate(UUID instanceId, AcElementDeploy element, UUID compositionTargetId, Map<String, Object> properties) throws PfModelException; @Override public void migratePrecheck(CompositionElementDto compositionElement, CompositionElementDto compositionElementTarget, InstanceElementDto instanceElement, InstanceElementDto instanceElementMigrate) throws PfModelException { intermediaryApi.updateAutomationCompositionElementState(instanceElementMigrate.instanceId(), instanceElementMigrate.elementId(), DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Migrate precheck not supported"); } } |
Note: "migratePrecheck" is just an example and it will be not delivered in Q1.
Into participant-library will be an abstract class for each version that we would be code compatible; after a specific number of versions we can decide to deprecate the oldest abstract class and remove it in new version.
With that solution a Participant that was developed in Montreal release, could be use the wrapper "AcElementListenerV1" and do not need other changes.
Code Block | ||||
---|---|---|---|---|
| ||||
@Component
public class AutomationCompositionElementHandler extends AcElementListenerV1 {
public AutomationCompositionElementHandler(ParticipantIntermediaryApi intermediaryApi) {
super(intermediaryApi);
}
@Override
public void deploy(UUID automationCompositionId, AcElementDeploy element, Map<String, Object> properties)
throws PfModelException {
---------- |
If participant needs the functionality of New Delhi version, they can use "AcElementListenerV2"
Code Block | ||||
---|---|---|---|---|
| ||||
/**
* Wrapper of AutomationCompositionElementListener.
* Valid since 7.1.2 release.
*/
public abstract class AcElementListenerV2 implements AutomationCompositionElementListener {
private final ParticipantIntermediaryApi intermediaryApi;
protected AcElementListenerV2(ParticipantIntermediaryApi intermediaryApi) {
this.intermediaryApi = intermediaryApi;
}
@Override
public void lock(CompositionElementDto compositionElement, InstanceElementDto instanceElement)
throws PfModelException {
intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
instanceElement.elementId(), null, LockState.LOCKED, StateChangeResult.NO_ERROR, "Locked");
}
@Override
public void unlock(CompositionElementDto compositionElement, InstanceElementDto instanceElement)
throws PfModelException {
intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(),
instanceElement.elementId(), null, LockState.UNLOCKED, StateChangeResult.NO_ERROR, "Unlocked");
}
@Override
public void handleRestartComposition(CompositionDto composition, AcTypeState state) throws PfModelException {
switch (state) {
case PRIMING -> prime(composition);
case DEPRIMING -> deprime(composition);
default ->
intermediaryApi.updateCompositionState(composition.compositionId(), state, StateChangeResult.NO_ERROR, "Restarted");
}
}
@Override
public void handleRestartInstance(CompositionElementDto compositionElement, InstanceElementDto instanceElement,
DeployState deployState, LockState lockState) throws PfModelException {
if (DeployState.DEPLOYING.equals(deployState)) {
deploy(compositionElement, instanceElement);
return;
}
if (DeployState.UNDEPLOYING.equals(deployState)) {
undeploy(compositionElement, instanceElement);
return;
}
if (DeployState.UPDATING.equals(deployState)) {
update(compositionElement, instanceElement, instanceElement);
return;
}
if (DeployState.DELETING.equals(deployState)) {
delete(compositionElement, instanceElement);
return;
}
if (LockState.LOCKING.equals(lockState)) {
lock(compositionElement, instanceElement);
return;
}
if (LockState.UNLOCKING.equals(lockState)) {
unlock(compositionElement, instanceElement);
return;
}
intermediaryApi.updateAutomationCompositionElementState(instanceElement.instanceId(), instanceElement.elementId(),
deployState, lockState, StateChangeResult.NO_ERROR, "Restarted");
}
} |