Image of yes written on a wall

Extending Alfresco FormService with Optimistic Locking

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:

Alfresco Share "Optimistic Locking" Failure Dialog
Alfresco Share "Optimistic Locking" Failure 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

References

Andreas Steffan
Pragmatic ? Scientist and DevOps Mind @ Contentreich. Believes in Open Source, the Open Web and Linux. Freelancing in DevOps-, Cloud-, Kubernetes, JVM- and Contentland and speaks Clojure, Kotlin, Groovy, Go, Python, JavaScript, Java, Alfresco and WordPress. Built infrastructure before it was cool. ❤️ Emacs.

2 thoughts on “Extending Alfresco FormService with Optimistic Locking”

  1. 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

  2. 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

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert