The ecr-webdav project provides a webdav extension for the arveo Content Repository. It allows accessing document and folder types by a webdav api.

The project is structured into two main parts:

  1. The Webdav Extension: Located in the extension submodule, this contains the core logic and reusable components for webdav. Those modules can be used to add webdav 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.

The webdav extension provides specialized annotations and base interfaces to manage document and folder access by webdav api. To use this extension, either adapt your existing arveo based service or create a new arveo-based service with the archetype.

1. Type Definitions

1.1. Setup and Registration

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

1.1.1. Adding the Dependency

To use the webdav type definitions, add the following dependency to the pom.xml of your type definitions module:

    <dependencies>
        <dependency>
            <groupId>de.eitco.ecr.dav</groupId>
            <artifactId>ecr-webdav-extension-type-definitions</artifactId>
            <version>2.0.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

1.1.2. Type Registration

To make your type definitions available to the arveo Content Repository, you need to register them. The webdav extension provides the de.eitco.ecr.dav.extensions.type.definitions.DavTypeRegistration interface for this purpose.

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

@AutoConfiguration
@Register(MyDavCollection.class)
@Register(MyDavContent1.class)
@Register(MyDavContent2.class)
@Register(MyDavProtoContent.class)
public class MyDavTypeRegistration implements DavTypeRegistration {
}

1.1.3. Details of DavTypeRegistration

DavTypeRegistration provides several key features:

@PreSchemaInitialization("classpath:liquibase/webdav-liquibase.xml") (1)
public interface DavTypeRegistration extends TypeDefinitionRegistration { (2)

    @Override
    default Class<?>[] getRegisteredClasses() { (3)
        // ...
    }
}
1 Schema Initialization: It triggers the execution of webdav-specific Liquibase scripts via the @PreSchemaInitialization annotation.
2 Base Type Registration: It ensures that at least on of each DavCollection and DavContent are correctly registered in the system.

1.2. Defining Types

The webdav extension provides specialized annotations and base interfaces to access documents and folders by webdav api.

1.2.1. Base Interfaces

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

  • de.eitco.ecr.dav.extensions.type.definitions.MandatoryDavProperties: The base interface for all webdav-enabled resources. It defines the properties an arveo type definition needs to implement a dav resource e.g. dav live properties.

  • de.eitco.ecr.dav.extensions.type.definitions.DavContent: Extends MandatoryDavProperties and works as base interface for all dav content resource type definitions.

  • de.eitco.ecr.dav.extensions.type.definitions.DavCollection: Extends MandatoryDavProperties and works as base interface for all dav collection resource type definitions

Defining a simple webdav content

The extension can be used for any content that requires webdav functionality. To do so, your type definition should extend DavContent.

This way it will have all relevant webdav live properties. All other webdav properties ("dead" properties) will - by default - be stored as arveo variables. The variable name will be the properties xml namespace followed by a pipe (|) and its element name, the variable value will be a text representation of the properties xml value.

This way any arbitrary property can be set at runtime. The downside of storing the properties as variables is that searches have fewer features and database indices will be more complex. If you have a specific set of properties you expect on every member of a given type, you can specify them as arveo properties in your type. The webdav extension will automatically expose them as webdav "dead" properties. You may specify the properties' webdav specific name using the annotation de.eitco.ecr.dav.extensions.type.definitions.XmlName. If omitted the namespace of a property defaults to http://dav.eitco.ecr.de/index-data/<type-name> and the element name to its field name.

@ShortName("my:dav-content-1")
public interface MyDavContent1 extends DavContent { (1)

    @ForeignKey(
            target = MyDavCollection.class, (2)
            targetProperty = "id"
    )
    @Override
    FolderId getContainer();

    Integer getArbitraryInteger(); (3)
    void setArbitraryInteger(Integer arbitraryInteger);

    @XmlName(namespace = "https://content.example.com/example/name/space", elementName = "scale") (4)
    Double getArbitraryDouble();
    void setArbitraryDouble(Double arbitraryDouble);

}

In this example,

1 MyDavContent1 extends DavContent, which provides it with all the necessary webdav properties (file name, content type etc.).
2 It then specifies, that it can only be part of a collection of the type MyDavCollection
3 It adds the custom property arbitrary_integer, which will be represent on the dav api as <arbritrary_integer xmlns="\http://dav.eitco.ecr.de/index-data/my_dav_content1">[integer value]</arbritrary_integer>
4 It also adds the custom property arbitrary_double, which will be represented as <scale xmlns="\https://content.example.com/example/name/space">[double value]</scale>

1.2.2. Type Transition

Type transition is the automatic change of resource types during updates based on its properties. This is necessary since in webdav 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 webdav, you need to define how they can be recognized by their meta-data. To do so, implement one or more type transition rules.

1.2.3. Object Creation and Proto Resources

When a new object is created by webdav, 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 dav collection resource types or dav 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 a dav resource type as proto resource, annotate it with de.eitco.ecr.dav.extensions.type.definitions.ProtoDavResource as the example class de.eitco.ecr.dav.types.MyDavProtoContent does:

@ShortName("my:dav-proto-content")
@ProtoDavResource (1)
public interface MyDavProtoContent extends DavContent { (2)

    @ForeignKey(
            target = MyDavCollection.class,
            targetProperty = "id"
    )
    @Override
    FolderId getContainer();

}
1 adding the annotation marks the type definition as proto resource
2 since it extends DavContent it is the proto content resource

If you only have one dav collection resource or one dav 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 dav content or dav 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.

2. Defining the service

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

        <dependency>
            <groupId>de.eitco.ecr.dav</groupId>
            <artifactId>ecr-webdav-extension-backend</artifactId>
            <version>2.0.0-SNAPSHOT</version>
        </dependency>

This will register the de.eitco.ecr.dav.extensions.backend.http.WebDavController in your service. It provides a full webdav api.

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 endpoint
2 allow the webdav custom http methods

3. Adapting your ecr package

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

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

        <dependency>
            <groupId>de.eitco.ecr.dav</groupId>
            <artifactId>ecr-webdav-extension-backend-ecr</artifactId>
            <version>2.0.0-SNAPSHOT</version>
        </dependency>

4. TL; DR