Skip to main content
Version: 3.8.1

Extensions

Current Versions#

ReferenceVersion
#parent#4.7.50
#process#4.9.4

The code samples do contain the references listed above. Replace the reference with the specified version.

When upgrading, check your existing extensions and update the versions accordingly.

Project Setup#

Start with setting up the maven project configuration:

<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xmlns="http://maven.apache.org/POM/4.0.0"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>    <groupId>com.agosense.symphony.process</groupId>    <artifactId>extension</artifactId>    <packaging>bundle</packaging>    <name>Process Extension</name>    <description>Process Extension</description>    <version>4.7.50</version>
    <parent>        <groupId>com.agosense.pom</groupId>        <artifactId>process</artifactId>        <version>#parent#</version>    </parent>
    <properties>        <custom.imports>            com.agosense.symphony.process.merge        </custom.imports>        <custom.exports>            com.agosense.symphony.process.extension        </custom.exports>    </properties>
    <dependencies>        <dependency>            <groupId>com.agosense.symphony.process</groupId>            <artifactId>merge</artifactId>            <version>#process#</version>            <scope>provided</scope>        </dependency>    </dependencies>
</project>

The package in this example is com.agosense.symphony.process.extension, referenced in custom.exports. When adjusting the package in your implementation, make sure you update the reference.

Create a default folder structure for a maven project and add the package to the source folder.

Extension Class#

The extension is implemented as a single class that provides an Extension style micro service in the applications OSGi environment:

@Component(provides = Extension.class)public class MyExtension implements Extension {

The @Component annotation is mandatory, otherwise the extension will not register properly.

Skip (deprecated)#

The skip method is intended to let you decide if an object synchronization should be skipped. If you return Boolean.TRUE, that is the case. You can base your decision on the sourceObject or the targetObject:

    @Override    public Boolean skip(DataObject sourceObject, DataObject targetObject) {        return Boolean.TRUE;    }
warning

The targetObject can be null in cases where no target object exists yet

note

Skipping of items was introduced when no query filtering was available to enable skipping specific items. Now that query filtering is available the skip extension point is deprecated and subject to be removed in one of the next releases. Please make sure to use a specific query instead matching your extension filter criteria.

MissingTarget#

The missingTarget method is executed when a persistence entry exists for a source issue but the target issue is missing unexpectedly. You can decide what to do in such a situation. One example would be to update a flag on the source object so that it is excluded from the query.

@Overridepublic void missingTarget(    ExtensionContext context,    DataObject sourceObject)     throws AdapterException    {            ImmutableDataObject.Builder flagAttribute = ImmutableDataObject.builder()                    .putAttributes("includeInSync", ImmutableAttribute.builder()                            .value(ImmutableValueBoolean.builder()                                    .value(Boolean.FALSE)                                    .build()).build());
            adapterContext.source().update().accept(flagAttribute.build());    }
note

DataObject is an immutable object, you can not directly change it. Instead use the ImmutableDataObject.builder() to create a new object with your modifications

Create#

The create method is executed short before the application is going to create a new target object. The data object targetCreate is what the application would use to create the new target object. The CreateResult that you provide back should contain your modifications to the targetCreate.

note

DataObject is an immutable object, you can not directly change it. Instead use the ImmutableDataObject.builder() to create a new object with your modifications

    @Override    public CreateResult create(        DataObject sourceObject,         DataObject targetCreate    ) {
        ImmutableDataObject.Builder targetBuilder = ImmutableDataObject.builder()                .putAllAttributes(targetCreate.attributes());
        targetBuilder.putAttributes("MyAttribute", ImmutableAttribute.builder()                .value(ImmutableValueString.builder()                        .value("MyValue")                        .build())                .build());
        return ImmutableCreateResult.builder()            .target(targetBuilder.build())            .build();    }

Extension Context#

The extension context object is injected into the postCreate and update method to provide further flexibility. It contains a reference to the source and the target object through its source and target properties.

Each object reference provides the following properties:

  • url - The base url of the connection
  • id - The identifier of the object

Further more each object reference has an update method that allows to update the corresponding object using a DataObject.

Post Create#

The postCreate method is triggered short after a new target object was created. The extensionContext provided allows you to run updates on either the source or the target object.

    @Override    public void postCreate(        ExtensionContext context,         DataObject sourceObject,         DataObject targetObject    ) {
        LOGGER.info("sourceUrl={}", context.source().url());        LOGGER.info("sourceId={}", context.source().id());
        LOGGER.info("targetUrl={}", context.target().url());        LOGGER.info("targetId={}", context.target().id());    }

Update#

The update method is triggered short before the source and target object updates happen. The extensionContext provided allows you to run intermediate updates on either the source or the target object.

    @Override    public UpdateResult update(        ExtensionContext extensionContext,         DataObject sourceObject,         DataObject targetObject,         DataObject sourceUpdate,         DataObject targetUpdate    ) {        return ImmutableUpdateResult.builder()            .source(sourceUpdate)            .target(targetUpdate)            .build();    }

Name#

The name method is used to identify your extension. You have to make sure that the extension name is unqiue throughout the application.

note

The name is used to render the selection list of extensions in the user interface of the application. Make sure to use a readable and meaningful name.

    @Override    public String name() {        return "MyExtension";    }};

Install Extension#

After you have successfully built your extension, copy it to the bundle/extensions folder and restart the application in order to install it.

Remove Extension#

To remove the extension you simply need to remove the according jar file from the bundle/extensions folder.

note

In case the extension is used in a sync already please unselect in the sync before removing it from the folder.

Update Extension#

First remove the existing version of your extension from the bundle/extensions folder, then copy the new version into that folder.

Workflow Manager#

When specifying state mappings, state transitions can require multiple states to be transitioned in order to reach the target state. To support this use case you can use the workflow manager to define these paths along with mandatory fields per transition. To trigger state transitions before updating the item you could run the update on the workflow object using the input data:

    Workflow workflow = ImmutableWorkflow.builder()                .addTransitions(                        ImmutableTransition.builder().from("In Progress").to("Tested").addAllAttributes(Arrays.asList("MandatoryFieldA","MandatoryFieldA")).build(),                        ImmutableTransition.builder().from("To Do").to("In Progress").build(),                        ImmutableTransition.builder().from("Tested").to("Done").build(),                        ImmutableTransition.builder().from("In Progress").to("Rejected").build()                ).build();
    workflow.updateObject(context.target().update(), targetObject, targetUpdate, targetWorkflow, "Status");                            

targetWorkflow will include all mapped target attributes with activated workflowflag. These should match the mandatory fields specified on the state transitions and will be set when triggering the state updates.