Alfresco: Max Version Policy
UPDATE: Updated code examples to match updates to source code
As part of a POC for a customer I was asked to write an extension that allowed them to control the total number of versions allowed per versioned content. (Download links at bottom of page)
Alfresco has a strong versioning story, that gives you the ability to version any content stored in the repository, no matter what the file type. Versions are full files and not diffs of the files. Alfresco gives you the ability to have both major and minor versions of content. Versions can be created/updated by checkout/checkin, by rule, through any interface or through script/APIs.
Alfresco also provides the ability to apply behaviors / policies to content/metadata within the repository. You can think of these as event listeners, that allow you to take custom actions based on what is happening within the repository. Jeff Potts has written an excellent tutorial on creating behaviors, with examples for both Java and javascript. (Other resources include: )
There are 4 versioning policies that we can work with:
- beforeCreateVersion
- afterCreateVersion
- onCreateVersion
- calculateVersionLabel
(For an example of a calculateVersionLabel policy look at this post and the accompanying code by Peter Monks.)
For this policy we are going to use afterCreateVersion: after a version is created we want to remove any version that puts us past the max version value.
You start by implementing the policy interface for the policy you want to apply.
public class MaxVersionPolicy implements AfterCreateVersionPolicy
Adding the method implemented by the interface
public void afterCreateVersion( NodeRef versionableNode, Version version)
We also need to bind the behavior to the policy and we need to do this when the class is loaded by the springframework. We wrap this registration in an init() method
public void init() {
this.afterCreateVersion = new JavaBehaviour(this, "afterCreateVersion",
NotificationFrequency.TRANSACTION_COMMIT);
this.policyComponent.bindClassBehaviour(QName.createQName(
NamespaceService.ALFRESCO_URI, "afterCreateVersion"),
MaxVersionPolicy.class, this.afterCreateVersion);
}
The init method is then called when spring loads the bean
<bean id="maxVersion"
class="org.alfresco.extension.versioning.MaxVersionPolicy"
init-method="init">
<property name="policyComponent">
<ref bean="policyComponent" />
</property>
<property name="versionService">
<ref bean="versionService" />
</property>
<!-- The max number of versions per versioned node -->
<property name="maxVersions">
<value>10</value>
</property>
</bean>
Now to the meat of the policy, enforcing the removal of versions.
First we we have our maxVersion property. This is set in the springbean (see above) and read when the bean is loaded. (You can overwrite the default value by copying the maxversionpolicy-context.xml file into the extensions directory and changing the value of the maxVersion property)
public void setMaxVersions(int maxVersions) {
this.maxVersions = maxVersions;
}
Next we want to remove any version that puts us over the max
@Override
public void afterCreateVersion(NodeRef nodeRef, Version version) {
VersionHistory versionHistory = versionService
.getVersionHistory(nodeRef);
// If the current number of versions in the VersionHistory is greater
// than the maxVersions limit, remove the root/least recent version
if (versionHistory.getAllVersions().size() > maxVersions) {
logger.debug("Removing Version: "
+ versionHistory.getRootVersion().getVersionLabel());
versionService.deleteVersion(nodeRef, versionHistory
.getRootVersion());
}
}
We first get the versionHistory for the node and check it against the maxVersion property. If we have more versions than the limit we delete the least recent version from the version history.

Notice: 10 total versions; least recent version is 1.1; no pagination!
Q: Why are you using an if statement, instead of looping through all of the versions. I have a few (many) more versions over the limit I want to impose?
A: A while statement would be more efficient in removing everything above the limit, but the versionHistory object isn’t being updated with each delete in a loop (I tried) until the policy has completely run. Luckily, as you will find as you implement custom behaviors, they get called a lot! So while a single update will result in a single version added, you will actually see that the behavior is called multiple times for that one action (In testing I saw the behavior being called a minimum of 7 times). While you might not clear out every version over the limit with a single update, you will see a good many of them removed. If you want to bring every down to the limit you could create a “cleaner” extension that could go in and remove all of the versions over the limit (this would be a very intensive/costly operation depending on the amount of content in your repository and the size of your version history – There several different strategies for this). You could then use this extension to enforce that limit.
Q: Does this affect all versioned content in the repository? What if I only want it to work on some content?
A: Yes, by default this policy is being applied to all versioned content, but you could add in checks to look for a specific aspect, parent space name, property, etc. before passing through the delete code.
Q: Compliance regulations/laws don’t allow me to delete the versions, but require me keep them for X years. How can I archive versions?
A: There are probably several different ways to handle this. One way could be to use Alfresco’s content store selector. The content store selector allows you to configure multiple underlying filesystem locations for your content. To the end user all of the content appears to come from the same location, while the content itself lives in different disc systems. The idea would be to set up a secondary store that would act as the archive. When your trigger is reached, be it a property (status [property or aspect], date, etc.), a version number/count, etc. have the policy move the content to the archive space structure (The archive will need to have the cm:storeSelector aspect set to mark it to use the secondary store). Because we are working from the least recent version, the 1.0 version of the content, it becomes the first version in the archive, each subsequent version, is added as a version for the content, version numbers are then maintained in the archive. I would also add an aspect to the original node that contains a pointer/aspect to the archived version, for reference. Then delete the least recent version of the original versioned node.
Google Code Project: http://code.google.com/p/alfresco-maxversion-policy/
Download Alfresco Max Version Policy AMP
Tested on Alfresco (Enterprise) 3.2 – 3.3.1
Jared, another great blog. Thanks so much for sharing your experience with the community.
Nancy Garrity
Alfresco Community Manager
Nancy Garrity
23 Jul 10 at 1:20 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
[...] difference in the structure needed for this behavior and the Max Version Policy I wrote about in a previous post. So I used it as a template for this [...]
Alfresco: Default Quota Policy at Jared Ottley
15 Sep 10 at 11:16 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Can I install the .amp file into a pre-existing (running) Alfresco installation?
Thank you
Martino
21 Jun 11 at 9:19 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
yes. though I would not recommend it. I don’t believe we test a “hot-deployment”
jared
5 Jul 11 at 11:27 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Hi Jared,
Thanks for the post and information. Limiting the number of document revisions and hence the file store bloat is essential for us. Show stopper in fact. Unfortunately this is the only solution for doing this with Alfresco that I can find and sadly this AMP applied to Alfresco Community 4.0a seems to breaks things rather badly. Any chance you might be able to resurrect it?
Steve
26 Oct 11 at 8:21 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
I can definitely take a look. Can you post the exceptions that you are seeing?
jared
26 Oct 11 at 9:30 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Just a quick test post. I’m trying to post a long item and nothing seems to be happening. Perhaps I’m being too verbose (yet again)!
Steve
27 Oct 11 at 9:10 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Yep, I’m too long winded! I’ll try break it down.
Hi Jared, It works!!!
I must report that previously when I attempted to apply this AMP to Community 4.0.a I managed to break something so badly that after an Alfresco restart I couldn’t even login. Today I downloaded the AMP again and applied it to my 4.0.a test server specifically to get any error or exceptions from log files for you. Today I succeeded! So sorry for “crying wolf”. Obviously my trouble on the previous attempt was something stupid that I did on the day.
Steve
27 Oct 11 at 9:14 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Today however was not all plain sailing. For those who like me are reading this as Alfresco newbies. Here are the things I was doing wrong:-
You must make sure that you delete the Tomcat work and temp file directories. The bottom of page http://wiki.alfresco.com/wiki/Module_Management_Tool clearly says this. A script called clean_tomcat.sh (Linux system) will do this for you. Make sure Tomcat is not running first.
You must also make sure that the ~/tomcat/webapps/alfresco directory is also removed. Make sure you backup any modified files first. I didn’t have any but being a chicken I moved the directory rather than delete it.
Steve
27 Oct 11 at 9:17 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Last. And I don’t know if this is actually required. But it is something I didn’t do last time. I discovered that the alfresco-maxversion-policy.amp file contained a file called module.properties and inside this was:-
module.repo.version.min=3.2
module.repo.version.max=3.3.1
Since my Community version is 4.0.a I’m guessing that this AMP may not have applied properly.
So I changed the values to:-
module.repo.version.min=0
module.repo.version.max=999
And now it works!
Steve
27 Oct 11 at 9:19 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Interestingly, upon very quick inspection. When using Share in 4.0.a it appears that only exactly one file in excess of the maximum 10 is deleted upon any update. So if you have say 15 revisions then you’re always going to have 15 revisions. But if you start clean then it will correctly build to 10 revisions and hold at that point.
Either way, the solution is completely acceptable for us. Show stopper removed!
Thanks again Jared!
Steve
27 Oct 11 at 9:19 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
This is very good to know. I’ll update the min/max to allow for 4.0. Glad this could be of some value to you.
Jared
jared
27 Oct 11 at 9:48 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>