diff --git a/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java b/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java index cb9f735..65004c5 100644 --- a/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java +++ b/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java @@ -100,7 +100,7 @@ public class VersionCopier { final NodeState versionHistory = getVersionHistoryNodeState(sourceRoot, versionableUuid); final Calendar lastModified = getVersionHistoryLastModified(versionHistory); - if (lastModified.after(minDate) || minDate.getTimeInMillis() == 0) { + if (versionHistory.exists() && (lastModified.after(minDate) || minDate.getTimeInMillis() == 0)) { NodeStateCopier.builder() .include(versionHistoryPath) .merge(VERSION_STORE_PATH) diff --git a/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java b/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java index 1473811..e61c727 100644 --- a/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java +++ b/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java @@ -26,6 +26,9 @@ import org.apache.jackrabbit.oak.spi.commit.Editor; import org.apache.jackrabbit.oak.spi.commit.EditorProvider; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.apache.jackrabbit.oak.spi.state.NodeState; +import org.apache.jackrabbit.oak.upgrade.RepositoryUpgrade; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Set; @@ -41,6 +44,7 @@ import static org.apache.jackrabbit.JcrConstants.MIX_REFERENCEABLE; import static org.apache.jackrabbit.JcrConstants.MIX_VERSIONABLE; import static org.apache.jackrabbit.oak.plugins.memory.MultiGenericPropertyState.nameProperty; import static org.apache.jackrabbit.oak.plugins.version.VersionConstants.MIX_REP_VERSIONABLE_PATHS; +import static org.apache.jackrabbit.oak.upgrade.version.VersionHistoryUtil.getVersionHistoryNodeState; /** * The VersionableEditor provides two possible ways to handle @@ -56,6 +60,8 @@ import static org.apache.jackrabbit.oak.plugins.version.VersionConstants.MIX_REP */ public class VersionableEditor extends DefaultEditor { + private static final Logger logger = LoggerFactory.getLogger(VersionableEditor.class); + private static final Set SKIPPED_PATHS = of("/oak:index", "/jcr:system/jcr:versionStorage"); private final Provider provider; @@ -113,22 +119,22 @@ public class VersionableEditor extends DefaultEditor { final VersionCopyConfiguration c = provider.config; if (isVersionable.apply(after)) { final String versionableUuid = getProperty(after, JCR_UUID, Type.STRING); - boolean versionHistoryExists = isVersionHistoryExists(versionableUuid); if (c.isCopyVersions() && c.skipOrphanedVersionsCopy()) { - versionHistoryExists = copyVersionHistory(after); + copyVersionHistory(after); } else if (c.isCopyVersions() && !c.skipOrphanedVersionsCopy()) { // all version histories have been copied, but maybe the date // range for orphaned entries is narrower if (c.getOrphanedMinDate().after(c.getVersionsMinDate())) { - versionHistoryExists = copyVersionHistory(after); + copyVersionHistory(after); } - } else { - versionHistoryExists = false; } - if (versionHistoryExists) { + if (isVersionHistoryExists(versionableUuid)) { setVersionablePath(versionableUuid); } else { + if (!getVersionHistoryNodeState(provider.sourceRoot, versionableUuid).exists()) { + logger.warn("No version history exists for {}", path); + } removeVersionProperties(getNodeBuilder(rootBuilder, this.path)); } } diff --git a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/BrokenVersionableTest.java b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/BrokenVersionableTest.java new file mode 100644 index 0000000..d78a06f --- /dev/null +++ b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/BrokenVersionableTest.java @@ -0,0 +1,109 @@ +/* + * 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; + +import static org.apache.jackrabbit.JcrConstants.MIX_VERSIONABLE; +import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED; + +import javax.jcr.Credentials; +import javax.jcr.Node; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; +import javax.jcr.version.VersionManager; + +import org.apache.commons.lang.StringUtils; +import org.apache.jackrabbit.api.JackrabbitSession; +import org.apache.jackrabbit.oak.Oak; +import org.apache.jackrabbit.oak.jcr.Jcr; +import org.apache.jackrabbit.oak.jcr.repository.RepositoryImpl; +import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore; +import org.apache.jackrabbit.oak.spi.commit.CommitInfo; +import org.apache.jackrabbit.oak.spi.commit.EmptyHook; +import org.apache.jackrabbit.oak.spi.state.NodeBuilder; +import org.apache.jackrabbit.oak.spi.state.NodeStore; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class BrokenVersionableTest { + + private static final Credentials CREDENTIALS = new SimpleCredentials("admin", "admin".toCharArray()); + + private NodeStore targetNodeStore; + + private Repository targetRepository; + + @Before + public synchronized void upgradeRepository() throws Exception { + targetNodeStore = new SegmentNodeStore(); + targetRepository = new Jcr(new Oak(targetNodeStore)).createRepository(); + NodeStore source = createSourceContent(); + RepositorySidegrade sidegrade = new RepositorySidegrade(source, targetNodeStore); + sidegrade.setCopyOrphanedVersions(null); + sidegrade.copy(); + } + + private NodeStore createSourceContent() throws Exception { + SegmentNodeStore source = new SegmentNodeStore(); + RepositoryImpl repository = (RepositoryImpl) new Jcr(new Oak(source)).createRepository(); + Session session = repository.login(CREDENTIALS); + String versionHistoryPath; + try { + Node root = session.getRootNode(); + + Node versionable = root.addNode("versionable", NT_UNSTRUCTURED); + versionable.addMixin(MIX_VERSIONABLE); + versionable.addNode("child", NT_UNSTRUCTURED); + session.save(); + + VersionManager vMgr = session.getWorkspace().getVersionManager(); + vMgr.checkin("/versionable"); + versionHistoryPath = vMgr.getVersionHistory("/versionable").getPath(); + } finally { + session.logout(); + } + repository.shutdown(); + + NodeBuilder rootBuilder = source.getRoot().builder(); + NodeBuilder builder = rootBuilder; + for (String segment : StringUtils.split(versionHistoryPath, '/')) { + if (segment.isEmpty()) { + continue; + } + builder = builder.child(segment); + } + builder.remove(); + source.merge(rootBuilder, EmptyHook.INSTANCE, CommitInfo.EMPTY); + return source; + } + + public JackrabbitSession createAdminSession() throws RepositoryException { + return (JackrabbitSession) targetRepository.login(CREDENTIALS); + } + + @Test + public void verifyNoVersionable() throws RepositoryException { + Session session = createAdminSession(); + try { + Assert.assertFalse(session.getNode("/versionable").isNodeType(MIX_VERSIONABLE)); + } finally { + session.logout(); + } + } +}