getChildElements(key) would be b
. The key's last path
- * element represents the key's property name, and can therefore be any value without affecting
- * the outcome of the call.
+ * Fetches all child elements for the given path. The key's property should be
+ * null as it is completely ignored.
+ *
+ * ...
+ * registryService.addValue(KEY_A_B_C_1, VALUE_ONE);
+ * registryService.addValue(KEY_A_B_C_2, VALUE_TWO);
+ * ...
+ * assertTrue(registryService.getChildElements(KEY_A_B_null).contains("C"));
+ * ...
+ *
*
* @param key the registry key with the path. The last element in the path
- * will be ignored, and can be any acceptable property localname.
+ * will be ignored, and can be any acceptable value localname or null.
* @return Returns all child elements (not values) for the given key, ignoring
* the last element in the key.
+ *
+ * @see RegistryKey#getPath()
*/
Collection getChildElements(RegistryKey key);
+
+ /**
+ * Copies the path or value from the source to the target location. The source and target
+ * keys must be both either path-specific or property-specific. If the source doesn't
+ * exist, then nothing will be done; there is no guarantee that the target will exist after
+ * the call.
+ *
+ * This is essentially a merge operation. Use {@link #delete(RegistryKey) delete} first
+ * if the target must be cleaned.
+ *
+ * @param sourceKey the source registry key to take values from
+ * @param targetKey the target registyr key to move the path or value to
+ */
+ void copy(RegistryKey sourceKey, RegistryKey targetKey);
+
+ /**
+ * Delete the path element or value described by the key. If the key points to nothing,
+ * then nothing is done.
+ * delete(/a/b/c)
will remove value c from path /a/b.
+ * delete(/a/b/null)
will remove node /a/b along with all values and child
+ * elements.
+ *
+ * @param key the path or value to delete
+ */
+ void delete(RegistryKey key);
}
diff --git a/source/java/org/alfresco/repo/admin/registry/RegistryServiceImpl.java b/source/java/org/alfresco/repo/admin/registry/RegistryServiceImpl.java
index 770d000589..f43428534b 100644
--- a/source/java/org/alfresco/repo/admin/registry/RegistryServiceImpl.java
+++ b/source/java/org/alfresco/repo/admin/registry/RegistryServiceImpl.java
@@ -28,7 +28,10 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
@@ -43,6 +46,7 @@ import org.alfresco.service.namespace.NamespaceException;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
+import org.alfresco.util.EqualsHelper;
import org.alfresco.util.Pair;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.PropertyMap;
@@ -168,8 +172,11 @@ public class RegistryServiceImpl implements RegistryService
}
/**
+ * Get the node-qname pair for the key. If the key doesn't have a value element,
+ * i.e. if it is purely path-based, then the QName will be null.
+ *
* @return Returns the node and property name represented by the key or null
- * if it doesn't exist and was not allowed to be created
+ * if it doesn't exist and was not allowed to be created.
*/
private Pair getPath(RegistryKey key, boolean create)
{
@@ -234,10 +241,15 @@ public class RegistryServiceImpl implements RegistryService
currentNodeRef = childAssocRefs.get(0).getChildRef();
}
}
+ // Cater for null properties, i.e. path-based keys
+ QName propertyQName = null;
+ if (property != null)
+ {
+ propertyQName = QName.createQName(
+ namespaceUri,
+ QName.createValidLocalName(property));
+ }
// Create the result
- QName propertyQName = QName.createQName(
- namespaceUri,
- QName.createValidLocalName(property));
Pair resultPair = new Pair(currentNodeRef, propertyQName);
// done
if (logger.isDebugEnabled())
@@ -259,8 +271,12 @@ public class RegistryServiceImpl implements RegistryService
/**
* @inheritDoc
*/
- public void addValue(RegistryKey key, Serializable value)
+ public void addProperty(RegistryKey key, Serializable value)
{
+ if (key.getProperty() == null)
+ {
+ throw new IllegalArgumentException("Registry values must be added using paths that contain property names: " + key);
+ }
// Check the namespace being used in the key
String namespaceUri = key.getNamespaceUri();
if (!namespaceService.getURIs().contains(namespaceUri))
@@ -281,23 +297,27 @@ public class RegistryServiceImpl implements RegistryService
}
}
- public Serializable getValue(RegistryKey key)
+ public Serializable getProperty(RegistryKey key)
{
+ if (key.getProperty() == null)
+ {
+ throw new IllegalArgumentException("Registry values must be fetched using paths that contain property names: " + key);
+ }
// Get the path, without creating
Pair keyPair = getPath(key, false);
- Serializable value = null;
+ Serializable property = null;
if (keyPair != null)
{
- value = nodeService.getProperty(keyPair.getFirst(), keyPair.getSecond());
+ property = nodeService.getProperty(keyPair.getFirst(), keyPair.getSecond());
}
// Done
if (logger.isDebugEnabled())
{
- logger.debug("Retrieved value from registry: \n" +
+ logger.debug("Retrieved property from registry: \n" +
" Key: " + key + "\n" +
- " Value: " + value);
+ " Value: " + property);
}
- return value;
+ return property;
}
public Collection getChildElements(RegistryKey key)
@@ -330,4 +350,175 @@ public class RegistryServiceImpl implements RegistryService
}
return results;
}
+
+ public void copy(RegistryKey sourceKey, RegistryKey targetKey)
+ {
+ if ((sourceKey.getProperty() == null) && !(targetKey.getProperty() == null))
+ {
+ throw new AlfrescoRuntimeException(
+ "Registry keys must both be path specific for a copy: \n" +
+ " Source: " + sourceKey + "\n" +
+ " Target: " + targetKey);
+ }
+ else if ((sourceKey.getProperty() != null) && (targetKey.getProperty() == null))
+ {
+ throw new AlfrescoRuntimeException(
+ "Registry keys must both be value specific for a copy: \n" +
+ " Source: " + sourceKey + "\n" +
+ " Target: " + targetKey);
+ }
+ // If the source is missing, then do nothing
+ Pair sourceKeyPair = getPath(sourceKey, false);
+ if (sourceKeyPair == null)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Nothing copied from non-existent registry source key: \n" +
+ " Source: " + sourceKey + "\n" +
+ " Target: " + targetKey);
+ }
+ return;
+ }
+ // Move based on the path or property
+ Pair targetKeyPair = getPath(targetKey, true);
+ if (sourceKeyPair.getSecond() != null)
+ {
+ // It is property-based so we just need to copy the value
+ Serializable value = nodeService.getProperty(sourceKeyPair.getFirst(), sourceKeyPair.getSecond());
+ nodeService.setProperty(targetKeyPair.getFirst(), targetKeyPair.getSecond(), value);
+ }
+ else
+ {
+ // It is path based so we need to copy all registry entries
+ // We have an existing target, but we need to recurse
+ Set processedNodeRefs = new HashSet(20);
+ copyRecursive(sourceKey, targetKey, processedNodeRefs);
+ }
+ // Done
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Copied registry keys: \n" +
+ " Source: " + sourceKey + "\n" +
+ " Target: " + targetKey);
+ }
+ }
+
+ /**
+ * @param sourceKey the source path that must exist
+ * @param targetKey the target path that will be created
+ * @param processedNodeRefs a set to help avoid infinite loops
+ */
+ private void copyRecursive(RegistryKey sourceKey, RegistryKey targetKey, Set processedNodeRefs)
+ {
+ String sourceNamespaceUri = sourceKey.getNamespaceUri();
+ String targetNamespaceUri = targetKey.getNamespaceUri();
+ // The source just exist
+ Pair sourceKeyPair = getPath(sourceKey, false);
+ if (sourceKeyPair == null)
+ {
+ // It has disappeared
+ return;
+ }
+ NodeRef sourceNodeRef = sourceKeyPair.getFirst();
+ // Check that we don't have a circular reference
+ if (processedNodeRefs.contains(sourceNodeRef))
+ {
+ // This is very serious, but it can be worked around
+ logger.error("Circular paths detected in registry entries: \n" +
+ " Current Source Key: " + sourceKey + "\n" +
+ " Current Target Key: " + targetKey + "\n" +
+ " Source Node: " + sourceNodeRef);
+ logger.error("Bypassing circular registry entry");
+ return;
+ }
+
+ // Make sure that the target exists
+ Pair targetKeyPair = getPath(targetKey, true);
+ NodeRef targetNodeRef = targetKeyPair.getFirst();
+
+ // Copy properties of the source namespace
+ Map sourceProperties = nodeService.getProperties(sourceNodeRef);
+ Map targetProperties = nodeService.getProperties(targetNodeRef);
+ boolean changed = false;
+ for (Map.Entry entry : sourceProperties.entrySet())
+ {
+ QName sourcePropertyQName = entry.getKey();
+ if (!EqualsHelper.nullSafeEquals(sourcePropertyQName.getNamespaceURI(), sourceNamespaceUri))
+ {
+ // Wrong namespace
+ continue;
+ }
+ // Copy the value over
+ Serializable value = entry.getValue();
+ QName targetPropertyQName = QName.createQName(targetNamespaceUri, sourcePropertyQName.getLocalName());
+ targetProperties.put(targetPropertyQName, value);
+ changed = true;
+ }
+ if (changed)
+ {
+ nodeService.setProperties(targetNodeRef, targetProperties);
+ }
+ // We have processed the source node
+ processedNodeRefs.add(sourceNodeRef);
+
+ // Now get the child elements of the source
+ Collection sourceChildElements = getChildElements(sourceKey);
+ String[] sourcePath = sourceKey.getPath();
+ String[] childSourcePath = new String[sourcePath.length + 1]; //
+ System.arraycopy(sourcePath, 0, childSourcePath, 0, sourcePath.length);
+ String[] targetPath = targetKey.getPath();
+ String[] childTargetPath = new String[targetPath.length + 1]; //
+ System.arraycopy(targetPath, 0, childTargetPath, 0, targetPath.length);
+ for (String sourceChildElement : sourceChildElements)
+ {
+ // Make the source child key using the current source namespace
+ childSourcePath[sourcePath.length] = sourceChildElement;
+ RegistryKey sourceChildKey = new RegistryKey(sourceNamespaceUri, childSourcePath, null);
+ // Make the target child key using the current target namespace
+ childTargetPath[targetPath.length] = sourceChildElement;
+ RegistryKey targetChildKey = new RegistryKey(targetNamespaceUri, childTargetPath, null);
+ // Recurse
+ copyRecursive(sourceChildKey, targetChildKey, processedNodeRefs);
+ }
+ }
+
+ public void delete(RegistryKey key)
+ {
+ Pair keyPair = getPath(key, false);
+ if (keyPair == null)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Nothing to delete for registry key: \n" +
+ " Key: " + key);
+ }
+ return;
+ }
+ NodeRef pathNodeRef = keyPair.getFirst();
+ QName propertyQName = keyPair.getSecond();
+ if (propertyQName == null)
+ {
+ // This is a path-based deletion
+ nodeService.deleteNode(pathNodeRef);
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Performed path-based delete: \n" +
+ " Key: " + key + "\n" +
+ " Node: " + pathNodeRef);
+ }
+ }
+ else
+ {
+ // This is a value-based deletion
+ nodeService.removeProperty(pathNodeRef, propertyQName);
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Performed value-based delete: \n" +
+ " Key: " + key + "\n" +
+ " Node: " + pathNodeRef + "\n" +
+ " Property: " + propertyQName);
+ }
+ }
+ // Done
+ }
}
diff --git a/source/java/org/alfresco/repo/admin/registry/RegistryServiceImplTest.java b/source/java/org/alfresco/repo/admin/registry/RegistryServiceImplTest.java
index 5180858bba..310b5de226 100644
--- a/source/java/org/alfresco/repo/admin/registry/RegistryServiceImplTest.java
+++ b/source/java/org/alfresco/repo/admin/registry/RegistryServiceImplTest.java
@@ -75,8 +75,11 @@ public class RegistryServiceImplTest extends TestCase
private static final Long VALUE_ONE = 1L;
private static final Long VALUE_TWO = 2L;
private static final Long VALUE_THREE = 3L;
+ private static final RegistryKey KEY_A = new RegistryKey(null, "a", null);
+ private static final RegistryKey KEY_A_B = new RegistryKey(null, "a", "b", null);
private static final RegistryKey KEY_A_B_0 = new RegistryKey(null, "a", "b", "0");
private static final RegistryKey KEY_A_B_1 = new RegistryKey(null, "a", "b", "1");
+ private static final RegistryKey KEY_A_B_C = new RegistryKey(null, "a", "b", "c", null);
private static final RegistryKey KEY_A_B_C_0 = new RegistryKey(null, "a", "b", "c", "0");
private static final RegistryKey KEY_A_B_C_1 = new RegistryKey(null, "a", "b", "c", "1");
private static final RegistryKey KEY_A_B_C_2 = new RegistryKey(null, "a", "b", "c", "2");
@@ -85,7 +88,12 @@ public class RegistryServiceImplTest extends TestCase
private static final RegistryKey KEY_A_B_C_D_1 = new RegistryKey(null, "a", "b", "c", "d", "1");
private static final RegistryKey KEY_A_B_C_D_2 = new RegistryKey(null, "a", "b", "c", "d", "2");
private static final RegistryKey KEY_A_B_C_D_3 = new RegistryKey(null, "a", "b", "c", "d", "3");
+ private static final RegistryKey KEY_X_Y_Z = new RegistryKey(null, "x", "y", "z", null);
private static final RegistryKey KEY_X_Y_Z_0 = new RegistryKey(null, "x", "y", "z", "0");
+ private static final RegistryKey KEY_X_Y_Z_1 = new RegistryKey(null, "x", "y", "z", "1");
+ private static final RegistryKey KEY_X_Y_Z_2 = new RegistryKey(null, "x", "y", "z", "2");
+ private static final RegistryKey KEY_X_Y_Z_D_1 = new RegistryKey(null, "x", "y", "z", "d", "1");
+ private static final RegistryKey KEY_X_Y_Z_D_2 = new RegistryKey(null, "x", "y", "z", "d", "2");
private static final RegistryKey KEY_SPECIAL = new RegistryKey(null, "me & you", "whatever");
private static final RegistryKey KEY_DOES_NOT_EXIST = new RegistryKey(null, "does", "not", "exist");
/**
@@ -93,29 +101,29 @@ public class RegistryServiceImplTest extends TestCase
*/
public void testProperUsage() throws Exception
{
- registryService.addValue(KEY_A_B_C_1, VALUE_ONE);
- registryService.addValue(KEY_A_B_C_2, VALUE_TWO);
- registryService.addValue(KEY_A_B_C_3, VALUE_THREE);
- registryService.addValue(KEY_A_B_C_D_1, VALUE_ONE);
- registryService.addValue(KEY_A_B_C_D_2, VALUE_TWO);
- registryService.addValue(KEY_A_B_C_D_3, VALUE_THREE);
+ registryService.addProperty(KEY_A_B_C_1, VALUE_ONE);
+ registryService.addProperty(KEY_A_B_C_2, VALUE_TWO);
+ registryService.addProperty(KEY_A_B_C_3, VALUE_THREE);
+ registryService.addProperty(KEY_A_B_C_D_1, VALUE_ONE);
+ registryService.addProperty(KEY_A_B_C_D_2, VALUE_TWO);
+ registryService.addProperty(KEY_A_B_C_D_3, VALUE_THREE);
- assertEquals("Incorrect value from service registry", VALUE_ONE, registryService.getValue(KEY_A_B_C_1));
- assertEquals("Incorrect value from service registry", VALUE_TWO, registryService.getValue(KEY_A_B_C_2));
- assertEquals("Incorrect value from service registry", VALUE_THREE, registryService.getValue(KEY_A_B_C_3));
- assertEquals("Incorrect value from service registry", VALUE_ONE, registryService.getValue(KEY_A_B_C_D_1));
- assertEquals("Incorrect value from service registry", VALUE_TWO, registryService.getValue(KEY_A_B_C_D_2));
- assertEquals("Incorrect value from service registry", VALUE_THREE, registryService.getValue(KEY_A_B_C_D_3));
+ assertEquals("Incorrect value from service registry", VALUE_ONE, registryService.getProperty(KEY_A_B_C_1));
+ assertEquals("Incorrect value from service registry", VALUE_TWO, registryService.getProperty(KEY_A_B_C_2));
+ assertEquals("Incorrect value from service registry", VALUE_THREE, registryService.getProperty(KEY_A_B_C_3));
+ assertEquals("Incorrect value from service registry", VALUE_ONE, registryService.getProperty(KEY_A_B_C_D_1));
+ assertEquals("Incorrect value from service registry", VALUE_TWO, registryService.getProperty(KEY_A_B_C_D_2));
+ assertEquals("Incorrect value from service registry", VALUE_THREE, registryService.getProperty(KEY_A_B_C_D_3));
- assertNull("Missing key should return null value", registryService.getValue(KEY_A_B_C_0));
- assertNull("Missing key should return null value", registryService.getValue(KEY_A_B_C_D_0));
- assertNull("Missing key should return null value", registryService.getValue(KEY_X_Y_Z_0));
+ assertNull("Missing key should return null value", registryService.getProperty(KEY_A_B_C_0));
+ assertNull("Missing key should return null value", registryService.getProperty(KEY_A_B_C_D_0));
+ assertNull("Missing key should return null value", registryService.getProperty(KEY_X_Y_Z_0));
}
public void testGetElements() throws Exception
{
- registryService.addValue(KEY_A_B_C_1, VALUE_ONE);
- registryService.addValue(KEY_A_B_C_2, VALUE_TWO);
+ registryService.addProperty(KEY_A_B_C_1, VALUE_ONE);
+ registryService.addProperty(KEY_A_B_C_2, VALUE_TWO);
// Check that we get an empty list for a random query
assertEquals("Excpected empty collection for random query", 0, registryService.getChildElements(KEY_DOES_NOT_EXIST).size());
@@ -130,7 +138,67 @@ public class RegistryServiceImplTest extends TestCase
public void testSpecialCharacters()
{
- registryService.addValue(KEY_SPECIAL, VALUE_THREE);
- assertEquals("Incorrect value for special key", VALUE_THREE, registryService.getValue(KEY_SPECIAL));
+ registryService.addProperty(KEY_SPECIAL, VALUE_THREE);
+ assertEquals("Incorrect value for special key", VALUE_THREE, registryService.getProperty(KEY_SPECIAL));
+ }
+
+ public void testDelete()
+ {
+ registryService.addProperty(KEY_A_B_C_1, VALUE_ONE);
+ registryService.addProperty(KEY_A_B_C_2, VALUE_TWO);
+ // Safety check
+ assertEquals("Incorrect value from service registry", VALUE_ONE, registryService.getProperty(KEY_A_B_C_1));
+ assertEquals("Incorrect value from service registry", VALUE_TWO, registryService.getProperty(KEY_A_B_C_2));
+
+ // Property-based delete
+ registryService.delete(KEY_A_B_C_1);
+ assertNull("Expected deleted value to be null", registryService.getProperty(KEY_A_B_C_1));
+
+ // Path-based delete
+ registryService.delete(KEY_A_B);
+ assertNull("Expected deleted value to be null", registryService.getProperty(KEY_A_B_C_1));
+ assertNull("Expected deleted value to be null", registryService.getProperty(KEY_A_B_C_2));
+
+ Collection childElements = registryService.getChildElements(KEY_A);
+ assertEquals("There should be no more elements within A", 0, childElements.size());
+ }
+
+ public void testCopy()
+ {
+ registryService.addProperty(KEY_A_B_C_1, VALUE_ONE);
+ registryService.addProperty(KEY_A_B_C_2, VALUE_TWO);
+ registryService.addProperty(KEY_A_B_C_D_1, VALUE_ONE);
+ registryService.addProperty(KEY_A_B_C_D_2, VALUE_TWO);
+
+ // Check illegal copy
+ try
+ {
+ registryService.copy(KEY_A_B_C_1, KEY_X_Y_Z);
+ fail("Failed to detect copy from property to path");
+ }
+ catch (Throwable e)
+ {
+ // Expected
+ }
+ try
+ {
+ registryService.copy(KEY_A_B_C, KEY_X_Y_Z_0);
+ fail("Failed to detect copy from path to property");
+ }
+ catch (Throwable e)
+ {
+ // Expected
+ }
+
+ // Property-based copy
+ registryService.copy(KEY_A_B_C_1, KEY_X_Y_Z_0);
+ assertEquals("Incorrect value after property copy", VALUE_ONE, registryService.getProperty(KEY_X_Y_Z_0));
+
+ // Path-based copy
+ registryService.copy(KEY_A_B_C, KEY_X_Y_Z);
+ assertEquals("Value not recursively copied during path copy", VALUE_ONE, registryService.getProperty(KEY_X_Y_Z_1));
+ assertEquals("Value not recursively copied during path copy", VALUE_TWO, registryService.getProperty(KEY_X_Y_Z_2));
+ assertEquals("Path not recursively copied during path copy", VALUE_ONE, registryService.getProperty(KEY_X_Y_Z_D_1));
+ assertEquals("Path not recursively copied during path copy", VALUE_TWO, registryService.getProperty(KEY_X_Y_Z_D_2));
}
}