3

I am getting following exception in a single box cq5 author environment.

javax.jcr.InvalidItemStateException: Item cannot be saved 
because node property has been modified externally

more exception details:

Caused by: javax.jcr.InvalidItemStateException: Unable to update a stale item: item.save()
    at org.apache.jackrabbit.core.ItemSaveOperation.perform(ItemSaveOperation.java:262)
    at org.apache.jackrabbit.core.session.SessionState.perform(SessionState.java:216)
    at org.apache.jackrabbit.core.ItemImpl.perform(ItemImpl.java:91)
    at org.apache.jackrabbit.core.ItemImpl.save(ItemImpl.java:329)
    at org.apache.jackrabbit.core.session.SessionSaveOperation.perform(SessionSaveOperation.java:65)
    at org.apache.jackrabbit.core.session.SessionState.perform(SessionState.java:216)
    at org.apache.jackrabbit.core.SessionImpl.perform(SessionImpl.java:361)
    at org.apache.jackrabbit.core.SessionImpl.save(SessionImpl.java:812)
    at com.day.crx.core.CRXSessionImpl.save(CRXSessionImpl.java:142)
    at org.apache.sling.jcr.resource.internal.helper.jcr.JcrResourceProvider.commit(JcrResourceProvider.java:511)
    ... 215 more
Caused by: org.apache.jackrabbit.core.state.StaleItemStateException: 3bec1cb7-9276-4bed-a24e-0f41bb3cf5b7/{}ssn has been modified externally
    at org.apache.jackrabbit.core.state.SharedItemStateManager$Update.begin(SharedItemStateManager.java:679)
    at org.apache.jackrabbit.core.state.SharedItemStateManager.beginUpdate(SharedItemStateManager.java:1507)
    at org.apache.jackrabbit.core.state.SharedItemStateManager.update(SharedItemStateManager.java:1537)
    at org.apache.jackrabbit.core.state.LocalItemStateManager.update(LocalItemStateManager.java:400)
    at org.apache.jackrabbit.core.state.XAItemStateManager.update(XAItemStateManager.java:354)
    at org.apache.jackrabbit.core.state.LocalItemStateManager.update(LocalItemStateManager.java:375)
    at org.apache.jackrabbit.core.state.SessionItemStateManager.update(SessionItemStateManager.java:275)
    at org.apache.jackrabbit.core.ItemSaveOperation.perform(ItemSaveOperation.java:258)

Here is the code sample:

adminResourceResolver = resourceResolverFactory
                .getAdministrativeResourceResolver(null);
Resource fundPageResource = adminResourceResolver.getResource(page
                .getPath() + "/jcr:content");
ModifiableValueMap homePageResourceProperties = fundPageResource
                .adaptTo(ModifiableValueMap.class);
homePageResourceProperties.put("ssn",(person.getSsn());

adminResourceResolver.commit();

Any ideas ? It could be possible multiple threads accessing this code, as multiple authors on multiple pages calling this code from a authored component.

Thank you, Sri

Sri
  • 1,205
  • 2
  • 21
  • 52

1 Answers1

6

This is an error your see often in CQ5.5 (and lessens with each version upwards). The root cause of this issue is that multiple processes/services are modifying the same resource in roughly the same timespan (usually using different sessions, sometimes even with different users).

A small example to demonstrate perhaps. Session A and B both have a reference to Resource X. Session A modifies some properties on X, saves and commits, and is destroyed. This all goes smoothly. Session B still has a snapshot of the situation before A made modifications, session B makes modifications and all seems well UNTIL it tries to save. At this point, session B detects that it can't commit its changes because it doesn't have the latest node state. It has detected some other sessions made changes to the same node. In essence the current node state conflicts with modifications that session A has done and throws an ItemStale exception. The reason for this exception is the notion that the API doesn't know wether you want to keep the changes made by A, keep the changes made by the current session and discard the changes made by A, or merge them.

This error happens often with long running sessions and with workflow/listener combinations. Therefore the recommendation is to keep sessions as short as possible to prevent this kind of conflicts as much as possible.

One way to deal with this is to call session.refresh(keepChangesBoolean) before calling .save(). This instructs the current session to check for updates made by other sessions and deal with it according to the boolean flag you submit. This however is not a guarantee as it's still possible that between your refresh and your save call, yet another session has done the same. It only lowers the odds of this exception occurring. Another way to deal with this is to retry again from scratch.

3xil3
  • 1,546
  • 1
  • 12
  • 16
  • Thank you @3xil3 , it make sense. I know how to deal with transactions in regular webapp, not a JCR app. – Sri Aug 11 '16 at 14:19