Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/NodeTypeRegistry.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/NodeTypeRegistry.java (revision 1879337) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/NodeTypeRegistry.java (working copy) @@ -16,6 +16,9 @@ */ package org.apache.jackrabbit.oak.plugins.nodetype.write; +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -89,6 +92,22 @@ private void registerNodeTypes(InputStream stream, String systemId) { try { + // OAK-9134: nt:frozenNode is not implementing mix:referenceable from JCR 2.0. + // This system property allows to add it back when initializing a repository. + boolean referenceableFrozenNode = Boolean.parseBoolean(System.getProperty("oak.referenceableFrozenNode")); + if (referenceableFrozenNode) { + BufferedReader breader = new BufferedReader(new InputStreamReader(stream)); + StringBuffer result = new StringBuffer(); + String line; + while ((line = breader.readLine()) != null) { + if (line.trim().equals("[nt:frozenNode]")) { + line = "[nt:frozenNode] > mix:referenceable"; + } + result.append(line).append(System.lineSeparator()); + } + stream = new BufferedInputStream(new ByteArrayInputStream(result.toString().getBytes())); + } + CndImporter.registerNodeTypes( new InputStreamReader(stream, Charsets.UTF_8), systemId, ntMgr, nsReg, vf, false); Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionableState.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionableState.java (revision 1879337) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionableState.java (working copy) @@ -37,6 +37,7 @@ import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE; import static org.apache.jackrabbit.JcrConstants.JCR_UUID; import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONHISTORY; +import static org.apache.jackrabbit.JcrConstants.MIX_REFERENCEABLE; import static org.apache.jackrabbit.JcrConstants.MIX_VERSIONABLE; import static org.apache.jackrabbit.JcrConstants.NT_FROZENNODE; import static org.apache.jackrabbit.JcrConstants.NT_VERSIONEDCHILD; @@ -50,6 +51,7 @@ import javax.jcr.RepositoryException; import javax.jcr.Value; +import javax.jcr.nodetype.NodeType; import javax.jcr.nodetype.PropertyDefinition; import javax.jcr.version.OnParentVersionAction; @@ -108,6 +110,8 @@ private final NodeBuilder versionable; private final ReadWriteVersionManager vMgr; private final ReadOnlyNodeTypeManager ntMgr; + + private final boolean isFrozenNodeReferenceable; private VersionableState(@NotNull NodeBuilder version, @NotNull NodeBuilder history, @@ -120,6 +124,21 @@ this.versionable = checkNotNull(versionable); this.vMgr = checkNotNull(vMgr); this.ntMgr = checkNotNull(ntMgr); + + boolean referenceableFound = false; + try { + NodeType[] superTypes = ntMgr.getNodeType(NT_FROZENNODE).getSupertypes(); + for (NodeType superType : superTypes) { + if (superType.isNodeType(MIX_REFERENCEABLE)) { + // OAK-9134: add uuid in older repositories with mix:referenceable in nt:frozenNode + referenceableFound = true; + break; + } + } + } catch (RepositoryException e) { + log.warn("Unable to access node type " + NT_FROZENNODE, e); + } + this.isFrozenNodeReferenceable = referenceableFound; } /** @@ -175,7 +194,10 @@ NodeBuilder node, String nodeId) { // initialize jcr:frozenNode - frozen.setProperty(JCR_UUID, UUIDUtils.generateUUID(), Type.STRING); + if (isFrozenNodeReferenceable) { + // OAK-9134: add uuid in older repositories with mix:referenceable in nt:frozenNode + frozen.setProperty(JCR_UUID, UUIDUtils.generateUUID(), Type.STRING); + } frozen.setProperty(JCR_PRIMARYTYPE, NT_FROZENNODE, Type.NAME); List mixinTypes; if (node.hasProperty(JCR_MIXINTYPES)) { Index: oak-core/src/main/resources/org/apache/jackrabbit/oak/builtin_nodetypes.cnd =================================================================== --- oak-core/src/main/resources/org/apache/jackrabbit/oak/builtin_nodetypes.cnd (revision 1879337) +++ oak-core/src/main/resources/org/apache/jackrabbit/oak/builtin_nodetypes.cnd (working copy) @@ -416,7 +416,7 @@ /** * @since 1.0 */ -[nt:frozenNode] > mix:referenceable +[nt:frozenNode] orderable - jcr:frozenPrimaryType (NAME) mandatory autocreated protected ABORT - jcr:frozenMixinTypes (NAME) protected multiple ABORT Index: oak-core/src/test/java/org/apache/jackrabbit/oak/ReferenceableFrozenNodeTest.java =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/ReferenceableFrozenNodeTest.java (nonexistent) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/ReferenceableFrozenNodeTest.java (working copy) @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.oak; + +import static org.junit.Assert.assertEquals; + +import org.apache.jackrabbit.oak.api.CommitFailedException; +import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.api.Type; +import org.apache.jackrabbit.oak.spi.commit.CommitInfo; +import org.apache.jackrabbit.oak.spi.commit.EmptyHook; +import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore; +import org.apache.jackrabbit.oak.spi.state.NodeBuilder; +import org.apache.jackrabbit.oak.spi.state.NodeState; +import org.apache.jackrabbit.oak.spi.state.NodeStore; +import org.junit.Test; + +/** + * Tests the query engine using the default index implementation: the + * {@link PropertyIndexProvider} + */ +public class ReferenceableFrozenNodeTest { + + private NodeState initializeRepository() { + NodeStore store = new MemoryNodeStore(); + NodeBuilder builder = store.getRoot().builder(); + new InitialContent().initialize(builder); + try { + store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); + } catch (CommitFailedException e) { + } + return store.getRoot(); + } + + @Test + public void testInitializeRepositoryWithoutReferenceableFrozenNode() throws CommitFailedException { + System.setProperty("oak.referenceableFrozenNode", "false"); + NodeState root = initializeRepository(); + + NodeState frozenNode = root.getChildNode("jcr:system").getChildNode("jcr:nodeTypes").getChildNode("nt:frozenNode"); + PropertyState superTypes = frozenNode.getProperty("jcr:supertypes"); + + assertEquals(1, superTypes.count()); + assertEquals("nt:base", superTypes.getValue(Type.NAME, 0)); + + System.clearProperty("oak.referenceableFrozenNode"); + } + + @Test + public void testInitializeRepositoryWithReferenceableFrozenNode() throws CommitFailedException { + System.setProperty("oak.referenceableFrozenNode", "true"); + NodeState root = initializeRepository(); + + NodeState frozenNode = root.getChildNode("jcr:system").getChildNode("jcr:nodeTypes").getChildNode("nt:frozenNode"); + PropertyState superTypes = frozenNode.getProperty("jcr:supertypes"); + + assertEquals(2, superTypes.count()); + assertEquals("nt:base", superTypes.getValue(Type.NAME, 0)); + assertEquals("mix:referenceable", superTypes.getValue(Type.NAME, 1)); + + System.clearProperty("oak.referenceableFrozenNode"); + } +}