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.
Annotation | Type | Required | Description |
---|---|---|---|
AncestorId | @Id | Yes | The previous version in the set. The type of this field will be dictated by the entity’s @Id field. |
AncestorRootId | @Id | Yes | The first version in the set. The type of this field will be dictated by the entity’s @Id field. |
SuccessorId | @Id | Yes | The next version in the set. The type of this field will be dictated by the entity’s @Id field. |
LockOwner | String | No | The name of the lock owner. |
VersionNumber | String | No | The entity’s version number. |
VersionLabel | String | No | The 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
Updated almost 2 years ago