You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by al...@apache.org on 2013/10/10 14:00:40 UTC

svn commit: r1530931 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/plugins/name/ main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/ test/java/org/apache/jackrabbit/oak/plugins/name/

Author: alexparvulescu
Date: Thu Oct 10 12:00:39 2013
New Revision: 1530931

URL: http://svn.apache.org/r1530931
Log:
OAK-924 Optimize namespace lookups
 - initial patch in, still WIP


Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NameValidator.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NameValidatorProvider.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NamespaceConstants.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NamespaceValidator.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NamespaceValidatorProvider.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/Namespaces.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/ReadOnlyNamespaceRegistry.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/ReadWriteNamespaceRegistry.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/InitialContent.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/name/ReadWriteNamespaceRegistryTest.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NameValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NameValidator.java?rev=1530931&r1=1530930&r2=1530931&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NameValidator.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NameValidator.java Thu Oct 10 12:00:39 2013
@@ -20,6 +20,7 @@ import java.util.Set;
 
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.core.ImmutableTree;
 import org.apache.jackrabbit.oak.spi.commit.DefaultValidator;
 import org.apache.jackrabbit.oak.spi.commit.Validator;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -34,7 +35,11 @@ class NameValidator extends DefaultValid
 
     private final Set<String> prefixes;
 
-    public NameValidator(Set<String> prefixes) {
+    public NameValidator(NodeState root) {
+        this.prefixes = Namespaces.getNamespacePrefixesAsSet(new ImmutableTree(root));
+    }
+
+    NameValidator(Set<String> prefixes) {
         this.prefixes = prefixes;
     }
 
@@ -44,7 +49,7 @@ class NameValidator extends DefaultValid
             String prefix = name.substring(0, colon);
             if (prefix.isEmpty() || !prefixes.contains(prefix)) {
                 throw new CommitFailedException(
-                        CommitFailedException.NAME, 1, "Invalid namespace prefix: " + name);
+                        CommitFailedException.NAME, 1, "Invalid namespace prefix("+prefixes+"): " + prefix);
             }
         }
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NameValidatorProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NameValidatorProvider.java?rev=1530931&r1=1530930&r2=1530931&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NameValidatorProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NameValidatorProvider.java Thu Oct 10 12:00:39 2013
@@ -18,7 +18,6 @@ package org.apache.jackrabbit.oak.plugin
 
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Service;
-import org.apache.jackrabbit.oak.core.ImmutableTree;
 import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
 import org.apache.jackrabbit.oak.spi.commit.Validator;
 import org.apache.jackrabbit.oak.spi.commit.ValidatorProvider;
@@ -35,8 +34,7 @@ public class NameValidatorProvider exten
 
     @Override
     public Validator getRootValidator(NodeState before, NodeState after) {
-        return new NameValidator(
-                Namespaces.getNamespaceMap(new ImmutableTree(after)).keySet());
+        return new NameValidator(after);
     }
 
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NamespaceConstants.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NamespaceConstants.java?rev=1530931&r1=1530930&r2=1530931&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NamespaceConstants.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NamespaceConstants.java Thu Oct 10 12:00:39 2013
@@ -68,4 +68,15 @@ public interface NamespaceConstants {
             NAMESPACE_REP,
             NAMESPACE_SV
     ));
-}
\ No newline at end of file
+
+    String EMPTY_KEY = "oak:empty";
+
+    // index nodes for faster lookup
+
+    String NSDATA = "oak:namespaces";
+
+    String NSDATA_URIS = "oak:uris";
+
+    String NSDATA_PREFIXES = "oak:prefixes";
+
+}

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NamespaceValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NamespaceValidator.java?rev=1530931&r1=1530930&r2=1530931&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NamespaceValidator.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NamespaceValidator.java Thu Oct 10 12:00:39 2013
@@ -16,40 +16,70 @@
  */
 package org.apache.jackrabbit.oak.plugins.name;
 
+import static javax.jcr.NamespaceRegistry.PREFIX_JCR;
+import static javax.jcr.NamespaceRegistry.PREFIX_MIX;
+import static javax.jcr.NamespaceRegistry.PREFIX_NT;
+
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+import static org.apache.jackrabbit.oak.plugins.name.NamespaceConstants.NSDATA;
+import static org.apache.jackrabbit.oak.plugins.name.NamespaceConstants.NSDATA_PREFIXES;
+import static org.apache.jackrabbit.oak.plugins.name.NamespaceConstants.NSDATA_URIS;
+import static org.apache.jackrabbit.oak.plugins.name.NamespaceConstants.REP_NAMESPACES;
+import static org.apache.jackrabbit.oak.plugins.name.Namespaces.encodeUri;
+import static org.apache.jackrabbit.oak.plugins.name.Namespaces.escapePropertyKey;
+import static org.apache.jackrabbit.oak.plugins.name.Namespaces.isValidPrefix;
+import static org.apache.jackrabbit.oak.plugins.name.Namespaces.safeGet;
+import static org.apache.jackrabbit.oak.plugins.name.Namespaces.unescapePropertyKey;
+
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
 
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.spi.commit.DefaultValidator;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.core.ImmutableTree;
+import org.apache.jackrabbit.oak.spi.commit.DefaultEditor;
+import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
 
-import static org.apache.jackrabbit.oak.api.Type.STRING;
+import com.google.common.collect.ImmutableSet;
 
 /**
  * TODO document
  */
-class NamespaceValidator extends DefaultValidator {
+class NamespaceValidator extends DefaultEditor {
 
-    private final Map<String, String> map;
+    private final NodeBuilder builder;
 
-    public NamespaceValidator(Map<String, String> map) {
-        this.map = map;
+    private boolean modified = false;
+
+    private final NodeState namespaces;
+
+    public NamespaceValidator(NodeState root, NodeBuilder builder) {
+        this.namespaces = root.getChildNode(JCR_SYSTEM).getChildNode(
+                REP_NAMESPACES);
+        this.builder = builder;
     }
 
-    //----------------------------------------------------------< Validator >---
     @Override
-    public void propertyAdded(PropertyState after)
-            throws CommitFailedException {
+    public void propertyAdded(PropertyState after) throws CommitFailedException {
         String prefix = after.getName();
         // ignore jcr:primaryType
-        if (prefix.equals("jcr:primaryType")) {
+        if (JCR_PRIMARYTYPE.equals(prefix)) {
             return;
         }
-        if (map.containsKey(prefix)) {
-            throw new CommitFailedException(
-                    CommitFailedException.NAMESPACE, 1,
+
+        if (namespaces.hasProperty(prefix)) {
+            throw new CommitFailedException(CommitFailedException.NAMESPACE, 1,
                     "Namespace mapping already registered: " + prefix);
-        } else if (Namespaces.isValidPrefix(prefix)) {
+        } else if (isValidPrefix(prefix)) {
             if (after.isArray() || !STRING.equals(after.getType())) {
                 throw new CommitFailedException(
                         CommitFailedException.NAMESPACE, 2,
@@ -58,36 +88,90 @@ class NamespaceValidator extends Default
                 throw new CommitFailedException(
                         CommitFailedException.NAMESPACE, 3,
                         "XML prefixes are reserved: " + prefix);
-            } else if (map.containsValue(after.getValue(STRING))) {
+            } else if (containsValue(namespaces, after.getValue(STRING))) {
                 throw modificationNotAllowed(prefix);
             }
         } else {
-            throw new CommitFailedException(
-                    CommitFailedException.NAMESPACE, 4,
+            throw new CommitFailedException(CommitFailedException.NAMESPACE, 4,
                     "Not a valid namespace prefix: " + prefix);
         }
+        modified = true;
+    }
+
+    private static boolean containsValue(NodeState namespaces, String value) {
+        return safeGet(new ImmutableTree(namespaces.getChildNode(NSDATA)),
+                NSDATA_URIS).contains(value);
     }
 
     @Override
     public void propertyChanged(PropertyState before, PropertyState after)
             throws CommitFailedException {
-        if (map.containsKey(after.getName())) {
-            throw modificationNotAllowed(after.getName());
-        }
+        // TODO allow changes if there is no content referencing the mappings
+        throw modificationNotAllowed(after.getName());
     }
 
     @Override
     public void propertyDeleted(PropertyState before)
             throws CommitFailedException {
-        if (map.containsKey(before.getName())) {
-            // TODO: Check whether this namespace is still used in content
+
+        // FIXME Desired Behavior: if we enable it, there are a few generic
+        // #unregister tests that fail
+        // TODO allow changes if there is no content referencing the mappings
+        // throw modificationNotAllowed(before.getName());
+
+        // FIXME Best effort backwards compatible:
+        if (jcrSystemNS.contains(before.getName())) {
+            throw modificationNotAllowed(before.getName());
         }
+        modified = true;
     }
 
+    private static Set<String> jcrSystemNS = ImmutableSet.of(PREFIX_JCR,
+            PREFIX_NT, PREFIX_MIX, NamespaceConstants.PREFIX_SV);
+
     private static CommitFailedException modificationNotAllowed(String prefix) {
-        return new CommitFailedException(
-                CommitFailedException.NAMESPACE, 5,
+        return new CommitFailedException(CommitFailedException.NAMESPACE, 5,
                 "Namespace modification not allowed: " + prefix);
     }
 
+    @Override
+    public void leave(NodeState before, NodeState after)
+            throws CommitFailedException {
+        if (!modified) {
+            return;
+        }
+
+        Set<String> prefixes = new HashSet<String>();
+        Set<String> uris = new HashSet<String>();
+        Map<String, String> reverse = new HashMap<String, String>();
+
+        NodeBuilder namespaces = builder.child(JCR_SYSTEM)
+                .child(REP_NAMESPACES);
+        for (PropertyState property : namespaces.getProperties()) {
+            String prefix = unescapePropertyKey(property.getName());
+            if (STRING.equals(property.getType()) && isValidPrefix(prefix)) {
+                prefixes.add(prefix);
+                String uri = property.getValue(STRING);
+                uris.add(uri);
+                reverse.put(escapePropertyKey(uri), prefix);
+            }
+        }
+
+        NodeBuilder data = namespaces.setChildNode(NSDATA);
+        data.setProperty(NSDATA_PREFIXES, prefixes, Type.STRINGS);
+        data.setProperty(NSDATA_URIS, uris, Type.STRINGS);
+        for (Entry<String, String> e : reverse.entrySet()) {
+            data.setProperty(encodeUri(e.getKey()), e.getValue());
+        }
+    }
+
+    @Override
+    public Editor childNodeChanged(String name, NodeState before,
+            NodeState after) throws CommitFailedException {
+        if (NSDATA.equals(name) && !before.equals(after)) {
+            throw modificationNotAllowed(name);
+        }
+        return null;
+    }
+
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NamespaceValidatorProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NamespaceValidatorProvider.java?rev=1530931&r1=1530930&r2=1530931&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NamespaceValidatorProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/NamespaceValidatorProvider.java Thu Oct 10 12:00:39 2013
@@ -21,11 +21,11 @@ import static org.apache.jackrabbit.oak.
 
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Service;
-import org.apache.jackrabbit.oak.core.ImmutableTree;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.spi.commit.Editor;
 import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
-import org.apache.jackrabbit.oak.spi.commit.SubtreeValidator;
-import org.apache.jackrabbit.oak.spi.commit.Validator;
-import org.apache.jackrabbit.oak.spi.commit.ValidatorProvider;
+import org.apache.jackrabbit.oak.spi.commit.SubtreeEditor;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
 /**
@@ -35,13 +35,12 @@ import org.apache.jackrabbit.oak.spi.sta
  */
 @Component
 @Service(EditorProvider.class)
-public class NamespaceValidatorProvider extends ValidatorProvider {
+public class NamespaceValidatorProvider implements EditorProvider {
 
     @Override
-    public Validator getRootValidator(NodeState before, NodeState after) {
-        Validator validator = new NamespaceValidator(
-                Namespaces.getNamespaceMap(new ImmutableTree(before)));
-        return new SubtreeValidator(validator, JCR_SYSTEM, REP_NAMESPACES);
+    public Editor getRootEditor(NodeState before, NodeState after,
+            NodeBuilder builder) throws CommitFailedException {
+        return new SubtreeEditor(new NamespaceValidator(before, builder), JCR_SYSTEM, REP_NAMESPACES);
     }
 
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/Namespaces.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/Namespaces.java?rev=1530931&r1=1530930&r2=1530931&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/Namespaces.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/Namespaces.java Thu Oct 10 12:00:39 2013
@@ -16,16 +16,34 @@
 */
 package org.apache.jackrabbit.oak.plugins.name;
 
+import static javax.jcr.NamespaceRegistry.NAMESPACE_EMPTY;
+import static javax.jcr.NamespaceRegistry.NAMESPACE_JCR;
+import static javax.jcr.NamespaceRegistry.NAMESPACE_MIX;
+import static javax.jcr.NamespaceRegistry.NAMESPACE_NT;
+import static javax.jcr.NamespaceRegistry.NAMESPACE_XML;
+import static javax.jcr.NamespaceRegistry.PREFIX_EMPTY;
+import static javax.jcr.NamespaceRegistry.PREFIX_JCR;
+import static javax.jcr.NamespaceRegistry.PREFIX_MIX;
+import static javax.jcr.NamespaceRegistry.PREFIX_NT;
+import static javax.jcr.NamespaceRegistry.PREFIX_XML;
 import static org.apache.jackrabbit.oak.api.Type.STRING;
+import static org.apache.jackrabbit.oak.api.Type.STRINGS;
+import static org.apache.jackrabbit.oak.api.Type.NAME;
 
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
-
-import javax.jcr.NamespaceRegistry;
+import java.util.Set;
 
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.util.Text;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
 
 /**
  * Internal static utility class for managing the persisted namespace registry.
@@ -35,40 +53,150 @@ public class Namespaces implements Names
     private Namespaces() {
     }
 
-    private static final Map<String, String> DEFAULTS = new HashMap<String, String>();
-    static {
+    public static void setupNamespaces(NodeBuilder system) {
+        if (system.hasChildNode(REP_NAMESPACES)) {
+            return;
+        }
+
+        NodeBuilder namespaces = system.child(REP_NAMESPACES);
+        namespaces.setProperty(JcrConstants.JCR_PRIMARYTYPE,
+                JcrConstants.NT_UNSTRUCTURED, NAME);
+
         // Standard namespace specified by JCR (default one not included)
-        DEFAULTS.put(NamespaceRegistry.PREFIX_EMPTY, NamespaceRegistry.NAMESPACE_EMPTY);
-        DEFAULTS.put(NamespaceRegistry.PREFIX_JCR, NamespaceRegistry.NAMESPACE_JCR);
-        DEFAULTS.put(NamespaceRegistry.PREFIX_NT,  NamespaceRegistry.NAMESPACE_NT);
-        DEFAULTS.put(NamespaceRegistry.PREFIX_MIX, NamespaceRegistry.NAMESPACE_MIX);
-        DEFAULTS.put(NamespaceRegistry.PREFIX_XML, NamespaceRegistry.NAMESPACE_XML);
+        namespaces.setProperty(escapePropertyKey(PREFIX_EMPTY), NAMESPACE_EMPTY);
+        namespaces.setProperty(PREFIX_JCR, NAMESPACE_JCR);
+        namespaces.setProperty(PREFIX_NT,  NAMESPACE_NT);
+        namespaces.setProperty(PREFIX_MIX, NAMESPACE_MIX);
+        namespaces.setProperty(PREFIX_XML, NAMESPACE_XML);
 
         // Namespace included in Jackrabbit 2.x
-        DEFAULTS.put(PREFIX_SV, NAMESPACE_SV);
-        DEFAULTS.put(PREFIX_REP, NAMESPACE_REP);
+        namespaces.setProperty(PREFIX_SV, NAMESPACE_SV);
+        namespaces.setProperty(PREFIX_REP, NAMESPACE_REP);
+
+        // index node for faster lookup
+        NodeBuilder data = namespaces.child(NSDATA);
+        data.setProperty(NSDATA_PREFIXES, ImmutableList.of(PREFIX_EMPTY, PREFIX_JCR, PREFIX_NT, PREFIX_MIX, PREFIX_XML, PREFIX_SV, PREFIX_REP), STRINGS);
+        data.setProperty(NSDATA_URIS, ImmutableList.of(NAMESPACE_EMPTY, NAMESPACE_JCR, NAMESPACE_NT, NAMESPACE_MIX, NAMESPACE_XML, NAMESPACE_SV, NAMESPACE_REP), STRINGS);
+
+        data.setProperty(encodeUri(escapePropertyKey(NAMESPACE_EMPTY)), PREFIX_EMPTY);
+        data.setProperty(encodeUri(NAMESPACE_JCR), PREFIX_JCR);
+        data.setProperty(encodeUri(NAMESPACE_NT), PREFIX_NT);
+        data.setProperty(encodeUri(NAMESPACE_MIX), PREFIX_MIX);
+        data.setProperty(encodeUri(NAMESPACE_XML), PREFIX_XML);
+        data.setProperty(encodeUri(NAMESPACE_SV), PREFIX_SV);
+        data.setProperty(encodeUri(NAMESPACE_REP), PREFIX_REP);
+    }
+
+    private static Tree getNamespaceTree(Tree root) {
+        return root.getChild(JcrConstants.JCR_SYSTEM).getChild(REP_NAMESPACES);
     }
 
     public static Map<String, String> getNamespaceMap(Tree root) {
-        Map<String, String> map = new HashMap<String, String>(DEFAULTS);
+        Map<String, String> map = new HashMap<String, String>();
 
-        Tree namespaces = root.getChild(JcrConstants.JCR_SYSTEM).getChild(REP_NAMESPACES);
+        Tree namespaces = getNamespaceTree(root);
         for (PropertyState property : namespaces.getProperties()) {
             String prefix = property.getName();
-            if (!property.isArray() && isValidPrefix(prefix)) {
-                String value = property.getValue(STRING);
-                if (STRING.equals(property.getType())) {
-                    map.put(prefix, value);
-                }
+            if (STRING.equals(property.getType()) && isValidPrefix(prefix)) {
+                map.put(unescapePropertyKey(prefix), property.getValue(STRING));
             }
         }
 
         return map;
     }
 
+    static String[] getNamespacePrefixes(Tree root) {
+        Set<String> prefSet = getNamespacePrefixesAsSet(root);
+        String[] prefixes = prefSet.toArray(new String[prefSet.size()]);
+        Arrays.sort(prefixes);
+        return prefixes;
+    }
+
+    static Set<String> getNamespacePrefixesAsSet(Tree root) {
+        return safeGet(getNamespaceTree(root).getChild(NSDATA), NSDATA_PREFIXES);
+    }
+
+    static String getNamespacePrefix(Tree root, String uri) {
+        Tree namespaces = getNamespaceTree(root);
+        PropertyState ps = namespaces.getChild(NSDATA)
+                .getProperty(encodeUri(escapePropertyKey(uri)));
+        if (ps != null) {
+            return ps.getValue(STRING);
+        }
+        return null;
+    }
+
+    static String[] getNamespaceURIs(Tree root) {
+        Set<String> uris = safeGet(getNamespaceTree(root).getChild(NSDATA), NSDATA_URIS);
+        return uris.toArray(new String[uris.size()]);
+    }
+
+    static String getNamespaceURI(Tree root, String prefix) {
+        if (isValidPrefix(prefix)) {
+            PropertyState property = getNamespaceTree(root).getProperty(
+                    escapePropertyKey(prefix));
+            if (property != null && STRING.equals(property.getType())) {
+                return property.getValue(STRING);
+            }
+        }
+        return null;
+    }
+
+    // utils
+
+    /**
+     * Replaces an empty string with the special {@link #EMPTY_KEY} value.
+     *
+     * @see #unescapePropertyKey(String)
+     * @param key property key
+     * @return escaped property key
+     */
+    static String escapePropertyKey(String key) {
+        if (key.equals("")) {
+            return EMPTY_KEY;
+        } else {
+            return key;
+        }
+    }
+
+    /**
+     * Converts the special {@link #EMPTY_KEY} value back to an empty string.
+     *
+     * @see #escapePropertyKey(String)
+     * @param key property key
+     * @return escaped property key
+     */
+    static String unescapePropertyKey(String key) {
+        if (key.equals(EMPTY_KEY)) {
+            return "";
+        } else {
+            return key;
+        }
+    }
+
+    /**
+     * encodes the uri value to be used as a property
+     * 
+     * @param uri
+     * @return encoded uri
+     */
+    static String encodeUri(String uri) {
+        return Text.escapeIllegalJcrChars(uri);
+    }
+
+    static Set<String> safeGet(Tree tree, String name) {
+        PropertyState ps = tree.getProperty(name);
+        if (ps == null) {
+            return Sets.newHashSet();
+        }
+        return Sets.newHashSet(ps.getValue(Type.STRINGS));
+    }
+
+    // validation
+
     public static boolean isValidPrefix(String prefix) {
         // TODO: Other prefix rules?
-        return !prefix.isEmpty() && prefix.indexOf(':') == -1;
+        return prefix.indexOf(':') == -1;
     }
 
     public static boolean isValidLocalName(String local) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/ReadOnlyNamespaceRegistry.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/ReadOnlyNamespaceRegistry.java?rev=1530931&r1=1530930&r2=1530931&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/ReadOnlyNamespaceRegistry.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/ReadOnlyNamespaceRegistry.java Thu Oct 10 12:00:39 2013
@@ -16,9 +16,6 @@
  */
 package org.apache.jackrabbit.oak.plugins.name;
 
-import java.util.Arrays;
-import java.util.Map;
-
 import javax.annotation.Nonnull;
 import javax.jcr.NamespaceException;
 import javax.jcr.NamespaceRegistry;
@@ -63,11 +60,7 @@ public abstract class ReadOnlyNamespaceR
     @Nonnull
     public String[] getPrefixes() throws RepositoryException {
         try {
-            Tree root = getReadTree();
-            Map<String, String> map = Namespaces.getNamespaceMap(root);
-            String[] prefixes = map.keySet().toArray(new String[map.size()]);
-            Arrays.sort(prefixes);
-            return prefixes;
+            return Namespaces.getNamespacePrefixes(getReadTree());
         } catch (RuntimeException e) {
             throw new RepositoryException(
                     "Failed to retrieve registered namespace prefixes", e);
@@ -78,10 +71,7 @@ public abstract class ReadOnlyNamespaceR
     @Nonnull
     public String[] getURIs() throws RepositoryException {
         try {
-            Tree root = getReadTree();
-            Map<String, String> map = Namespaces.getNamespaceMap(root);
-            String[] uris = map.values().toArray(new String[map.size()]);
-            return uris;
+            return Namespaces.getNamespaceURIs(getReadTree());
         } catch (RuntimeException e) {
             throw new RepositoryException(
                     "Failed to retrieve registered namespace URIs", e);
@@ -92,9 +82,7 @@ public abstract class ReadOnlyNamespaceR
     @Nonnull
     public String getURI(String prefix) throws RepositoryException {
         try {
-            Tree root = getReadTree();
-            Map<String, String> map = Namespaces.getNamespaceMap(root);
-            String uri = map.get(prefix);
+            String uri = Namespaces.getNamespaceURI(getReadTree(), prefix);
             if (uri == null) {
                 throw new NamespaceException(
                         "No namespace registered for prefix " + prefix);
@@ -111,15 +99,12 @@ public abstract class ReadOnlyNamespaceR
     @Nonnull
     public String getPrefix(String uri) throws RepositoryException {
         try {
-            Tree root = getReadTree();
-            Map<String, String> map = Namespaces.getNamespaceMap(root);
-            for (Map.Entry<String, String> entry : map.entrySet()) {
-                if (entry.getValue().equals(uri)) {
-                    return entry.getKey();
-                }
-            }
-            throw new NamespaceException(
+            String prefix = Namespaces.getNamespacePrefix(getReadTree(), uri);
+            if (prefix == null) {
+                throw new NamespaceException(
                         "No namespace prefix registered for URI " + uri);
+            }
+            return prefix;
         } catch (RuntimeException e) {
             throw new RepositoryException(
                     "Failed to retrieve the namespace prefix for URI "

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/ReadWriteNamespaceRegistry.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/ReadWriteNamespaceRegistry.java?rev=1530931&r1=1530930&r2=1530931&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/ReadWriteNamespaceRegistry.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/name/ReadWriteNamespaceRegistry.java Thu Oct 10 12:00:39 2013
@@ -16,18 +16,15 @@
  */
 package org.apache.jackrabbit.oak.plugins.name;
 
-import java.util.Map;
+import static org.apache.jackrabbit.oak.plugins.name.Namespaces.getNamespaceURI;
+
 import javax.jcr.NamespaceException;
 import javax.jcr.RepositoryException;
 
-import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
-import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
 
-import static org.apache.jackrabbit.oak.api.Type.NAME;
-import static org.apache.jackrabbit.oak.api.Type.STRING;
 
 /**
  * Writable namespace registry. Mainly for use to implement the full JCR API.
@@ -56,42 +53,22 @@ public abstract class ReadWriteNamespace
         // do nothing
     }
 
-    private static Tree getOrCreate(Root root, String... path) {
-        Tree tree = root.getTree("/");
-        assert tree.exists();
-        for (String name : path) {
-            Tree child = tree.getChild(name);
-            if (!child.exists()) {
-                child = tree.addChild(name);
-            }
-            tree = child;
-        }
-        return tree;
-    }
-
     //--------------------------------------------------< NamespaceRegistry >---
 
     @Override
     public void registerNamespace(String prefix, String uri)
             throws RepositoryException {
-        Map<String, String> map = Namespaces.getNamespaceMap(getReadTree());
-        if (uri.equals(map.get(prefix))) {
+        if (uri.equals(getNamespaceURI(getReadTree(), prefix))) {
             return; // Namespace already registered, so we do nothing
         }
-
         try {
             Root root = getWriteRoot();
-            Tree namespaces =
-                    getOrCreate(root, JcrConstants.JCR_SYSTEM, REP_NAMESPACES);
-            if (!namespaces.hasProperty(JcrConstants.JCR_PRIMARYTYPE)) {
-                namespaces.setProperty(JcrConstants.JCR_PRIMARYTYPE,
-                        JcrConstants.NT_UNSTRUCTURED, NAME);
-            }
+            Tree namespaces = root.getTree(NAMESPACES_PATH);
+
             // remove existing mapping to given uri
-            for (PropertyState p : namespaces.getProperties()) {
-                if (!p.isArray() && p.getValue(STRING).equals(uri)) {
-                    namespaces.removeProperty(p.getName());
-                }
+            String ns = Namespaces.getNamespacePrefix(namespaces, uri);
+            if (ns != null) {
+                namespaces.removeProperty(ns);
             }
             namespaces.setProperty(prefix, uri);
             root.commit();

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/InitialContent.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/InitialContent.java?rev=1530931&r1=1530930&r2=1530931&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/InitialContent.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/InitialContent.java Thu Oct 10 12:00:39 2013
@@ -28,9 +28,12 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.index.IndexUtils;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
 import org.apache.jackrabbit.oak.plugins.memory.ModifiedNodeState;
+import org.apache.jackrabbit.oak.plugins.name.NamespaceValidatorProvider;
+import org.apache.jackrabbit.oak.plugins.name.Namespaces;
 import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants;
 import org.apache.jackrabbit.oak.plugins.nodetype.RegistrationEditorProvider;
 import org.apache.jackrabbit.oak.plugins.version.VersionConstants;
+import org.apache.jackrabbit.oak.spi.commit.CompositeEditorProvider;
 import org.apache.jackrabbit.oak.spi.commit.EditorHook;
 import org.apache.jackrabbit.oak.spi.lifecycle.RepositoryInitializer;
 import org.apache.jackrabbit.oak.spi.state.ApplyDiff;
@@ -68,6 +71,8 @@ public class InitialContent implements R
                     .setProperty(JCR_PRIMARYTYPE, NT_REP_NODE_TYPES, Type.NAME);
             system.child(VersionConstants.JCR_ACTIVITIES)
                     .setProperty(JCR_PRIMARYTYPE, VersionConstants.REP_ACTIVITIES, Type.NAME);
+
+            Namespaces.setupNamespaces(system);
         }
 
         if (!builder.hasChildNode(IndexConstants.INDEX_DEFINITIONS_NAME)) {
@@ -84,8 +89,9 @@ public class InitialContent implements R
 
         NodeState base = builder.getNodeState();
         NodeStore store = new MemoryNodeStore(base);
-        BuiltInNodeTypes.register(new SystemRoot(
-                store, new EditorHook(new RegistrationEditorProvider())));
+        BuiltInNodeTypes.register(new SystemRoot(store, new EditorHook(
+                new CompositeEditorProvider(new NamespaceValidatorProvider(),
+                        new RegistrationEditorProvider()))));
         NodeState target = store.getRoot();
         target.compareAgainstBaseState(base, new ApplyDiff(builder));
     }

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/name/ReadWriteNamespaceRegistryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/name/ReadWriteNamespaceRegistryTest.java?rev=1530931&r1=1530930&r2=1530931&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/name/ReadWriteNamespaceRegistryTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/name/ReadWriteNamespaceRegistryTest.java Thu Oct 10 12:00:39 2013
@@ -21,10 +21,13 @@ import static org.junit.Assert.assertEqu
 import javax.jcr.NamespaceRegistry;
 
 import org.apache.jackrabbit.oak.NodeStoreFixture;
+import org.apache.jackrabbit.oak.Oak;
 import org.apache.jackrabbit.oak.OakBaseTest;
 import org.apache.jackrabbit.oak.api.ContentSession;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent;
+import org.apache.jackrabbit.oak.spi.security.OpenSecurityProvider;
 import org.junit.Test;
 
 public class ReadWriteNamespaceRegistryTest extends OakBaseTest {
@@ -33,6 +36,13 @@ public class ReadWriteNamespaceRegistryT
         super(fixture);
     }
 
+    @Override
+    protected ContentSession createContentSession() {
+        return new Oak(store).with(new OpenSecurityProvider())
+                .with(new InitialContent())
+                .with(new NamespaceValidatorProvider()).createContentSession();
+    }
+
     @Test
     public void testMappings() throws Exception {
         final ContentSession session = createContentSession();