Document Management API

fastCode’s document management API provides the ability to store, lock, and version documents.

Many enterprise applications have a need to manage documents. For example, an email may be sent by an application’s user that contains file attachments that need to be stored for audit purposes. The document management API provides you a configurable and easy to use API to store documents in locations, including, but not limited to a database. It also allows you to pessimistically lock and version documents.

Document storage mechanisms

When a document (file) is associated with one of the attributes of a Java Persistence API (JPA) entity, we can use @Lob annotation to store the document. The @Lob annotation specifies that a persistent property or field should be persisted as a large object to a database-supported large object type. A @Lob may be either a binary or character type. The Lob type is inferred from the type of the persistent field or property, and except for string and character-based types, it defaults to Blob.

@Lob is problematic because (1) it forces you to store your documents in the database which isn’t necessarily where you want to store them. For example, the file-system or AWS S3 buckets might be better. (2) Secondly, when using @Lob to store byte[], it may cause an OutOfMemoryException if the document is very large.

With the current version of the document management API, we are providing you support for storing documents on the file-system. In future releases, we will support (a) Amazon S3 buckets as a storage mechanism and (b) database as a storage mechanism by streaming the document from the client to the database without being loaded into the memory, eliminating the issue of OutOfMemoryException.

Locking and versioning documents

In some use cases, you may need the capability to lock a document, make changes to the locked document, and then version the changed document as a new version of the original document. These capabilities are not provided out-of-the-box by JPA, but fastCode’s Document Management API provides these capabilities.

API Specification

In order to use the document management API, we first need to define a File entity that will hold the metadata associated with a document.

@Entity
@Table(name = "file")
@Getter
@Setter
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@NoArgsConstructor

public class FileEntity extends AbstractEntity {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    @EqualsAndHashCode.Include
    private Long id;

    @Basic
    @Column(name = "name", nullable = true)
    private String name;

    @Basic
    @Column(name = "summary", nullable = true)
    private String summary;

    @Basic
    @Column(name = "created", nullable = true)
    private Date created;

    @Basic
    @Column(name = "content_id", nullable = true, length = 255)
    @ContentId
    private String contentId;

    @Basic
    @Column(name = "mime_type", nullable = true, length = 255)
    @MimeType
    private String mimeType;

    @Basic
    @Column(name = "content_length", nullable = true)
    @ContentLength
    private Long contentLength;

    }

All the fields of this entity, except for the primary key, are optional, but the
File entity won’t represent a document if we create it without the optional
fields that represent the document metadata.

Document Storage API Methods

Note: Ensure that the application.properties file has the following property
set for the file upload location.

spring.content.fs.filesystem-root=src/main/java

The first set of ReST methods are used to perform C.R.U.D. operations on the
File entity.

File entity Operations

Create a File entity

API Reference:
https://docs.getfastcode.com/reference/create17

Update a File entity

API Reference:
https://docs.getfastcode.com/reference/update17

Delete a File entity

API Reference:
https://docs.getfastcode.com/reference/delete17

Find a File entity by its Id

API Reference:
https://docs.getfastcode.com/reference/findbyid17

Search & Find File entities

API Reference:
https://docs.getfastcode.com/reference/find15

Content Operations

Once a File entity is created, we would want to typically associate a document
with this file entity. The next set of methods allow you to perform content
operations.

Associate a document with a File entity

API Reference:
https://docs.getfastcode.com/reference/setcontent

Un-associate a document that’s associated with a File entity

API Reference:
https://docs.getfastcode.com/reference/unsetcontent

Get the document that’s associated with the File entity

API reference:
https://docs.getfastcode.com/reference/getcontent

Document Locking & Versioning - Introduction

Java Persistence API (JPA) supports both optimistic and pessimistic locking.

Pessimistic locking mechanism involves locking entities on the database
level.

In case of pessimistic locking, JPA creates a transaction that obtains a lock on
the entity until the transaction is completed. This prevents other transactions
from making any updates to the entity until the lock is released. Using
pessimistic locking may result in deadlocks. However, it ensures greater
integrity of data than optimistic locking. Pessimistic locking should be used
for applications that are update/delete heavy and not read-heavy.

Optimistic locking is based on detecting changes on entities by checking their
version attribute
. If any concurrent update takes place,
OptmisticLockException occurs. After that, we can retry updating the data.

Optimistic locking is suitable for applications which are more read-heavy and
not update/delete heavy.

To implement optimistic locking, the persistence provider provides
javax.persistence.Version annotation to mark a property as a version attribute
of an entity. This property can be a numeric (int, long, short) or
java.sql.Timestamp field. Only one property with @Version annotation can exist
per entity. Also, the @Version attribute must be in the primary table for an
entity mapped to multiple tables.

@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Getter
@Setter
public class File implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    @EqualsAndHashCode.Include
    private Long id;
    
    @Version
    @EqualsAndHashCode.Include
    @Column(name = "VERSION", nullable = false)
    private Long version;
}

Here, each transaction that reads data holds the value of the version property.
Before any transaction modifies the underlying data, it checks the value of
version property again. If the value has changed in the meantime, an
OptimisticLockException will be thrown and the transaction will be rolled back.
Otherwise, the transaction commits the update and increments the value of
version property.

In fastCode, we represent a document as a File entity. In addition to supporting
JPA’s optimistic locking mechanism, we also support pessimistic locking. To
implement pessimistic locking of a File entity, we define it as follows

@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Getter
@Setter
public class File implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    @EqualsAndHashCode.Include
    private Long id;

    @Version
    @EqualsAndHashCode.Include
    @Column(name = "VERSION", nullable = false)
    private Long version;

    @Column
    private String name;

    @Column
    private Date created = new Date();

    @Column
    private String summary;

    @Column
    @ContentId
    private String contentId;

    @Column
    @ContentLength
    private long contentLength;

    @Column
    @MimeType
    private String mimeType;

    @Column
    @LockOwner
    private String lockOwner;

    @Column
    @AncestorId
    private Long ancestorId;

    @Column
    @AncestorRootId
    private Long ancestralRootId;

    @Column
    @SuccessorId
    private Long successorId;

    @Column
    @VersionNumber
    private String version;

    @Column
    @VersionLabel
    private String label;

}

Lock and version information is recorded on each entity instance by adding the
following attribute annotations to the entity class.

AnnotationTypeRequiredDescription
AncestorId@IdYesThe previous version in the set. The type of this field will be dictated by the entity’s @Id field.
AncestorRootId@IdYesThe first version in the set. The type of this field will be dictated by the entity’s @Id field.
SuccessorId@IdYesThe next version in the set. The type of this field will be dictated by the entity’s @Id field.
LockOwnerStringNoThe name of the lock owner.
VersionNumberStringNoThe entity’s version number.
VersionLabelStringNoThe entity’s version label.

Document Locking & Versioning - ReST API Methods

In fastCode’s document management API, before a File entity can be versioned, it
has to be pessimistically locked to prevent other users from updating it.

Acquires a pessimistic lock on the entity

API Reference:
https://docs.getfastcode.com/reference/lock

If held, release the pessimistic lock on the entity

API Reference:
https://docs.getfastcode.com/reference/unlock

Create a new version an entity that holds a pessimistic lock

API reference:
https://docs.getfastcode.com/reference/version

The new version will not have a new ContentId as the ContentId should be the
same for all versions.

Return all versions of an entity

API Reference:
https://docs.getfastcode.com/reference/findallversions

Return the latest versions of all entities

API Reference:
https://docs.getfastcode.com/reference/findallversionslatest