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();
