The ecr-ilm project provides an Information Lifecycle Management (ILM) extension for the arveo Content Repository. It allows managing the lifecycle of documents, specifically those linked via SAP ArchiveLink.

Since ilm is specified on top of webdav, a lot of this documentation repeats the documentation of the arveo webdav extension.

The project is structured into two main parts:

  1. The ILM Extension: Located in the extension submodule, this contains the core logic and reusable components for ILM. Those modules can be used to add ilm functionality to your arveo based project.

  2. The Example Implementation: The rest of the project (backend, frontend, packaging) serves as an example of how to use and integrate the extension to your project.

1. Using the ILM Extension

The ILM extension provides specialized annotations and base interfaces to manage document retention and legal holds. To use this extension, either adapt your existing arveo based service or create a new arveo based service with the archetype.

1.1. Type Definitions

1.1.1. Setup and Registration

The extension/type-definitions module contains the core components for defining ILM-enabled types.

Adding the Dependency

To use the ILM type definitions and the annotation processor in your project, add the following dependency and annotation processor path to the pom.xml of your type definitions module:

    <dependencies>
        <dependency>
            <groupId>de.eitco.ecr.ilm</groupId>
            <artifactId>ecr-ilm-extension-type-definitions</artifactId>
            <version>1.0.0</version>
        </dependency>
        ...
    </dependencies>

    ...
    <build>
        <plugins>
            ...
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>de.eitco.ecr</groupId>
                            <artifactId>ecr-type-definition-processor</artifactId>
                        </path>
                        <path>
                            <groupId>de.eitco.ecr.ilm</groupId>
                            <artifactId>ecr-ilm-extension-type-definitions</artifactId>
                            <version>1.0.0</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>
Type Registration

To make your type definitions available to the arveo Content Repository, you need to register them. The ILM extension provides the de.eitco.ecr.ilm.IlmTypeRegistration interface for this purpose.

In your project, you should create an @AutoConfiguration class that implements IlmTypeRegistration and uses the @Register annotation to list your ILM-enabled types. If you already have a type registration class you may as wall adapt the existing class.

@AutoConfiguration
@Register(MyArchiveLinkDocument.class)
@Register(MyArchiveLinkComponent.class)
@Register(MyIlmContent.class)
@Register(MyIlmProtoContent.class) (1)
public class ArchiveLinkDocumentArchiveLinkComponentTypeRegistration implements IlmTypeRegistration { //⚠️  (2)
}
1 Register your types as before. In this example those are MyArchiveLinkDocument, MyArchiveLinkComponent, MyIlmContent and MyIlmProtoContent.
2 Make your Registration implement IlmTypeRegistration, this enables ilm functionality.
Details of IlmTypeRegistration

IlmTypeRegistration extends DavTypeRegistration and provides several key features:

@PreSchemaInitialization("classpath:liquibase/ilm-liquibase.xml") (1)
public interface IlmTypeRegistration extends DavTypeRegistration {

    @Override
    default Class<?>[] getRegisteredClasses() { (2)

        // ...
    }
}
1 Schema Initialization: It triggers the execution of ILM-specific Liquibase scripts via the @PreSchemaInitialization annotation.
2 It overwrites getRegisteredClasses for two porpoises
  • Automatic Reference Discovery: It automatically discovers and registers all generated ArchiveLink reference types. The annotation processor generates a file named archive-link-reference-types.txt containing the names of all generated types, which is then read by the registration at runtime.

  • Base Type Registration: It ensures that at least one of each IlmCollection and IlmContent are correctly registered in the system.

1.1.2. Defining Types

The ILM extension provides specialized annotations and base interfaces to manage document retention and legal holds.

Base Interfaces

The extension provides several base interfaces that your type definitions should extend to gain ILM functionality:

  • IlmResource: The base interface for all ILM-enabled resources. It defines properties for retention dates, legal holds, and various SAP-specific fields (using the http://www.sap.com/ILM/ namespace).

  • IlmContent: Extends IlmResource and DavContent, used for ILM-enabled document content.

  • IlmCollection: Extends IlmResource and DavCollection, used for ILM-enabled folders/collections.

Defining a simple ILM Content

Beside ArchiveLink, the extension can also be used for any other content that requires ILM functionality. To do so, your type definition should extend IlmContent.

@ShortName("ecr:ilm:example:content")
public interface MyIlmContent extends IlmContent {


    @Mandatory
    @NotNull
    @XmlName(namespace = "http://eitco.de/ecr/ilm/example")
    String getSession();
    void setSession(@NotNull String session);
}

In this example, MyIlmContent extends IlmContent, which provides it with all the necessary ILM properties (retention date, legal hold, etc.). It also adds a custom session property.

Adding ILM functionality to custom arveo types

To add ILM functionality to your custom arveo types, they must extend one of the base interfaces:

  • For document types (which have content), extend IlmContent.

  • For collection/folder types, extend IlmCollection.

By extending these interfaces, your types will automatically inherit the properties required for ILM, such as:

  • retentionDate (from EntityWithIlmRetention)

  • deletionProtected (from EntityWithIlmRetention)

  • litigationHold (from EntityWithLitigationManagement)

  • SAP ILM specific properties like startOfRetention, compulsoryDestructionDate, origin, etc. (from IlmResource)

Type Transition

Type transition is the automatic change of resource types during updates based on its properties. This is necessary since ilm is defined on top of webdav, where objects are created by PUT or MKCOL requests where no meta-data can be specified. If your project defines several arveo types to be accessible by ilm, you need to define how they can be recognized by their meta-data. To do so, implement one or more type transition rules.

Object Creation and Proto Resources

When a new object is created by ilm, it is either a collection resource or content resource (depending on whether it is created by PUT or MKCOL). In that state it will be typed with the according proto resource type.

If you have several ilm collection resource types or ilm content resource types in your project, you need to specify whose are your proto content and proto collection resource. Your proto resources may not have any mandatory fields that do not have default values, since those fields cannot be set in the creation calls. To specify an ilm resource type as proto resource, annotate it with de.eitco.ecr.dav.extensions.type.definitions.ProtoDavResource as the example class de.eitco.ecr.ilm.types.MyIlmProtoContent does:

@ShortName("ecr:ilm:example:proto-content")
@ProtoDavResource
public interface MyIlmProtoContent extends IlmContent {

}

If you only have one ilm collection resource or one ilm content resource you do not need to specify it explicitly as proto resource. Note that it will be assumed to be a proto resource automatically and thus may not have mandatory properties without default values.

Implementing a Type Transition Rule

If you have several ilm content or ilm collection resources you need to implement the interface de.eitco.ecr.dav.extensions.type.definitions.TypeTransitionRule to determine the correct type given an objects properties.

To implement a type transition rule, create a class that implements the TypeTransitionRule interface and register it as a Spring @Component.

public interface TypeTransitionRule {
    @NotNull Optional<TypeTransitionDescription<?>> needsTransition(
            @NotNull Class<? extends MandatoryDavProperties> type,
            @NotNull TypeTransitionCriterion transitionCriterion
    );
}

The needsTransition method should:

  1. Inspect the current properties and the type of the resource.

  2. Determine if a transition is necessary.

  3. If so, return a TypeTransitionDescription specifying the target type and any additional properties or operations (like creating child components) required for the transition.

  4. If the rule determines that the type should not change return a TypeTransitionDescription to the current type

  5. If the rule is indifferent on whether a change should occur, return Optional.empty()

Type transition rules are applied in their spring inversion of control container order.

The fallback type transition

There is a fallback type transition rule. It will only be applied to the proto resources. However, if - on a proto resource - properties are updated so that:

  • exactly the properties of another type are given, or

  • all properties of another type are given, or

  • at least all mandatory properties of another type are given

and the other type is of the same resource type as the proto resource (i.e. collection or content), the entity will be transformed to the other type.

The order of the fallback type transition rule is Ordered.LOWEST_PRECEDENCE / 2 thus it should come last, however, you can still implement a fallback rule for this rule.

Example: ArchiveLinkReferenceTransitionRule

The ArchiveLinkReferenceTransitionRule class in the extension/backend module is a prime example. It checks if a newly created or updated document:

  • Is an instance of IlmContent.

  • Contains al_crep_id and al_doc_id in its properties.

  • Has zero content length.

If these criteria are met, it transitions the document to the appropriate ArchiveLinkReference type and automatically creates the corresponding ArchiveLinkComponentReference objects.

This is actually ilm specification conformant behavior.

Ilm specifies archive link references. An ilm archive link reference is an ilm content resource with

  • zero content length

  • that has the properties al_crep_id and al_doc_id

If this is the case, the al_doc_id holds the id of the referenced archive link document. Any expiration_date and legal_hold properties of the reference need to be applied to referenced archive link document and its components.

This arveo ilm extension takes care of this. Still, you need to specify which of your types are archive link types. This is done with the de.eitco.ecr.ilm.ArchiveLinkType annotation. This annotation marks arveo type definitions as SAP ArchiveLink entities. Mark your archive link document types and archive link component types and the extension will apply the retention constraints.

@Model
@ShortName("ecr:ilm:example:al-document")
@ArchiveLinkType (1)
public interface MyArchiveLinkDocument extends ArchiveLinkDocument { (2)

}

To specify an archive link document:

1 mark the class with @ArchiveLinkType
2 extend from the interface de.eitco.archive.link.ecrtypedef.ArchiveLinkDocument
@Model
@ShortName("ecr:ilm:example:al-component")
@ArchiveLinkType (1)
public interface MyArchiveLinkComponent extends ArchiveLinkComponent { (2)

    @TargetType(MyArchiveLinkDocument.class) (3)
    @Override
    String getDocId();

    @TargetType(MyArchiveLinkDocument.class) (3)
    @Override
    String getRepositoryId();
}

To specify an archive link component:

1 mark the class with @ArchiveLinkType
2 extend from the interface de.eitco.archive.link.ecrtypedef.ArchiveLinkComponent
3 specify archive link document type this component belongs to.
Implementation details

Marking types with the @ArchiveLinkType annotation triggers the ArchiveLinkReferenceTypeFactoryAnnotationProcessor, which automatically generates corresponding reference types. It must be applied manually to each type as inheritance does not trigger the processor.

When a type is annotated with @ArchiveLinkType, the annotation processor generates a reference type (by default with the suffix ArchiveLinkReference). These generated types are used to maintain the relationship between the ILM entities and the arveo Content Repository.

For example, if you define MyArchiveLinkDocument, the processor will generate MyArchiveLinkDocumentArchiveLinkReference.

Your type TypeDefinitionRegistration class - inheriting from IlmTypeRegistration - will register those generated classes automatically. There are two types of archive link reference types:

  • document archive link references de.eitco.ecr.ilm.IlmArchiveLinkReference

  • component archive link references de.eitco.ecr.ilm.IlmArchiveLinkComponentReference

The document archive link reference is an ilm type and thus may have ilm managed retention properties (expiration_date and legal_hold) which is translated to arveo retention properties as for any other ilm resource. It also references its corresponding archive link document with a foreign key configured to cascade on delete. If a request tries to delete the archive link document, this would try to delete the archive link document reference, which will fail if this violates its retention constraints, thus failing the transaction with a retention violation exception.

The component archive link reference builds on top of that: It holds a foreign key to the archive link reference that references the archive link document its referenced archive link component belongs to. This foreign key has two purposes:

  1. It is configured to cascade on delete, thus deleting the references as soon as the archive link document reference is deleted

  2. It is used to inherit the retention properties so that the reference is always in the same retention state as the archive link document reference

Additionally, the archive link component reference has a foreign key to an archive link component. This foreign key is also configured as cascade delete, using the same technique as archive link document references to deny delete requests to the referenced archive link component, if the archive link component reference is under retention or litigation hold.

1.2. Defining the service

To expose the ilm api in your arveo based service add the following dependency to your service module:

        <dependency>
            <groupId>de.eitco.ecr.ilm</groupId>
            <artifactId>ecr-ilm-extension-backend</artifactId>
            <version>1.0.0</version>
        </dependency>

This will register the de.eitco.ecr.dav.extensions.backend.http.WebDavController in your service. It provides a full webdav api, and with this extension this api will be fully ilm compatible.

By default, the endpoint will be provided at /dav/, this can be changed with the ecr-webdav-extension.base-path property.

Adapt your default security configuration accordingly:

security:
  general:
    secured-ant-matchers: "/api/**,/dav/**" (1)
    open-ant-matchers: "/actuator/**,/api/info,/webjars/swagger-ui/**,/public/**,/*,/assets/**"
    role-for-secured-access: ""
    cors-configuration:
      allowed-origins: "*"
      allowed-headers: "*"
      allowed-methods: "GET,POST,PUT,PATCH,DELETE,OPTIONS,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE" (2)
      max-age: 3600
1 secure access to your webdav/ilm endpoint
2 allow the webdav custom http methods
This will add basic auth authentication to your service, since this is a requirement for ilm

1.3. Adapting your ecr package

For full ilm support, you also need to adapt your ecr packaging module. This assures the events needed for webdav and ilm conformance are registered.

In the pom of your packaging/content-repository-service add the following dependency:

        <dependency>
            <groupId>de.eitco.ecr.ilm</groupId>
            <artifactId>ecr-ilm-extension-backend-ecr</artifactId>
            <version>1.0.0</version>
        </dependency>

1.4. TL; DR

2. Project Structure

The project follows a modular structure:

  • extension/: Contains the core ILM logic.

    • extension/api/: API definitions for the ILM extension.

    • extension/backend/: Backend implementation of the ILM logic, including retention and legal hold property handlers.

    • extension/type-definitions/: Annotations and processors for defining ILM-enabled types.

    • extension/xml-types/: XML type definitions for ILM communication.

  • backend/: Example implementation using the extension.

    • backend/type-definitions/: Example arveo type definitions (e.g., MyArchiveLinkDocument, MyArchiveLinkComponent) that use the extension’s annotations.

    • backend/api/: Example service API.

    • backend/implementation/: Implementation of the example service.

  • packaging/: Modules for packaging the application.

    • packaging/service/: Packages the example spring boot service.

    • packaging/content-repository-service/: Packages a customized arveo content repository image including the example type definitions.

  • frontend/: Example web frontend (Angular).

  • test/: System tests for verifying the end-to-end functionality.

2.1. Building the Project

To build the entire project, including the extension and the example implementation, run:

mvn clean install

This will generate:

  • An executable JAR in packaging/service/target.

  • Docker images for the service and the customized content repository.

2.2. Running the Example Environment

You can start a complete test environment (including dependencies like arveo) using the test/system-test module:

cd test/system-test
mvn -Denv

The environment will remain active until you press 'Enter' in the terminal.

2.3. Debugging

To debug the service in your IDE:

  1. Start the environment without the service: mvn -Denv -Dservice.skip.

  2. Run de.eitco.ecr.ilm.service.IlmExampleServiceStandaloneApplication in debug mode from your IDE.

Configuration templates for the test environment can be found in test/system-test/src/test/resource-templates/config.