Author: tomekr
Date: Thu Apr 14 12:28:18 2016
New Revision: 1739069
URL: http://svn.apache.org/viewvc?rev=1739069&view=rev
Log:
OAK-4166: Fixed version-related properties for the simple versionable nodes
Added:
jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionablePropertiesEditor.java
Modified:
jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java
jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistoryTest.java
Modified:
jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java?rev=1739069&r1=1739068&r2=1739069&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java
(original)
+++
jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositoryUpgrade.java
Thu Apr 14 12:28:18 2016
@@ -117,6 +117,7 @@ import org.apache.jackrabbit.oak.upgrade
import org.apache.jackrabbit.oak.upgrade.security.RestrictionEditorProvider;
import org.apache.jackrabbit.oak.upgrade.version.VersionCopyConfiguration;
import org.apache.jackrabbit.oak.upgrade.version.VersionableEditor;
+import org.apache.jackrabbit.oak.upgrade.version.VersionablePropertiesEditor;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.QNodeDefinition;
import org.apache.jackrabbit.spi.QNodeTypeDefinition;
@@ -470,6 +471,10 @@ public class RepositoryUpgrade {
new SameNameSiblingsEditor.Provider()
)));
+ // this editor works on the VersionableEditor output, so it can't
be
+ // a part of the same EditorHook
+ hooks.add(new EditorHook(new
VersionablePropertiesEditor.Provider()));
+
// security-related hooks
for (SecurityConfiguration sc : security.getConfigurations()) {
hooks.addAll(sc.getCommitHooks(workspaceName));
Added:
jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionablePropertiesEditor.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionablePropertiesEditor.java?rev=1739069&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionablePropertiesEditor.java
(added)
+++
jackrabbit/oak/trunk/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionablePropertiesEditor.java
Thu Apr 14 12:28:18 2016
@@ -0,0 +1,184 @@
+/*
+ * 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.upgrade.version;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.plugins.nodetype.TypePredicate;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.DefaultEditor;
+import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.collect.Sets.newHashSet;
+import static org.apache.jackrabbit.JcrConstants.JCR_BASEVERSION;
+import static org.apache.jackrabbit.JcrConstants.JCR_FROZENMIXINTYPES;
+import static org.apache.jackrabbit.JcrConstants.JCR_ISCHECKEDOUT;
+import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
+import static org.apache.jackrabbit.JcrConstants.JCR_PREDECESSORS;
+import static org.apache.jackrabbit.JcrConstants.JCR_ROOTVERSION;
+import static org.apache.jackrabbit.JcrConstants.JCR_SUCCESSORS;
+import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
+import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONHISTORY;
+import static org.apache.jackrabbit.JcrConstants.MIX_VERSIONABLE;
+import static org.apache.jackrabbit.JcrConstants.NT_FROZENNODE;
+import static org.apache.jackrabbit.JcrConstants.NT_VERSION;
+import static org.apache.jackrabbit.oak.api.Type.NAMES;
+import static org.apache.jackrabbit.oak.api.Type.REFERENCE;
+import static org.apache.jackrabbit.oak.api.Type.REFERENCES;
+import static
org.apache.jackrabbit.oak.plugins.memory.MultiGenericPropertyState.nameProperty;
+import static
org.apache.jackrabbit.oak.upgrade.version.VersionHistoryUtil.getVersionHistoryNodeState;
+
+/**
+ * The VersionablePropertiesEditor adds missing versionable properties.
+ */
+public final class VersionablePropertiesEditor extends DefaultEditor {
+
+ private static final String MIX_SIMPLE_VERSIONABLE =
"mix:simpleVersionable";
+
+ private static final Logger log =
LoggerFactory.getLogger(VersionablePropertiesEditor.class);
+
+ private final NodeBuilder rootBuilder;
+
+ private final NodeBuilder builder;
+
+ private final TypePredicate isVersionable;
+
+ private final TypePredicate isSimpleVersionable;
+
+ private final TypePredicate isNtVersion;
+
+ private final TypePredicate isFrozenNode;
+
+ private VersionablePropertiesEditor(NodeBuilder rootBuilder) {
+ this.builder = rootBuilder;
+ this.rootBuilder = rootBuilder;
+ this.isVersionable = new TypePredicate(rootBuilder.getNodeState(),
MIX_VERSIONABLE);
+ this.isSimpleVersionable = new
TypePredicate(rootBuilder.getNodeState(), MIX_SIMPLE_VERSIONABLE);
+ this.isNtVersion = new TypePredicate(rootBuilder.getNodeState(),
NT_VERSION);
+ this.isFrozenNode = new TypePredicate(rootBuilder.getNodeState(),
NT_FROZENNODE);
+ }
+
+ private VersionablePropertiesEditor(VersionablePropertiesEditor parent,
NodeBuilder builder) {
+ this.builder = builder;
+ this.rootBuilder = parent.rootBuilder;
+ this.isVersionable = parent.isVersionable;
+ this.isSimpleVersionable = parent.isSimpleVersionable;
+ this.isNtVersion = parent.isNtVersion;
+ this.isFrozenNode = parent.isFrozenNode;
+ }
+
+ public static class Provider implements EditorProvider {
+ @Override
+ public Editor getRootEditor(NodeState before, NodeState after,
NodeBuilder builder, CommitInfo info)
+ throws CommitFailedException {
+ return new VersionablePropertiesEditor(builder);
+ }
+
+ @Override
+ public String toString() {
+ return "VersionablePropertiesEditor";
+ }
+ }
+
+ @Override
+ public Editor childNodeAdded(String name, NodeState after) throws
CommitFailedException {
+ NodeBuilder nodeBuilder = builder.getChildNode(name);
+ if (isVersionable.apply(after)) {
+ fixProperties(nodeBuilder);
+ } else if (isFrozenNode.apply(after)) {
+ updateFrozenMixins(nodeBuilder);
+ }
+ return new VersionablePropertiesEditor(this, nodeBuilder);
+ }
+
+ @Override
+ public Editor childNodeChanged(String name, NodeState before, NodeState
after) throws CommitFailedException {
+ return childNodeAdded(name, after);
+ }
+
+ private static boolean updateFrozenMixins(NodeBuilder builder) {
+ if (builder.hasProperty(JCR_FROZENMIXINTYPES)) {
+ final Set<String> mixins =
newHashSet(builder.getProperty(JCR_FROZENMIXINTYPES).getValue(NAMES));
+ if (mixins.remove(MIX_SIMPLE_VERSIONABLE)) {
+ mixins.add(MIX_VERSIONABLE);
+ builder.setProperty(nameProperty(JCR_FROZENMIXINTYPES,
mixins));
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void fixProperties(NodeBuilder node) {
+ NodeState versionHistory =
getVersionHistoryNodeState(rootBuilder.getNodeState(),
node.getString(JCR_UUID));
+ if (!versionHistory.exists()) {
+ log.warn("No version history for {}", node);
+ return;
+ }
+
+ Set<String> updated = new HashSet<>();
+ if (!node.hasProperty(JCR_VERSIONHISTORY)) {
+ node.setProperty(JCR_VERSIONHISTORY,
versionHistory.getString(JCR_UUID), REFERENCE);
+ updated.add(JCR_VERSIONHISTORY);
+ }
+
+ String baseVersion = null;
+ if (!node.hasProperty(JCR_BASEVERSION)) {
+ baseVersion = getLastVersion(versionHistory);
+ node.setProperty(JCR_BASEVERSION, baseVersion, REFERENCE);
+ updated.add(JCR_BASEVERSION);
+ }
+
+ if (!node.hasProperty(JCR_PREDECESSORS)) {
+ baseVersion = baseVersion == null ? getLastVersion(versionHistory)
: baseVersion;
+
+ List<String> predecessors = new ArrayList<>();
+ if (node.getBoolean(JCR_ISCHECKEDOUT)) {
+ predecessors.add(baseVersion);
+ }
+ node.setProperty(JCR_PREDECESSORS, predecessors, REFERENCES);
+ updated.add(JCR_PREDECESSORS);
+ }
+
+ if (!updated.isEmpty()) {
+ log.info("Updated versionable properties {} for {}", updated,
node);
+ }
+ }
+
+ private String getLastVersion(NodeState versionHistory) {
+ NodeState lastVersion = versionHistory.getChildNode(JCR_ROOTVERSION);
+ for (ChildNodeEntry child : versionHistory.getChildNodeEntries()) {
+ NodeState v = child.getNodeState();
+ if (!isNtVersion.apply(v)) {
+ continue;
+ }
+ if (v.getProperty(JCR_SUCCESSORS).count() == 0) { // no successors
+ lastVersion = v;
+ }
+ }
+ return lastVersion.getString(JCR_UUID);
+ }
+}
Modified:
jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistoryTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistoryTest.java?rev=1739069&r1=1739068&r2=1739069&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistoryTest.java
(original)
+++
jackrabbit/oak/trunk/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistoryTest.java
Thu Apr 14 12:28:18 2016
@@ -17,6 +17,7 @@
package org.apache.jackrabbit.oak.upgrade;
import com.google.common.collect.Maps;
+import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.core.RepositoryContext;
import org.apache.jackrabbit.core.config.RepositoryConfig;
import org.apache.jackrabbit.oak.Oak;
@@ -38,7 +39,10 @@ import javax.jcr.PropertyType;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.version.Version;
import javax.jcr.version.VersionHistory;
+import javax.jcr.version.VersionManager;
import java.io.File;
import java.io.IOException;
@@ -48,10 +52,13 @@ import java.util.Map;
import com.google.common.collect.Lists;
+import static org.apache.jackrabbit.JcrConstants.JCR_PREDECESSORS;
+import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONHISTORY;
import static org.apache.jackrabbit.JcrConstants.MIX_VERSIONABLE;
import static
org.apache.jackrabbit.oak.plugins.version.VersionConstants.MIX_REP_VERSIONABLE_PATHS;
import static
org.apache.jackrabbit.oak.upgrade.util.VersionCopyTestUtils.getOrAddNodeWithMixins;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -151,9 +158,11 @@ public class CopyVersionHistoryTest exte
}
});
+ assertVersionableProperties(session, VERSIONABLES_OLD,
VERSIONABLES_YOUNG);
assertExistingHistories(session,
VERSIONABLES_OLD, VERSIONABLES_OLD_ORPHANED,
VERSIONABLES_YOUNG, VERSIONABLES_YOUNG_ORPHANED);
assertVersionablePaths(session, VERSIONABLES_OLD, VERSIONABLES_YOUNG);
+ assertVersionsCanBeRestored(session, VERSIONABLES_OLD,
VERSIONABLES_YOUNG);
}
@Test
@@ -165,9 +174,11 @@ public class CopyVersionHistoryTest exte
}
});
+ assertVersionableProperties(session, VERSIONABLES_YOUNG);
assertExistingHistories(session, VERSIONABLES_YOUNG,
VERSIONABLES_YOUNG_ORPHANED);
assertVersionablePaths(session, VERSIONABLES_YOUNG);
assertMissingHistories(session, VERSIONABLES_OLD,
VERSIONABLES_OLD_ORPHANED);
+ assertVersionsCanBeRestored(session, VERSIONABLES_YOUNG);
}
@Test
@@ -179,9 +190,11 @@ public class CopyVersionHistoryTest exte
}
});
+ assertVersionableProperties(session, VERSIONABLES_OLD,
VERSIONABLES_YOUNG);
assertExistingHistories(session, VERSIONABLES_OLD, VERSIONABLES_YOUNG,
VERSIONABLES_YOUNG_ORPHANED);
assertVersionablePaths(session, VERSIONABLES_OLD, VERSIONABLES_YOUNG);
assertMissingHistories(session, VERSIONABLES_OLD_ORPHANED);
+ assertVersionsCanBeRestored(session, VERSIONABLES_OLD,
VERSIONABLES_YOUNG);
}
@Test
@@ -192,9 +205,11 @@ public class CopyVersionHistoryTest exte
config.setCopyOrphanedVersions(null);
}
});
+ assertVersionableProperties(session, VERSIONABLES_OLD,
VERSIONABLES_YOUNG);
assertExistingHistories(session, VERSIONABLES_OLD, VERSIONABLES_YOUNG);
assertVersionablePaths(session, VERSIONABLES_OLD, VERSIONABLES_YOUNG);;
assertMissingHistories(session, VERSIONABLES_OLD_ORPHANED,
VERSIONABLES_YOUNG_ORPHANED);
+ assertVersionsCanBeRestored(session, VERSIONABLES_OLD,
VERSIONABLES_YOUNG);
}
@Test
@@ -206,9 +221,11 @@ public class CopyVersionHistoryTest exte
config.setCopyOrphanedVersions(null);
}
});
+ assertVersionableProperties(session, VERSIONABLES_YOUNG);
assertExistingHistories(session, VERSIONABLES_YOUNG);
assertVersionablePaths(session, VERSIONABLES_YOUNG);
assertMissingHistories(session, VERSIONABLES_OLD,
VERSIONABLES_OLD_ORPHANED, VERSIONABLES_YOUNG_ORPHANED);
+ assertVersionsCanBeRestored(session, VERSIONABLES_YOUNG);
}
@Test
@@ -283,6 +300,26 @@ public class CopyVersionHistoryTest exte
return null;
}
+ private static void assertVersionableProperties(final Session session,
final String... names) throws RepositoryException {
+ VersionManager vMgr = session.getWorkspace().getVersionManager();
+ for (final String mixin : MIXINS) {
+ final String pathPrefix = VERSIONABLES_PATH_PREFIX + mixin + "/";
+ for (final String name : names) {
+ final String path = pathPrefix + name;
+ Node versionable = session.getNode(path);
+
+ String versionHistoryUuid =
versionable.getProperty(JCR_VERSIONHISTORY).getString();
+ assertEquals(getVersionHistoryForPath(session,
path).getIdentifier(), versionHistoryUuid);
+
+ final Version baseVersion = vMgr.getBaseVersion(path);
+ assertEquals("1.2", baseVersion.getName());
+ final Value[] predecessors =
versionable.getProperty(JCR_PREDECESSORS).getValues();
+ assertEquals(1, predecessors.length);
+ assertEquals(baseVersion.getIdentifier(),
predecessors[0].getString());
+ }
+ }
+ }
+
private static void assertExistingHistories(final Session session, final
String... names)
throws RepositoryException {
for (final String mixin : MIXINS) {
@@ -331,4 +368,32 @@ public class CopyVersionHistoryTest exte
assertEquals(PropertyType.PATH, pathProperty.getType());
assertEquals(versionablePath, pathProperty.getString());
}
+
+ private static void assertVersionsCanBeRestored(final Session session,
final String... names) throws RepositoryException {
+ VersionManager vMgr = session.getWorkspace().getVersionManager();
+ for (final String mixin : MIXINS) {
+ final String pathPrefix = VERSIONABLES_PATH_PREFIX + mixin + "/";
+ for (final String name : names) {
+ final String path = pathPrefix + name;
+ VersionHistory history = vMgr.getVersionHistory(path);
+ assertEquals("1.2",
session.getNode(path).getProperty("version").getString());
+ vMgr.restore(history.getVersion("1.0"), false);
+
+ Node versionable = session.getNode(path);
+ assertEquals("1.0",
versionable.getProperty("version").getString());
+
+ // restored node should have correct properties
+ String versionHistoryUuid =
versionable.getProperty(JCR_VERSIONHISTORY).getString();
+ assertEquals(history.getIdentifier(), versionHistoryUuid);
+
+ final Version baseVersion = vMgr.getBaseVersion(path);
+ assertEquals("1.0", baseVersion.getName());
+ final Value[] predecessors =
versionable.getProperty(JCR_PREDECESSORS).getValues();
+ assertEquals(0, predecessors.length);
+ assertFalse(vMgr.isCheckedOut(path));
+ }
+ }
+ // after restoring, the paths should be still versionable
+ assertVersionablePaths(session, names);
+ }
}