diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml
index 196149832c..5a7e99d2aa 100644
--- a/config/alfresco/core-services-context.xml
+++ b/config/alfresco/core-services-context.xml
@@ -431,6 +431,7 @@
+
diff --git a/config/alfresco/model/contentModel.xml b/config/alfresco/model/contentModel.xml
index 2f5c65c3db..53f30c333a 100644
--- a/config/alfresco/model/contentModel.xml
+++ b/config/alfresco/model/contentModel.xml
@@ -1076,6 +1076,10 @@
d:boolean
true
+
+ d:text
+ true
+
diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties
index f671759f27..8ca51d9042 100644
--- a/config/alfresco/repository.properties
+++ b/config/alfresco/repository.properties
@@ -1072,6 +1072,13 @@ alfresco.jmx.connector.enabled=true
# Please, see MNT-11895 for details.
system.propval.uniquenessCheck.enabled=true
+# Requests for ephemeral (in-memory) locks with expiry times (in seconds) greater
+# than this value will result in persistent locks being created instead. By default
+# this value is equal to the maximum allowed expiry for ephemeral locks, therefore
+# this feature is disabled by default. Setting this to -1 would mean that ALL
+# requests for ephemeral locks would result in persistent locks being created.
+alfresco.ephemeralLock.expiryThresh=172800
+
# SurfConfigFolder Patch
#
# Do we defer running the surf-config folder patch?
diff --git a/source/java/org/alfresco/repo/lock/LockServiceImpl.java b/source/java/org/alfresco/repo/lock/LockServiceImpl.java
index 9ae7dea6fb..af30667b7c 100644
--- a/source/java/org/alfresco/repo/lock/LockServiceImpl.java
+++ b/source/java/org/alfresco/repo/lock/LockServiceImpl.java
@@ -104,6 +104,8 @@ public class LockServiceImpl implements LockService,
private NodeIndexer nodeIndexer;
+ private int ephemeralExpiryThreshold;
+
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
@@ -299,15 +301,16 @@ public class LockServiceImpl implements LockService,
public void lock(NodeRef nodeRef, LockType lockType, int timeToExpire, Lifetime lifetime, String additionalInfo)
{
invokeBeforeLock(nodeRef, lockType);
- if (additionalInfo != null && !lifetime.equals(Lifetime.EPHEMERAL))
- {
- throw new IllegalArgumentException("additionalInfo may only be provided for ephemeral locks.");
- }
if (lifetime.equals(Lifetime.EPHEMERAL) && (timeToExpire > MAX_EPHEMERAL_LOCK_SECONDS))
{
throw new IllegalArgumentException("Attempt to create ephemeral lock for " +
timeToExpire + " seconds - exceeds maximum allowed time.");
}
+ if (lifetime.equals(Lifetime.EPHEMERAL) && (timeToExpire > ephemeralExpiryThreshold))
+ {
+ lifetime = Lifetime.PERSISTENT;
+ }
+
nodeRef = tenantService.getName(nodeRef);
@@ -344,7 +347,7 @@ public class LockServiceImpl implements LockService,
{
// Add lock aspect if not already present
ensureLockAspect(nodeRef);
- persistLockProps(nodeRef, lockType, lifetime, userName, expiryDate);
+ persistLockProps(nodeRef, lockType, lifetime, userName, expiryDate, additionalInfo);
}
finally
{
@@ -372,7 +375,7 @@ public class LockServiceImpl implements LockService,
}
}
- private void persistLockProps(NodeRef nodeRef, LockType lockType, Lifetime lifetime, String userName, Date expiryDate)
+ private void persistLockProps(NodeRef nodeRef, LockType lockType, Lifetime lifetime, String userName, Date expiryDate, String additionalInfo)
{
addToIgnoreSet(nodeRef);
try
@@ -382,6 +385,7 @@ public class LockServiceImpl implements LockService,
this.nodeService.setProperty(nodeRef, ContentModel.PROP_LOCK_TYPE, lockType.toString());
this.nodeService.setProperty(nodeRef, ContentModel.PROP_LOCK_LIFETIME, lifetime.toString());
this.nodeService.setProperty(nodeRef, ContentModel.PROP_EXPIRY_DATE, expiryDate);
+ this.nodeService.setProperty(nodeRef, ContentModel.PROP_LOCK_ADDITIONAL_INFO, additionalInfo);
}
finally
{
@@ -845,6 +849,7 @@ public class LockServiceImpl implements LockService,
LockType lockType = lockTypeStr != null ? LockType.valueOf(lockTypeStr) : null;
String lifetimeStr = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_LIFETIME);
Lifetime lifetime = lifetimeStr != null ? Lifetime.valueOf(lifetimeStr) : null;
+ String additionalInfo = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_ADDITIONAL_INFO);
// Mark lockstate as PERSISTENT as it was in the persistent storage!
lockState = LockState.createLock(
@@ -853,7 +858,7 @@ public class LockServiceImpl implements LockService,
lockOwner,
expiryDate,
lifetime,
- null);
+ additionalInfo);
}
else
{
@@ -912,4 +917,15 @@ public class LockServiceImpl implements LockService,
lockStore.set(lockInfo.getNodeRef(), lockInfo);
}
}
+
+ @Override
+ public void setEphemeralExpiryThreshold(int threshSecs)
+ {
+ ephemeralExpiryThreshold = threshSecs;
+ }
+
+ public int getEphemeralExpiryThreshold()
+ {
+ return ephemeralExpiryThreshold;
+ }
}
diff --git a/source/java/org/alfresco/service/cmr/lock/LockService.java b/source/java/org/alfresco/service/cmr/lock/LockService.java
index 997dc5f07c..6dee4739a2 100644
--- a/source/java/org/alfresco/service/cmr/lock/LockService.java
+++ b/source/java/org/alfresco/service/cmr/lock/LockService.java
@@ -19,14 +19,12 @@
package org.alfresco.service.cmr.lock;
import java.util.Collection;
-import java.util.List;
import org.alfresco.api.AlfrescoPublicApi;
import org.alfresco.repo.lock.mem.Lifetime;
import org.alfresco.repo.lock.mem.LockState;
import org.alfresco.service.Auditable;
import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.cmr.repository.StoreRef;
/**
@@ -363,4 +361,13 @@ public interface LockService
* @return LockState
*/
public LockState getLockState(NodeRef nodeRef);
+
+ /**
+ * Specifies the maximum expiry time for which a request for an ephemeral lock
+ * will be honoured. Requests for ephemeral locks with expiry times greater than
+ * this value will be automatically converted to a request for a persistent lock.
+ *
+ * @param threshSecs
+ */
+ public void setEphemeralExpiryThreshold(int threshSecs);
}
diff --git a/source/test-java/org/alfresco/repo/lock/LockServiceImplTest.java b/source/test-java/org/alfresco/repo/lock/LockServiceImplTest.java
index 6cda7f55f7..709dd6829c 100644
--- a/source/test-java/org/alfresco/repo/lock/LockServiceImplTest.java
+++ b/source/test-java/org/alfresco/repo/lock/LockServiceImplTest.java
@@ -18,6 +18,8 @@
*/
package org.alfresco.repo.lock;
+import static org.junit.Assert.assertNotEquals;
+
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
@@ -257,17 +259,12 @@ public class LockServiceImplTest extends BaseSpringTest
this.lockService.lock(this.noAspectNode, LockType.WRITE_LOCK);
}
- public void testPersistentLockDisallowsAdditionalInfo()
+ public void testPersistentLockMayStoreAdditionalInfo()
{
- try
- {
- lockService.lock(noAspectNode, LockType.NODE_LOCK, 0, Lifetime.PERSISTENT, "additional info");
- fail("additionalInfo must be null for persistent lock, expected IllegalArgumentException.");
- }
- catch (IllegalArgumentException e)
- {
- // Good, exception was thrown.
- }
+ lockService.lock(noAspectNode, LockType.NODE_LOCK, 0, Lifetime.PERSISTENT, "additional info");
+
+ LockState lockState = lockService.getLockState(noAspectNode);
+ assertEquals("additional info", lockState.getAdditionalInfo());
}
public void testEphemeralLock()
@@ -818,6 +815,59 @@ public class LockServiceImplTest extends BaseSpringTest
assertEquals(LockStatus.LOCK_EXPIRED, this.lockService.getLockStatus(this.parentNode));
}
+ public void testEphemeralExpiryThreshold()
+ {
+ TestWithUserUtils.authenticateUser(GOOD_USER_NAME, PWD, rootNodeRef, this.authenticationService);
+ final int origThresh = ((LockServiceImpl)lockService).getEphemeralExpiryThreshold();
+ // Check the default situation is that the threshold does not apply.
+ assertEquals(LockServiceImpl.MAX_EPHEMERAL_LOCK_SECONDS, origThresh);
+ try
+ {
+ // Set the ephemeral expiry threshold to a much smaller value than the default
+ // so that it takes effect.
+ lockService.setEphemeralExpiryThreshold(300);
+
+ // Check for an expiry time that should be unaffected by the threshold.
+ checkLifetimeForExpiry(Lifetime.EPHEMERAL, 0, Lifetime.EPHEMERAL);
+ checkLifetimeForExpiry(Lifetime.EPHEMERAL, 150, Lifetime.EPHEMERAL);
+
+ // Check the largest allowed ephemeral expiry time.
+ checkLifetimeForExpiry(Lifetime.EPHEMERAL, 300, Lifetime.EPHEMERAL);
+
+ // When the expiry is greater than the threshold, then the lock should be
+ // applied as a persistent lock.
+ checkLifetimeForExpiry(Lifetime.PERSISTENT, 301, Lifetime.EPHEMERAL);
+
+ // Switch off ephemeral locks entirely
+ lockService.setEphemeralExpiryThreshold(-1);
+ // Always persistent...
+ checkLifetimeForExpiry(Lifetime.PERSISTENT, 0, Lifetime.EPHEMERAL);
+ checkLifetimeForExpiry(Lifetime.PERSISTENT, 150, Lifetime.EPHEMERAL);
+ checkLifetimeForExpiry(Lifetime.PERSISTENT, 300, Lifetime.EPHEMERAL);
+ checkLifetimeForExpiry(Lifetime.PERSISTENT, 301, Lifetime.EPHEMERAL);
+ }
+ finally
+ {
+ lockService.setEphemeralExpiryThreshold(origThresh);
+ }
+ }
+
+ private void checkLifetimeForExpiry(Lifetime expectedLifetime, int expirySecs, Lifetime requestedLifetime)
+ {
+ lockService.unlock(parentNode);
+ assertNotEquals(LockStatus.LOCKED ,lockService.getLockStatus(parentNode));
+ lockService.lock(parentNode, LockType.WRITE_LOCK, expirySecs, requestedLifetime);
+ LockState lock = lockService.getLockState(parentNode);
+ assertEquals(expectedLifetime, lock.getLifetime());
+
+ // Check that for any timeouts we test, a request for a persistent lock always yields a persistent lock.
+ lockService.unlock(parentNode);
+ assertNotEquals(LockStatus.LOCKED ,lockService.getLockStatus(parentNode));
+ lockService.lock(parentNode, LockType.WRITE_LOCK, expirySecs, Lifetime.PERSISTENT);
+ lock = lockService.getLockState(parentNode);
+ assertEquals(Lifetime.PERSISTENT, lock.getLifetime());
+ }
+
/**
* Unit test to validate the behaviour of creating children of locked nodes.
* No lock - can create children
diff --git a/source/test-java/org/alfresco/repo/lock/mem/LockableAspectInterceptorTest.java b/source/test-java/org/alfresco/repo/lock/mem/LockableAspectInterceptorTest.java
index 19e474715a..ff8d9444ff 100644
--- a/source/test-java/org/alfresco/repo/lock/mem/LockableAspectInterceptorTest.java
+++ b/source/test-java/org/alfresco/repo/lock/mem/LockableAspectInterceptorTest.java
@@ -210,6 +210,7 @@ public class LockableAspectInterceptorTest
lockProps.put(ContentModel.PROP_LOCK_OWNER, "jbloggs");
lockProps.put(ContentModel.PROP_LOCK_TYPE, LockType.READ_ONLY_LOCK.toString());
lockProps.put(ContentModel.PROP_EXPIRY_DATE, now);
+ lockProps.put(ContentModel.PROP_LOCK_ADDITIONAL_INFO, "{ \"fieldName\": \"extra lock info\" }");
nodeService.addAspect(nodeRef, ContentModel.ASPECT_LOCKABLE, lockProps);
Map readProps = nodeService.getProperties(nodeRef);
@@ -221,6 +222,7 @@ public class LockableAspectInterceptorTest
assertEquals(Lifetime.PERSISTENT.toString(), readProps.get(ContentModel.PROP_LOCK_LIFETIME));
// Double check - not really present
ensurePropertyNotPresent(nodeRef, ContentModel.PROP_LOCK_LIFETIME);
+ assertEquals("{ \"fieldName\": \"extra lock info\" }", readProps.get(ContentModel.PROP_LOCK_ADDITIONAL_INFO));
}
/**