Optimistic locking is a commonly used concurrency control method for web applications. It is so frequently needed that frameworks such as Grails use it by default. Alfresco FormService
does not support it out of the box – and that may cause unexpected results. This post outlines an approach to change the system in order to get something “close to” optimistic locking behavior.
The Default Concurrent Write Situation
Today, I am working on an Alfresco Web Quickstart derived project. A part of the editorial interface is based on Alfresco Share and the editorial process as a whole makes heavy use of the FormService (which is used by the Web Editor as well). I have not used this service extensively in the past, but I was suprised to see that concurrent editing can end up “last writer wins”. A typical sequence of events goes:
- You open the editing form for one item two times – using one browser with two tabs will do. You can just as well use two diffrent users or browser sessions.
- You subsequently save the forms you just opened.
- The second write operation overwrites the changes of the first.
The risk of a conflict depends on the content change frequency, editing process duration and amount of editors working on the content. You may be tempted to address this using versioning, but doing so may open a new can of worms you don’t want. The first thing I thought was that other people should have been faced with this situation before, so I started googling for a solution. I was even more suprised not finding anything really useful and ended up DIY.
I found, that in fact it is quite easy to get a behavior which is not optimistic locking in the very strict sense, but close. Even more important, the system can behave much more expected in the situation of a conflict.
An “Almost” Optimistic Locking Implementation
The following is a rough outline of the solution I came up with (There is a link to the sources below). I have been using it with Alfresco Community 4.0.d.
In the repository, you deploy a filter
<bean id="optimisticLockFormFilter" class="OptimisticLockFilter" parent="baseFormFilter"> <property name="nodeService" ref="NodeService" /> <property name="namespaceService" ref="NamespaceService" /> <property name="filterRegistry" ref="nodeFilterRegistry" /> </bean>
which hooks into the form processing. This filter is aware of a special field name prefix _olock_
, telling it that a field is meant to be used for version checking. I used cm:modified
just because it was here and there and ready to go. In beforePersist
the filter checks whether fields matching this prefix have been submitted. If that is the case, it compares the values with current persisted ones. It throws an exception if they don’t match.
To make use of it, you use a special form control template in your form configuration:
<field id="cm:modified"> <control template="olock.ftl"/> </field>
The control template is derived from hidden.ftl
and the important part is
<#if form.mode == "edit" || form.mode == "create"> <input type="hidden" name="_olock_${field.name}" value="<#if field.value?is_number>${fieldValue?c}<#else>${fieldValue?html}</#if>" /> </#if>
With all this in place, the second save operation will (usually) fail showing the following dialog:

Actually quite funny that Share behaves nice here (with no further customization), presenting us with the failure dialog. :)
Again, this solution may not be beautiful for academic eyes as it is not 100% bullet-proof. Nevertheless, it should handle the real world situation fairly well and it should be almost impossible to create the “accidentally overwritten” situation. In fact, aiming for perfection, I tried making it 100% bullet-proof. At first glance, it seemed LockService
provides the functionality needed, but that is not quite the case. What I wanted was “select ... for update
” style row level read locking. The code using LockService
is still in place and in fact used when the service gets injected.
I still feel like I must have missed something and wonder how other people deal with this situation. To be honest, I think Alfresco should be shipping optimistic locking functionality out of the box. I wonder whether they have something in the making.
Download Optimistic Locking Alfresco Forms Service Extension
Hello Andreas,
cool idea, indeed! Do you think this can be enabled systemwide for all forms without configuring it on each form? Maybe the method beforeGenerate in your OptimisticLockFilter can be used to add this field based on a spring configuration for switching the optimistic locking on or off… What do you think about that?
Thanks,
Jens
Hi Jens,
thanks for your feedback !
I have not yet checked what it takes to enable this globally for all cm:auditable content items (due to cm:modified being used). My “workaround” will work as long as a special field prefixed “_olock_” gets submitted with the form. This makes the extension compare the submitted value of the field with the current persisted one. I’m fairly sure its just a few lines of code enabling this globally – assuming our only requirement is “code that does the job”. ;)
Anyways, “abusing” cm:modified is a workaround and FormsService only part of the issue. I just filed an improvement issue https://issues.alfresco.com/jira/browse/ALF-13604 ,as I think this should be addressed at a lower level in repo core code. Lets see what happens. ;)
cheers
Andreas