This content is dedicated to our next version. It is work in progress: its content will evolve until the new version is released.

Before that time, it cannot be considered as official.

Manage documents with code examples using Java APIs

Code examples that illustrate how the Bonita Engine Java APIs let you create, add a new version and attach documents to a Bonita process.

More information

You might want to consult the document documentation first.

A document is treated in a similar way to a variable in Bonita Engine database. It is defined in Bonita Studio or programmatically, and is stored in the Bonita Engine database. Inside a process instance, a document is a byte stream.

Set or update the value of a document in a process operation using a script

In a process operation, you might want to set / update the value of a document data using a groovy script instead of simply use a contract input.
The script should return a DocumentValue .
N.B: All the following examples deal with single document data. For multiple document data, just adapt the scripts to return a list of DocumentInput instead of a document input.

Create a new document

The following groovy scripts create a new document from an existing contract input / url / file.

import java.nio.file.Files

import org.bonitasoft.engine.bpm.contract.FileInputValue
import org.bonitasoft.engine.bpm.document.DocumentValue

// From a contractInput
def DocumentValue createNewDocument(FileInputValue fileFromContract) {
    new DocumentValue(fileFromContract.content, fileFromContract.contentType, fileFromContract.fileName)
}

// From an url
def DocumentValue createNewDocument(String url) {
    new DocumentValue(url)
}

// From an existing file on the fileSystem
def DocumentValue createNewDocument(File file) throws IOException {
    def mimeType = Files.probeContentType(file.toPath())
    new DocumentValue(file.bytes, mimeType, file.name)
}

Update the value of an existing document if a new content is provided

This following script is a way to address the use case of an optional update of the value of a document:
A user might want to change the value of a document, or not. If a new document is uploaded then the value of the data must be updated, if not then the data should be left unchanged.

import java.nio.file.Files

import org.bonitasoft.engine.bpm.contract.FileInputValue
import org.bonitasoft.engine.bpm.document.DocumentValue

// From a contractInput
def DocumentValue optionalUpdateDocument(long documentId, FileInputValue fileFromContract) {
    return fileFromContract
        ? new DocumentValue(documentId, fileFromContract.content, fileFromContract.contentType, fileFromContract.fileName)
        : new DocumentValue(documentId)
}

// From an url
def DocumentValue optionalUpdateDocument(long documentId, String url) {
    return url
        ? new DocumentValue(documentId, url)
        : new DocumentValue(documentId)
}

// From an existing file on the fileSystem
def DocumentValue optionalUpdateDocument(long documentId, File file) throws IOException {
    return file
        ? new DocumentValue(documentId, file.bytes, Files.probeContentType(file.toPath()), file.name)
        : new DocumentValue(documentId)
}

Create a case with documents

The following method, createCaseWithDocument, creates a case and attaches documents to it.

import java.nio.file.Files

import org.bonitasoft.engine.bpm.document.DocumentValue
import org.bonitasoft.engine.exception.BonitaException
import org.bonitasoft.engine.expression.ExpressionBuilder
import org.bonitasoft.engine.operation.OperationBuilder

import com.bonitasoft.engine.api.ProcessAPI

/**
 * In this example, `documents` is a map which link a document data (key) to a file (value)
 * This map will be converted to operations to set document data with DocumentValue
 * We assume in this example that all documents are initialized with Files (i.e contents), it could be URLs!
 */
def createCaseWithDocument(String processDefinitionName,
        String processVersion,
        Map<String, File> documents,
        ProcessAPI processAPI) throws BonitaException {

    def processDefinitionId = processAPI.getProcessDefinitionId(processDefinitionName, processVersion)
    def operations = []
    def listExpressionsContext = [:]

    // ----- create setDocument operations -----
    documents.keySet().each { documentName ->
        def file = documents.get(documentName)
        def mimeType = Files.probeContentType(file.toPath())
        def documentValue = new DocumentValue(file.bytes, mimeType, file.name)
        def expressionName = documentName + "Reference"
        def expression = new ExpressionBuilder().createInputExpression(expressionName, DocumentValue.class.getName())
        def operation = new OperationBuilder().createSetDocument(documentName, expression)
        operations.add(operation)
        listExpressionsContext.put(expressionName, documentValue)
    }

    // ----- start process instance -----
    processAPI.startProcess(processDefinitionId, operations, listExpressionsContext)
}

Attach a document to a case

To attach a first version of a document data to a case, use the attachDocument method of the process API.
To attach a new version of a document data to a case, use the attachDocumentNewDocumentVersion method of the process API.

import java.nio.file.Files

import com.bonitasoft.engine.api.ProcessAPI

// Set the first value of the document `documentName` with the file `document`
// throw an exception if `documentName` has already a value
def attachDocumentToCase(ProcessAPI processAPI, long processInstanceId, String documentName, File document) {
    def mimeType = Files.probeContentType(document.toPath())
    processAPI.attachDocument(processInstanceId, documentName, document.name, mimeType, document.bytes)
}

// Update the value of the document `documentName` with the file `document`
// throw an exception if `documentName` doesn't already have a value
def attachNewDocumentVersionToCase(ProcessAPI processAPI, long processInstanceId, String documentName, File document) {
    def mimeType = Files.probeContentType(document.toPath())
    processAPI.attachNewDocumentVersion(processInstanceId, documentName, document.name, mimeType, document.bytes)
}

Delete content of archived case documents based on archive date

The use case is to free database disk space by deleting the content of archived case documents older than a certain date. However, it keeps metadata attached to the process instance.
searchArchivedDocumentsOlderThanArchivedDate look for archived documents.
deleteArchivedDocumentsOlderThan delete the content of the document.

Although the document binary will be deleted, records will remain in the database. To completely remove the document from the database, delete the process instance to which it is attached, this will delete all elements of the process instance on cascade.
//Search for documents of archived cases with archived date older than "archivedDate"
def SearchResult searchArchivedDocumentsOlderThanArchivedDate(ProcessAPI processAPI, long archivedDate, int startIndex, int maxResults){
	processAPI.searchArchivedDocuments(new SearchOptionsBuilder(startIndex, maxResults).with {
			lessOrEquals(ArchivedDocumentsSearchDescriptor.ARCHIVE_DATE, archivedDate)
			done()
		 })
}

//Delete archived documents older than archivedDate
def deleteArchivedDocumentsOlderThan(ProcessAPI processAPI, long archivedDate) {
	int startIndex = 0
	int maxResults = 100
	def searchResult = searchArchivedDocumentsOlderThanArchivedDate(processAPI, archivedDate, startIndex, maxResults)
	while(searchResult.count > 0){
		searchResult.result.each { archivedDocument ->
			processAPI.deleteContentOfArchivedDocument(archivedDocument.getId())
		}
		startIndex += maxResults
		searchResult = searchArchivedDocumentsOlderThanArchivedDate(processAPI, archivedDate, startIndex, maxResults)
	}
}

//Then just call the method with desired archivedDate
deleteArchivedDocumentsOlderThan(processAPI, archivedDate)