Skip to main content

Content Object Storage customization

Content storage alternatives in Flowable

This guide describes how to configure or implement your own storage algorithm for storing Content Items. With the release of Flowable Work v3.15.0+ the new extended concept of ContentStorage was introduced. It is since possible to register multiple ContentStorage implementations and thus allow the integration of multiple custom storage algorithms.

Out of the box content storage

The product comes with four out of the box implementations to store content items.

SimpleFilesystemContentStorage

The SimpleFilesystemContentStorage is a simple implementation of the ContentStorage that relies on the passed metadata to store content items. Under a root folder, a division between task, process and case instances is made. New content gets a new UUID assigned and is placed in one of these folders. The id of the ContentObject indicates in which folder it is stored. Without any custom implementation this is the default behavior of the product. Please be aware, that this behavior will not work in clustered environments with several nodes, as Content Items are stored on the respective node. For clustered environment you may want to set the root-folder to a shared folder between the nodes. You can configure the file system content storage with the following properties:

flowable.content.storage.type=file
flowable.content.storage.root-folder=<storage folder for content items>
flowable.content.storage.create-root=<create root folder>

DatabaseContentStorage

The DatabaseContentStorage stores content items on the database in the table FLW_CO_CONTENT_OBJECT. To configure use of DatabaseContentStorage, you need to set the following property

flowable.content.storage.type=db

AwsS3ContentStorage

v3.12.0+

The AwsS3ContentStorage is a content storage relying on a S3 based data storage. To use it, you need to set the following properties:

flowable.content.storage.type=aws-s3
flowable.aws.region=<aws-region>
flowable.aws.s3.bucket=<aws-bucket-name>

The region property needs to be set to the correct AWS region where the S3 is located. The correct value can be retrieved from the AWS management console.

note

The AWS S3 dependencies are by default added when using Flowable Work/Engage. No need to do anything in that case.

However, if you're building your own Spring Boot project with Flowable dependencies, you'll need to add the flowable-spring-boot-starter-aws-s3 dependency to your pom.xml:

 <dependency>
<groupId>com.flowable.core</groupId>
<artifactId>flowable-spring-boot-starter-aws-s3</artifactId>
</dependency>

It is possible to use the additional property flowable.aws.endpoint-override to overwrite the AWS endpoint. This can be used for testing purposes or in case of using an AWS S3 compatible storage.

Authentication against AWS S3

First, you'll need an AWS account for this. Create an account or log in through the AWS Management console.

To be able to connect to the S3, you'll need credentials to connect to it. There are various ways of accomplishing this. Here we'll follow the best practice (at the time of writing) to use an IAM user with only S3 permissions, an access key id and a secret access key specific to the IAM user.

After logging into the AWS Management console, select your username dropdown at the top-right and click on My Security Credentials. From those screens, create a new group with S3 permissions only. Then create a new user and associate the group with it. Copy the access key access key id and a secret access key or generate one if none exists.

On your local system, create a new folder .aws in your user home. Create a file named credentials in it with following content, replacing the placeholders below with the actual values:

[default]
aws_access_key_id=<access_key_id_value>
aws_secret_access_key=<secret_access_key_value>

AzureBlobStorageContentStorage

v3.12.0+

The AzureBlobStorageContentStorage is a content storage relying on the Azure Blob storage. To use it, you need to set the following properties:

flowable.content.storage.type=azure-blob
flowable.azure.blob.connection-string=<connection-string>
flowable.azure.blob.container-name=<container-name>

First, you'll need an Azure Portal account for this. Create an account or log in through the Microsoft Azure Portal.

In your Azure account, you need to create a storage account. To acquire a connection string and for guidance how to store it please follow the guide in the Azure documentation. Last, you need to create a container and specify the container name.

note

The Azure Blob Storage dependencies are by default added when using Flowable Work/Engage. No need to do anything in that case.

However, if you're building your own Spring Boot project with Flowable dependencies, you'll need to add the flowable-spring-boot-starter-azure-blob-storage dependency to your pom.xml:

 <dependency>
<groupId>com.flowable.core</groupId>
<artifactId>flowable-spring-boot-starter-azure-blob-storage</artifactId>
</dependency>

Custom content storage

Furthermore, you can implement your custom content storage by implementing a class from the interface ContentStorage

import org.flowable.content.api.ContentObject;
import org.flowable.content.api.ContentStorage;

public class MyCustomContentStorage implements ContentStorage {

@Override
@Deprecated
public ContentObject createContentObject(InputStream inputStream, Map<String, Object> map) {
// your custom implementation to create a new content object in your content storage
}

@Override
public ContentObject createContentObject(InputStream contentStream, ContentObjectStorageMetadata metaData) {
// your custom implementation to create a new content object in your content storage
}

@Override
@Deprecated
public ContentObject updateContentObject(String id, InputStream inputStream, Map<String, Object> map) {
// your custom implementation to update a content object in your custom storage
}

@Override
public ContentObject updateContentObject(String id, InputStream contentStream, ContentObjectStorageMetadata metaData) {
// your custom implementation to update a content object in your custom storage
}

@Override
public ContentObject getContentObject(String id) {
// your custom implementation to retrieve your custom content object
}

@Override
public void deleteContentObject(String id) {
// your custom implementation to delete your custom content object
}

@Override
public Map<String, Object> getMetaData() {
// return meta data for your custom content storage
}

@Override
public String getContentStoreName() {
// your custom name of this implementation (is also saved in the database with the content object)
return "my-custom-content-storage";
}

}

The ContentStorage needs to create and return a ContentObject which also needs to be implemented as a custom implementation.

import org.flowable.content.api.ContentObject;

public class MyCustomContentObject implements ContentObject {

@Override
public String getId() {
// return the id of your custom object
}

@Override
public long getContentLength() {
// return the content length of your custom content object
}

@Override
public InputStream getContent() {
// return the content of your content object as input stream.
}

}

Now you can expose the custom implementation as a bean, and the Flowable Content Engine will use it.

@Configuration
public class FlowableContentStorageConfiguration {

@Order(20)
@Bean
public MyCustomContentStorage myCustomContentStorage() {
return new MyCustomContentStorage();
}

}
v3.15.0+
note

You can use the @Order annotation and add multiple custom ContentStorages.

CoreContentStorage

Instead of implementing the ContentStorage, you can also implement the CoreContentStorage interface. That allows you to handle content items depending on its ContentObjectStorageMetadata instead of just the contentStoreId.

public interface CoreContentStorage extends ContentStorage {

boolean supports(ContentObjectStorageMetadata contentObjectStorageMetadata);

}

ContentRenditionManager

Rendition items are the thumbnail and preview PDFs for each content item in Flowable Work. Flowable Work provides a default implementation that creates thumbnails for most of the office formats, images and simple text files. The PDF preview is available for most of the office formats and simple text files. The ContentRendition is the abstract file representation within Flowable.

public class MyCustomContentRenditionManager implements ContentRenditionManager {

@Override
public boolean isPdfRenditionSupported(String mimeType) {
//return true if the provided mimeType is supported for pdf renditions
}

@Override
public boolean isThumbnailRenditionSupported(String mimeType) {
//return true if the provided mimeType is supported for thumbnail renditions
}

@Override
public String getPdfRenditionDefaultMimeType() {
//return the pdf rendition mime type.
}

@Override
public String getThumbnailRenditionDefaultMimeType() {
//return the thumbnail rendition mime type.
}

@Override
public ContentRendition createPdfRendition(String name, InputStream inputStream) {
// your custom implementation to create a new content rendition object in your content storage
}

@Override
public ContentRendition createThumbnailRendition(String name, InputStream inputStream) {
// your custom implementation to create a new content rendition object in your content storage
}
}

At last, we need to provide a configuration and expose the MyCustomContentRenditionManager EngineConfigurationConfigurer. We can extend the FlowableContentStorageConfiguration


@AutoConfiguration(after = {
ContentEngineServicesAutoConfiguration.class
})
public class FlowableContentStorageConfiguration {

@Order(20)
@Bean
public MyCustomContentStorage myCustomContentStorage() {
return new MyCustomContentStorage();
}
@Bean
public EngineConfigurationConfigurer<ContentEngineConfiguration> MyCustomConfigurer(
MyCustomContentEngineConfigurator MyCustomContentEngineConfigurator) {
return engineConfiguration -> engineConfiguration.addConfigurator(MyCustomContentEngineConfigurator);
}
}
public class MyCustomContentEngineConfigurator implements EngineConfigurator {

@Override
public void beforeInit(AbstractEngineConfiguration engineConfiguration) {
ContentEngineConfiguration contentEngineConfiguration = (ContentEngineConfiguration) engineConfiguration;
contentEngineConfiguration.addContentRenditionManager(new MyCustomContentRenditionManager());
}
}