Table of Contents
The Activiti Enterprise BPM solution comes with a number of preconfigured Activities that can be used to communicate with the Alfresco ECM system. The following picture shows what features are available:
These are basically specialized Service Task implementations that talk to the Alfresco content management system. In this article we will have a look at how to use them to:
- Fetch content (files) from Alfresco and use in the workflow
- Upload content to Alfresco from the workflow
- Fetch properties for content stored in Alfresco
- Update properties for content stored in Alfresco
- Apply a Repo Action to a file stored in Alfresco
So why would you want to store content in Alfresco in the first place? It is useful to consolidate all content used in the Enterprise into one place instead of having it spread around in each enterprise application and system in use. This makes it easy to search for it, work with it, reuse it, and make it auditable (records management).
Source code for the Activiti Developer Series can be found here.
There is no need to set up an extension project when working with the Alfresco integration, it is all done via the Activiti Application UI.
Running Alfresco and Activiti alongside each other
To follow along with this article we need to have both the Activiti server and the Alfresco server running locally. I will assume that you already have an Activiti Enterprise server running locally. But if you are working mostly with Activiti, then you probably don’t have an Alfresco ECM installation available. If that is the case, then the easiest way to install Alfresco ECM is with the full installer, which you can download from here.
Now, by default both the Activiti Tomcat server and the Alfresco Tomcat server listens on the same ports (i.e. 8080, 8005, 8009). So if you have the Activiti server running then stop it as follows:
martin@gravitonian:/opt/activiti15$ /opt/activiti15/tomcat/bin/catalina.sh stop
Then open up the <activiti-install-dir>/tomcat/conf/server.xml file and change the port as follows:
<Server port="9005" shutdown="SHUTDOWN">
<Connector port="9080" protocol="HTTP/1.1"
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="9009" protocol="AJP/1.3" redirectPort="8443" />
So what we are doing here is changing the ports as follows:
- 8005 -> 9005
- 8080 -> 9080
- 8009 -> 9009
Now restart Activiti:
Create a site in Alfresco for Activiti content
When uploading content files to Alfresco Repository from an Activiti workflow instance they need to go into a so called Site. So we will create a separate site to be used in this article. Because we are going to work with “case” files we will create a case management site as follows:
Here we are logged in to Alfresco as the admin user. The Alfresco account/user that will be used from Activiti need to have read and write access to this site, so we will be using the Alfresco admin user from Activiti.
Configure Alfresco Repository Location
Before we can do any type of integration with the Alfresco ECM system we need to tell Activiti about where to find the Alfresco Content Repository. This is done via the Identity Management application (note. you need to be logged in as admin):
Click on this application so you see this screen:
Now click on the Tenants menu item, this takes you to this screen:
Here click on Alfresco Repositories followed by a click on the + sign to the right to add a new repository location (note. you can have multiple repository locations configured):
Now, set up the repository parameters for your local Alfresco server. We are not using an Alfresco Share connector so you can leave that check-box disabled (it is used when you want to start and manage your processes and tasks from Alfresco Share, read more about it here).
Then click Save.
This repository configuration can now be used by Activiti when using any of the Activities mentioned in the introduction. It will also be available when using Attachment fields in the form designer.
Give Activiti Users Access to Alfresco Repository
You probably noted that when we told Activiti about the location of the Alfresco Repository we did not provide a username and password. So it is not enough to just tell Activiti about the Alfresco Repository location, we also need to give an Activiti user access to it. This user will be the one executing the Alfresco Activities. In our case we will use the Administrator user for this.
Navigate to the Profile for the Activiti user, either via the Profile App, or the Identity Management App and the Personal menu item, so you see something like this:
Click on the Alfresco Repository you want to set up access to, such as Alfresco 5.2 Local in this case:
Specify the Alfresco username and password that should be used for accessing the Repository as this Activiti user. The Administrator username and password is specified during installation of Alfresco ECM, it is common to use admin/admin for testing.
The admin user was used to create the Case Management site in Alfresco so it will have full access to the site when uploading files from Activiti. If we use another Alfresco user here, then we must make sure that it has read and write access to the site.
Uploading files to Alfresco
At some point in a workflow we might want to upload and store files in Alfresco. For example, let’s say you are processing a case file of some sort and it is now complete, and you want to store the file so it is easily accessible. We can do this with the “Publish to Alfresco” task:
However, we need a file attached to the workflow before we can store anything in Alfresco. Create a Start form:
The form should look like this:
The form starts off with two fields with Case information. This is just so we have some simulated metadata to apply to the “case” files. The Attach field for File A is configured as follows:
In this case we are allowing the user to only pick files from the local disk. It is possible to also allow the user to pick only from Alfresco, or from both Alfresco and the local file system. For File B we will allow picking either from local file system or from the Alfresco Repository:
Now save the form and go back to BPMN Editor.
We now have some files to upload to Alfresco via the Publish to Alfresco task, click on it so you can see the properties section:
Start by configuring which Alfresco Repository we want to upload to and what Site we want to use, click on the Alfresco destination property:
The properties have the following meaning:
- Account: This is the Alfresco Repository that we want to store the files in
- Destination: This is the folder where we want to store the files. in this case I have selected the Case Management site created initially in this article and the documentLibrary folder (this is the root folder for documents in a site)
- Publish as: This is the Activiti user account that should be used to connect to Alfresco Repository, it contains the Alfresco username and password to use when connecting.
- Subfolder & Based on field: This is an interesting property as it can be used to created sub-folders dynamically. In our case we want a new subfolder in the document library created for every case, we can achieve this by creating the subfolder based on the Case Name property set at the start of the workflow.
Now save the Alfresco destination configuration.
Using an out-of-the-box content model
Next we need to configure what files, out of those attached to the workflow instance, that we want to upload to Alfresco, and what metadata that should be applied to those files. Click on the Alfresco content property for the task:
Here we can choose between storing all files attached to the workflow, or just one individual file. We select to publish all files attached (i.e. Publish all content uploaded in process), basically File A and File B as set up in the Start form.
When we store the files in Alfresco it needs to know what type they should have and what properties (metadata) that should be applied. Properties are divided into groups called Aspects but they are also part of the Type definition. We can select what properties to supply by adding and removing aspects. Both the type and the aspects need to be part of a Content Model deployed to the Alfresco Repository.
Now, you might say, I know that the cm:content type is the default type for a file uploaded to Alfresco, but I don’t know what the cmis:document type is? Activiti uses the CMIS standard when it communicates with Alfresco. CMIS defines its own domain model with types and properties, and it calls aspects Secondary Types. The corresponding CMIS type for the Alfresco type cm:content is called cmis:document.
We want to have the Case ID and Case Name properties applied as metadata on the uploaded file. This can be done by specifying an aspect with properties that can be used to hold the Case ID and Name. In this first example we are using the standard out-of-the-box content model, and it has an aspect called cm:titled that we can use for demonstration purpose.
This aspect has two properties where we can store the Case ID and Name, click on the Properties mapping tab to set up properties for the configured aspects:
There is no automatic lookup of what properties that are part of the cm:titled aspect, you need to manually find this out, see the out-of-the-box content model, it has this definition for this aspect:
Here we are mapping the cm:title property to store the Case ID form property and the cm:description to store the Case Name property. It would of course have been more clear if I had used something like a cm:case aspect, but that is not available out-of-the-box. So let’s first see how this works.
Important!. There is one out-of-the-box aspect called cm:auditable that cannot be used by the “Publish to Alfresco” task. This is because its properties are completely managed by the Alfresco server and cannot be set by the end-user.
Now, save the content configuration.
If we add an End State to the workflow definition we should now be able to test it. For this you need to also create an Activiti Application that will hold the process model. See this info for how to do that.
If we now start a workflow instance based on this definition we will be presented with the following start form:
Note here that only the “File B” Attach field have the Alfresco Logo. This is because we configured “File A” Attach field to only be able to select files from the local file system. So for File B we can select from either the local file system (by clicking on the SELECT A FILE button) or from a Site in Alfresco (by clicking on the Alfresco Logo). Note that you can only pick files from a site in Alfresco, not from other places such as for example /Company Home/Guest Home.
Now fill in the Case ID and Number and select two files from the local filesystem, by clicking on the SELECT A FILE button, you should see something like this now:
Then click START PROCESS. The process should complete and if we jump over to Alfresco Share and the Case Management site’s document library we should see the following files in the newly created Case Number One folder:
We can see that the folder has been created properly and the files have had the cm:titled aspect applied with the Case ID and Name.
So this is quite handy, you can get specific case, or project, folders created dynamically on the fly, with metadata applied to the contained files, making it easier to find them.
Note. if the workflow instance does not complete after the “Publish to Alfresco” task, then it might have to do with uploading an image and having the Media Management Module installed in Alfresco, see this bug report.
Using a custom content model
In the previous example we used a type and an aspect from an out-of-the-box content model. In a real world scenario it is more likely that we will have to use a domain specific custom content model. So let’s do an example that uses a custom type and a custom aspect. We will continue with the Case example and create a new custom content model around that.
Implementing and deploying a custom content model
Begin by stopping your Alfresco ECM installation if it is running. To implement and deploy a custom content model we can use some out-of-the-box extension files. They need to be renamed before they can be used.
Spring context file that loads custom content model:
/opt/alfresco/tomcat/shared/classes/alfresco/extension$ mv custom-model-context.xml.sample custom-model-context.xml
The content model definition file:
/opt/alfresco/tomcat/shared/classes/alfresco/extension$ mv customModel.xml.sample customModel.xml
Then open up the customModel.xml file and implement the content model as follows:
<?xml version="1.0" encoding="UTF-8"?>
<model name="myc:customModel" xmlns="http://www.alfresco.org/model/dictionary/1.0">
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
<namespace uri="http://www.mycompany.org/model/content/1.0" prefix="myc"/>
<title>Case Opened Date</title>
As we can see, we have one new type called myc:caseDocument, which has one mandatory custom aspect called myc:caseData. We are going to use this new type and aspect when we upload the files. Note that we have added a date property just to show that we can use dates.
Note. Activiti uses only date fields in the form editor. However, It is possible to use d:datetime properties in the content model if needed. It will just be displayed in Share as for example “Thu 8 Dec 2016 00:00:00” after initially being set by the “Publish to Alfresco” task.
Now start the Alfresco ECM server again and make sure there are no errors.
Updating the process model to use the new custom content model
We now got the custom case content model deployed. Let’s update the process model so it uses the custom type and aspect when uploading the files.
Start by adding a Case Opened Date to the start form:
Then change the Alfresco content property for the “Publish to Alfresco” task as follows, start by changing the type and aspect to the custom ones:
Then in the Properties mapping section configure the Case ID property as follows:
And the Case Name property:
And finally the Case Opened Date property is configured as follows:
Important! The Property type for the date property needs to be set as string even though it is a form field of type date. The reason for this is that Activiti internally represents the date field incorrectly and when the “Publish to Alfresco” task is executed we will see see exceptions such as ERROR com.activiti.alfrescoconnector.service.AlfrescoContentService - error while updating meta data: java.lang.IllegalArgumentException: Invalid aspect value! If we set the Property Type to date, see this bug report.
This should be the only thing we need to change.
Now save and republish the application and process model.
Testing with a custom content model
If we now start a workflow instance based on this definition we will be presented with the following start form:
Fill in the Case ID, Name, and date. Then select two files from the local file-system by clicking on the SELECT A FILE button.
Then click START PROCESS. The process should complete and if we jump over to Alfresco Share and the Case Management site’s document library we should see the following files in the newly created Case Number 101 folder:
In this case we cannot actually see any custom metadata from the new content model. This is because we have not configured the Share UI with forms etc for this content model. However, there is a way to easily check if the properties have been set, click on the first file so you are opening the Document Details page and looking at the Properties section in the lower right corner:
Because we have not defined a form for the myc:caseDocument type or for the myc:caseData aspect Alfresco will just layout all available properties randomly. But we can see that it all worked as expected. If we want to be 100% sure about what type the doc has and what aspects have been applied, then navigate to the node in the Node Browser (logged in as admin click on Admin Tools | Node Browser):
Scroll down a bit to see the rest of the properties and the aspects:
Retrieving properties for Alfresco content
Once we got some content in Alfresco that we are working with in the process instance there might be a need to fetch more properties for it. This might be properties such as created and modified dates, properties set by an end-user in Alfresco, properties set by rules in Alfresco etc.
To do this we can use the “Retrieve Alfresco properties” task. Add it to our process model as follows:
Then click on the “Alfresco properties” task property so we can configure what properties we want to fetch:
The first thing we will do in this form is to select what Alfresco content file we want to fetch properties for. In our case we can select one of the files via the form field that was used to upload it, which will contain info about the CMIS Object ID (i.e. node reference). We could also have a standard process variable containing the content file reference. This means that we could for example have a Java based Service Task that sets up the content file that we work with, without end-user interaction.
Note. properties can only be fetched for one content file at a time, if we wanted to fetch properties for both files, then we would have to use two “Retrieve Alfresco properties” tasks.
After specifying what content file we are interested in we should add a list of the content model properties that we want to fetch/retrieve and what process variables they will map to. The following shows the properties we have chosen to fetch in this example:
- Content Model Property Process Var Data Type
- cmis:name - fileAName (string)
- cmis:objectId - fileANodeRef (string)
- cmis:creationDate - fileACreated (date)
- cmis:createdBy - fileACreator (string)
- cmis:lastModificationDate - fileAModified (date)
- cmis:lastModifiedBy - fileAModifier (string)
- cm:title - fileATitle (string)
- cm:description - fileADescription (string)
- myc:caseId - fileACaseId (number)
- myc:caseName - fileACaseName (string)
- myc:caseOpenedDate - fileACaseOpenedDate (date)
Here we got three different types of properties in different namespaces. First we got the standard CMIS properties in the cmis: namespace. For more info about available CMIS properties see this page and the 18.104.22.168.3 Property Deﬁnitions section for cmis:document. After that we are fetching some properties from the out-of-the box content model with the cm: namespace. And finally we fetch some properties from our custom model just to demonstrate how that is done.
To display the fetched properties let’s use a User Task:
Configure a form for it as follows:
So here we are using a number of Display value form fields that will just display value of corresponding process variable. We have grouped the properties with Header form fields.
Now, save the process model and republish it.
Start a process instance and fill in the start form as follows:
The new User Task should display the fetched properties as follows:
We can see that all properties have been fetched successfully, except the date properties. This has to do with the problem described earlier on when Activiti is representing dates incorrectly internally. It does not work to fetch the date properties as strings. To get around this we would have to implement a custom Java based Service Task that uses for example OpenCMIS to fetch the date properties.
Worth noting here also is that the title and description are usually populated via metadata extraction in Alfresco, and in this case the PDF file contained a description property that the cm:title was populated with.
Related bug report.
Updating properties for Alfresco Content
Ok, so we now know how to upload files from Activiti to Alfresco and how to retrieve properties for files in Alfresco from Activiti. There might also be scenarios where we select files in Alfresco repository for processing in an Activiti process instance (in contrast to selecting them from the local file system as done so far in this article), and at the end of the process instance we want to set some properties for these files.
This can be done with the “Update Alfresco properties” task. Let’s try it out by changing some of the properties we have for our files. Add first a User Task, which will be used to set new property values, and then add an Update Alfresco properties task. You should now have a process model looking something like this:
Configure a form for the Set new Prop values User task as follows:
The form will also show current values for reference.
Now, for the “Update Alfresco properties” task click on the Alfresco properties property for it and fill it in as follows:
Here we map the form fields we just created to the content model properties. This is straight forward, except for the date property, which has to be configured as a string for it to work as has been discussed before.
We also specify what user that should be used to make the update, remember that this user has to be mapped to an Alfresco user that has write permissions to the repository. In this case we just use the process initiator, which is admin.
Now, save the process model and republish the application.
Then start a new process instance and fill in the first start form as follows:
Complete the “Show retrieved props” task so you get to the Set new Prop values task:
Fill in some new values to the left:
Then click COMPLETE.
Now, in the Share UI navigate to one of the files and the Details page for it and verify that the update was successful:
Related bug report.
Calling Repository Actions in Alfresco
Ok, so we now know how to upload files to the Alfresco Repository and how to classify them at the same time, or at a later point. Next step might be to do some processing on the files we just uploaded. When we process files in Alfresco we usually execute something called a Document Library Action in the front-end (i.e. Share UI), which in turn invokes a so called Repository Action in the back-end.
From Activiti we can invoke Repository Actions via a task called “Call Alfresco Action”. Let’s walk through how to call a number of the out-of-the-box repository actions.
Apply an Aspect to a file
A common requirement is to be able to apply a new aspect to an existing content file in Alfresco. This can be done by invoking the add-features Repository Action. In this case it is not enough to just use the “Update Alfresco properties” task as it will not add a new aspect, just set properties. What we need to do is to first use the “Call Alfresco Action” task to apply the aspect and then set the properties.
For this example we will create a new process model as follows:
You might be wondering why we can not provide the aspect properties at the same time as we apply the aspect? That is not possible with the remote REST API that is used by the “Call Alfresco Action”. If you think about how it works from the Share UI when using “Manage Aspects”, or applying an aspect via rules. Then it’s also not possible to provide the aspect properties at the same time, you usually set them via Edit Properties afterwards. We need to use the “Update Alfresco properties” task to set the aspect properties.
For a solution on how to apply the aspect and its properties at the same time see next section on executing a script.
For the Start Event we create a form where we can select an Alfresco file that we want to execute the action on:
We also add some fields for the properties that are part of the cm:emailed aspect we want to apply to the file. Configure the Attach field so we can only select content files from Alfresco Repository and the Case Management site:
Then, for the “Call Alfresco Action” task, select Repository and Content (i.e. the file form field we just configured in the start form):
And then finally click on the Action field for the task and configure the Repository Action that we want to invoke on the file:
The Action field drop down contains some pre-configured actions that we can choose from, select the Add Aspect action, which will resolve to the action id add-features. You can also type in the Repository Action ID (i.e. the Spring Bean ID for the action) manually if it is not in the list. See this file for a list of actions and their IDs (look for beans with parent="action-executer").
When selecting the Add Aspect action a property called aspect-name is automatically added to the list of parameters that should be passed to the action invoker. It should be set to the aspect name that we want to apply to the file, in this case the out-of-the-box aspect cm:emailed.
Then we specify what aspect related properties we want to set via the “Update Alfresco properties task”, and from what form fields to grab the values:
Remember that Activiti represents dates incorrectly internally so when we set properties we need to define the date property as a string for it to work, see above.
Now let’s try this process model. After starting a process instance we get presented with the Start Form where we should select a file from the Case Management site and fill in some random email info:
Click START PROCESS to get the aspect applied. If you navigate to the file in Alfresco Share you should see the following under the Properties section:
The cm:emailed aspect has a form defined so that’s why we see these properties nicely laid out. But we still don’t have any form defined for myc:caseDocument so the other properties will not be visible. But you can always go via the Node Browser to see all properties.
Important, if the first “Call Alfresco Action” executes successfully but the second “Update Alfresco properties” task fails, then you would have a repository in an inconsistent state. To have both the apply aspect and set properties operations in the same transaction you would have to implement a custom service task and handle all the updates manually. Or use a script as in the next section.
Calling a Script
Configure the Content and Repository properties for the task as follows:
So we now know what repository to talk to and what file to apply the script to. Next step is to configure the Action property for the task:
var props = new Array();
props['cm:originator'] = 'email@example.com';
props['cm:addressee'] = 'firstname.lastname@example.org';
props['cm:subjectline'] = 'Testing Some stuff';
props['cm:sentdate'] = new Date();
Now, to find out the Node Reference for this script have a look at the Details Page for the file in Alfresco Share, it will have a link property with this information:
Copy the nodeRef value and set it as the value for the script-ref action parameter.
Now run this process model. We should see a similar effect on the file as in the Apply Aspect action execution.
Being able to execute a script like this gives you almost unlimited possibilities in processing the Repository content. However, it is not possible to pass parameters to the script, so this action probably needs to work in conjunction with some other task.
Set Content Type for a file
Another very useful thing to be able to do from Activiti is to set a new type for a content file in Alfresco. This can be done by executing the specialise-type Repository Action. Create a new process model with a Start Event form that has an attachment field. Then add the “Call Alfresco Action” task.
Configure the Content and Repository properties for the task as follows:
So we now know what repository to talk to and what file to set the content type on. Next step is to configure the Action property for the task:
Select Specialise type from the Action drop down. This will add the mandatory type-name parameter. The parameter has to be set to the content model type name. For example: myc:caseDocument. Note that for this action to have any effect the type we set must extend the type already set on the content file.
Because the files in the Case Management site already have the myc:caseDocument type set, we will have to upload a new file to the Case Management site’s Document Library before we try this out.
Moving a file
One quite useful thing to be able to do from Activiti is to move a file in the Alfresco Repository after some processing have been done on it in the workflow, maybe something like a review and approval. This can be done by executing the move Repository Action. Create a new process model with a Start Event form that has an attachment field. Then add the “Call Alfresco Action” task.
Configure the Content and Repository properties for the task as follows:
So we now know what repository to talk to and what file to move. Next step is to configure the Action property for the task:
Select Move from the Action drop down. This will add the mandatory destination-folder parameter. The parameter has to be set to the Alfresco Node Reference for the folder where you want to move the file. For example: workspace://SpacesStore/2a8052aa-e5ec-4546-85bf-49f55f6b7e81
In this sample I have just created an Approved folder in the Case Management site’s Document Library:
Now, to find out the Node Reference for this folder have a look at the Folder Details Page in Alfresco Share, it will have a link property with this information:
Copy the nodeRef value and set it as the value for the destination-folder action parameter.
Now run this process model. We should see that the selected file is moved into the Approved folder.