In my project, I have multiple event listeners, workflows, and Sling jobs that can modify the very same asset at the same time which sometimes causes commit conflicts. Obviously, different JCR sessions try to save different changes. I thought that refreshing the session before the actual .commit() call would help, but it didn't.
I need some explanation why the following code throws this exception
org.apache.sling.api.resource.PersistenceException: Unable to commit changes to session.
..
Caused by: javax.jcr.InvalidItemStateException: OakState0001: Unresolved conflicts in /content/dam...
ResourceResolver rr1 = ...get new service resource resolver
ResourceResolver rr2 = ...get new service resource resolver
Node node1 = rr1.getResource("/content/dam/.../asset.jpg/jcr:content/metadata").adaptTo(Node.class);
node1.setProperty("dc:title", "title 1");
Node node2 = rr2.getResource("/content/dam/.../asset.jpg/jcr:content/metadata").adaptTo(Node.class);
node2.setProperty("dc:title", "title 2");
rr1.commit();
rr2.refresh();
rr2.commit();
Views
Replies
Total Likes
@dominik_lackovi If you are updating metadata on the same asset, you need not to take 2 separate resourceResolver, instead you can try as below -
private static Map<String, Object> addAssetData(final Asset asset, final Hit hit, Map<String, Object> map)
throws RepositoryException {
String title = asset.getName();
if (StringUtils.isNotBlank(asset.getMetadataValue(DamConstants.DC_TITLE))) {
title = asset.getMetadataValue(DamConstants.DC_TITLE);
}
// Excerpt
String excerpt = hit.getExcerpt();
if (StringUtils.isBlank(hit.getExcerpt())) {
excerpt = StringUtils.stripToEmpty(asset.getMetadataValue(DamConstants.DC_DESCRIPTION));
if (excerpt.length() > MAX_EXCERPT_LENGTH) {
excerpt = StringUtils.substring(excerpt, 0, (MAX_EXCERPT_LENGTH - ELLIPSE_LENGTH)) + "...";
}
}
map.put(CF_PATH, asset.getPath());
map.put(CF_NAME, asset.getName());
map.put(CF_TITLE, title);
map.put(CF_EXCERPT, excerpt);
map.put(CF_MIMETYPE, asset.getMimeType());
map.put(CF_SIZE, getSize(asset));
map.put(CF_CACHE_KILLER, getCacheKiller(asset));
map.put(CF_TYPE, "Asset");
map.put(CF_LAST_MODIFIED, getLastModified(asset));
return map;
}
Ref _
This happens when two threads try to write at same JCR location.
The resource is modified by two concurrent sessions hence the conflict.
session.refresh(true) , should be used to avoid it.
If you check my code example, you will see that I'm refreshing the second session before committing. I was expecting that this would prevent the exception but it is still thrown. This is the part I don't understand. Maybe one session cannot refresh changes from another one?
PersistenceException
This exception will be thrown during the try to persist changes to a PersistableValueMap
Each time you call Adaptable.adaptTo(Class) you get a new map instance which does not share modified properties with other representations.
The reason you code is throwing the following Exception is because you are using adaptTo for changing property.
Instead, use ModifiableValueMap and you should not get these exceptions.
Example : resource.adaptTo(ModifiableValueMap.class);
What you said makes sense that different sessions are trying to save to same node.
Have you looked at https://helpx.adobe.com/in/experience-manager/kb/how-to-find-conflicts-when-getting-oakstate0001.html and https://experienceleaguecommunities.adobe.com/t5/adobe-experience-manager/javax-jcr-invaliditemstate... for relevant discussions? They seem to be concluding what we think is happening here. But the logs may help identifying what other thread/functionality is happening concurrently. We can disable one and test if that is really the cause.
Can we also try to use rr.hasChanges() in this code and else where, where we suspect a session is making changes to the target nodes? Then try to commit.
We can also explore session.hasPermissions and session.refresh.
Views
Likes
Replies
Views
Likes
Replies