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 1880438) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/write/NodeTypeRegistry.java (working copy) @@ -16,9 +16,13 @@ */ package org.apache.jackrabbit.oak.plugins.nodetype.write; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; + import javax.jcr.NamespaceRegistry; import javax.jcr.RepositoryException; import javax.jcr.ValueFactory; @@ -89,9 +93,24 @@ private void registerNodeTypes(InputStream stream, String systemId) { try { - CndImporter.registerNodeTypes( - new InputStreamReader(stream, Charsets.UTF_8), - systemId, ntMgr, nsReg, vf, false); + Reader reader = new InputStreamReader(stream, Charsets.UTF_8); + // 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.getBoolean("oak.referenceableFrozenNode"); + if (referenceableFrozenNode) { + BufferedReader bufferedReader = new BufferedReader(reader); + StringBuilder result = new StringBuilder(); + String line; + while ((line = bufferedReader.readLine()) != null) { + if (line.trim().equals("[nt:frozenNode]")) { + line = "[nt:frozenNode] > mix:referenceable"; + } + result.append(line).append(System.lineSeparator()); + } + reader = new StringReader(result.toString()); + } + + CndImporter.registerNodeTypes(reader, systemId, ntMgr, nsReg, vf, false); } catch (IOException e) { throw new IllegalStateException("Unable to read " + systemId, e); } catch (ParseException e) { 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 1880438) +++ 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 1880438) +++ 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,128 @@ +/* + * 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.After; +import org.junit.Test; + +/** + * Tests the implementation of mix:referenceable in nt:frozenNode. + * By default from JCR 2.0, nt:frozenNode shouldn't implement mix:referenceable + * However, the System Property oak.referenceableFrozenNode can be set to true to change this behaviour. + */ +public class ReferenceableFrozenNodeTest { + + private NodeState initializeRepository() throws CommitFailedException { + NodeStore store = new MemoryNodeStore(); + NodeBuilder builder = store.getRoot().builder(); + new InitialContent().initialize(builder); + store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); + return store.getRoot(); + } + + @After + public void clearSystemProperty() { + System.clearProperty("oak.referenceableFrozenNode"); + } + + @Test + public void testInitializeRepositoryWithoutReferenceableFrozenNode() throws CommitFailedException { + System.setProperty("oak.referenceableFrozenNode", "false"); + doTestInitializeRepositoryWithoutReferenceableFrozenNode(); + } + + @Test + public void testInitializeRepositoryWithoutReferenceableFrozenNode_noProperty() throws CommitFailedException { + System.clearProperty("oak.referenceableFrozenNode"); + doTestInitializeRepositoryWithoutReferenceableFrozenNode(); + } + + private void doTestInitializeRepositoryWithoutReferenceableFrozenNode() throws CommitFailedException { + 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)); + } + + @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)); + } + + @Test + public void testReinitializeRepositoryWithProperty() throws CommitFailedException { + System.setProperty("oak.referenceableFrozenNode", "false"); + NodeStore store = new MemoryNodeStore(); + NodeBuilder builder = store.getRoot().builder(); + new InitialContent().initialize(builder); + store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); + + System.setProperty("oak.referenceableFrozenNode", "true"); + new InitialContent().initialize(builder); + store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); + NodeState root = store.getRoot(); + + 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)); + } + + @Test + public void testReinitializeRepositoryWithoutProperty() throws CommitFailedException { + System.setProperty("oak.referenceableFrozenNode", "true"); + NodeStore store = new MemoryNodeStore(); + NodeBuilder builder = store.getRoot().builder(); + new InitialContent().initialize(builder); + store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); + + System.setProperty("oak.referenceableFrozenNode", "false"); + new InitialContent().initialize(builder); + store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); + NodeState root = store.getRoot(); + + 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)); + } +} Property changes on: oak-core/src/test/java/org/apache/jackrabbit/oak/ReferenceableFrozenNodeTest.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManagerNtTest.java =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManagerNtTest.java (nonexistent) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManagerNtTest.java (working copy) @@ -0,0 +1,68 @@ +/* + * 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.plugins.version; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import java.util.Arrays; +import java.util.Collection; + +import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.api.Tree; +import org.apache.jackrabbit.oak.spi.version.VersionConstants; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class ReadOnlyVersionManagerNtTest extends ReadOnlyVersionManagerTest { + + @Parameterized.Parameters + public static Collection oakReferenceableFrozenNode() { + return Arrays.asList("false", "true", null); + } + + private final String oakReferenceableFrozenNode; + + public ReadOnlyVersionManagerNtTest(String oakReferenceableFrozenNode) { + this.oakReferenceableFrozenNode = oakReferenceableFrozenNode; + if (oakReferenceableFrozenNode != null) { + System.setProperty("oak.referenceableFrozenNode", oakReferenceableFrozenNode); + } else { + System.clearProperty("oak.referenceableFrozenNode"); + } + } + + @Test + public void testNtFrozenNodeUuid() throws Exception { + Tree baseVersion = checkNotNull(versionManager.getBaseVersion(versionable)); + Tree frozen = baseVersion.getChild(VersionConstants.JCR_FROZENNODE); + PropertyState uuid = frozen.getProperty("jcr:uuid"); + if (oakReferenceableFrozenNode == null || + "false".equals(oakReferenceableFrozenNode)) { + assertNull(uuid); + } else if ("true".equals(oakReferenceableFrozenNode)) { + assertNotNull(uuid); + } else { + fail("not supported"); + } + } + +} Property changes on: oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManagerNtTest.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManagerTest.java =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManagerTest.java (revision 1880438) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManagerTest.java (working copy) @@ -44,10 +44,10 @@ public class ReadOnlyVersionManagerTest extends AbstractSecurityTest { - private Tree versionable; + protected Tree versionable; private String workspaceName; - private ReadOnlyVersionManager versionManager; + protected ReadOnlyVersionManager versionManager; @Override @Before