Added: jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/Reporter.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/Reporter.java?rev=1792993&view=auto ============================================================================== --- jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/Reporter.java (added) +++ jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/Reporter.java Fri Apr 28 07:16:13 2017 @@ -0,0 +1,45 @@ +/* + * 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.nodestate.report; + +import javax.annotation.Nonnull; + +/** + * A {@code Reporter} receives callbacks for every NodeState + * and PropertyState that was accessed via a {ReportingNodeState} + * instance. + */ +public interface Reporter { + + /** + * Callback reporting that the given {@code nodeState} was accessed. + * + * @param nodeState The accessed {@code ReportingNodeState} instance. + */ + void reportNode(@Nonnull final ReportingNodeState nodeState); + + /** + * Callback reporting that the property named {@code propertyName} + * was accessed on the {@code parent} node. + * + * @param parent The parent node state of the reported property. + * @param propertyName The name of the reported property. + */ + void reportProperty(@Nonnull final ReportingNodeState parent, @Nonnull final String propertyName); +}
Added: jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/ReportingNodeState.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/ReportingNodeState.java?rev=1792993&view=auto ============================================================================== --- jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/ReportingNodeState.java (added) +++ jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/nodestate/report/ReportingNodeState.java Fri Apr 28 07:16:13 2017 @@ -0,0 +1,118 @@ +/* + * 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.nodestate.report; + +import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.commons.PathUtils; +import org.apache.jackrabbit.oak.spi.state.NodeState; +import org.apache.jackrabbit.oak.upgrade.nodestate.AbstractDecoratedNodeState; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * A decoration layer for NodeState instances that intercepts + * all accesses to NodeStates and PropertyStates (getters) and + * informs a {@link Reporter} via its callbacks that the respective + * NodeStates or PropertyStates have been accessed. + * <br> + * The decoration is deep, i.e. any child NodeStates will be + * decorated as well and will report to the same {@code Reporter} + * instance. + * <br> + * For convenience, a {@link PeriodicReporter} abstract class exists. + * This simplifies reporting every nth node/property only. + * <br> + * Note: Multiple accesses to the same node or property are each + * reported. Therefore if exactly counting unique accesses is a + * requirement, the reporter needs to take care of de-duplication. + * + * @see Reporter + * @see PeriodicReporter + * @see LoggingReporter + */ +public class ReportingNodeState extends AbstractDecoratedNodeState { + + private final ReportingNodeState parent; + private final String name; + private final Reporter reporter; + + /** + * Allows wrapping a NodeState as a ReportingNodeState. The wrapped + * NodeState is treated as the root of a tree (i.e. path is "/"). + * <br> + * Any children accessed via this NodeState are also wrapped. Each + * wrapped NodeState is also reported to the provided Reporter. + * + * @param nodeState The NodeState to be wrapped. + * @param reporter The reporter to report to. + * @return the wrapped NodeState. + */ + public static NodeState wrap(NodeState nodeState, Reporter reporter) { + return wrapAndReport(null, "/", nodeState, reporter); + } + + private static NodeState wrapAndReport(@Nullable ReportingNodeState parent, @Nonnull String name, + @Nonnull NodeState delegate, @Nonnull Reporter reporter) { + final ReportingNodeState nodeState = new ReportingNodeState(parent, name, delegate, reporter); + reporter.reportNode(nodeState); + return nodeState; + } + + private ReportingNodeState(ReportingNodeState parent, String name, NodeState delegate, Reporter reporter) { + super(delegate); + this.parent = parent; + this.name = name; + this.reporter = reporter; + } + + /** + * ReportingNodeState instances provide access to their path via their + * parent hierarchy. Note that calculating the path on every access may + * incur a significant performance penalty. + * + * @return The path of the ReportingNodeState instance, assuming that + * the first wrapped instance is the root node. + */ + public String getPath() { + if (parent == null) { + return name; + } + return PathUtils.concat(this.parent.getPath(), name); + } + + @Nonnull + @Override + protected NodeState decorateChild(@Nonnull final String name, @Nonnull final NodeState delegateChild) { + return wrapAndReport(this, name, delegateChild, reporter); + } + + @Override + @CheckForNull + protected PropertyState decorateProperty(@Nonnull final PropertyState delegatePropertyState) { + reporter.reportProperty(this, delegatePropertyState.getName()); + return delegatePropertyState; + } + + @Override + public String toString() { + return "ReportingNodeState{" + getPath() + ", " + delegate.toString() + "}"; + } +} Added: jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/AuthorizableFolderEditor.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/AuthorizableFolderEditor.java?rev=1792993&view=auto ============================================================================== --- jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/AuthorizableFolderEditor.java (added) +++ jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/AuthorizableFolderEditor.java Fri Apr 28 07:16:13 2017 @@ -0,0 +1,113 @@ +/* + * 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.security; + +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.commons.PathUtils; +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.NodeBuilder; +import org.apache.jackrabbit.oak.spi.state.NodeState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE; +import static org.apache.jackrabbit.oak.spi.security.user.UserConstants.NT_REP_AUTHORIZABLE_FOLDER; + +/** + * There are occasions where in old JR2 repositories not all ancestors on + * the users path are of type {@code rep:AuthorizableFolder}, thus leading + * to exceptions after repository upgrade. + * <br> + * In order to avoid such situations, this hook verifies that all nodes on + * the users and groups paths are of type {@code rep:AuthorizableFolder} and + * fixes the node-type if it is incorrect. + */ +public class AuthorizableFolderEditor extends DefaultEditor { + + private static final Logger LOG = LoggerFactory.getLogger(AuthorizableFolderEditor.class); + + private final NodeBuilder currentBuilder; + + private final String groupsPath; + + private final String usersPath; + + private final String path; + + public AuthorizableFolderEditor(final NodeBuilder builder, final String path, final String groupsPath, final String usersPath) { + this.currentBuilder = builder; + this.groupsPath = groupsPath; + this.usersPath = usersPath; + this.path = path; + } + + public static EditorProvider provider(final String groupsPath, final String usersPath) { + return new EditorProvider() { + @Override + public Editor getRootEditor(final NodeState before, + final NodeState after, + final NodeBuilder builder, + final CommitInfo info) throws CommitFailedException { + return new AuthorizableFolderEditor(builder, "/", groupsPath, usersPath); + } + }; + } + + @Override + public void propertyAdded(final PropertyState after) throws CommitFailedException { + propertyChanged(null, after); + } + + @Override + public void propertyChanged(final PropertyState before, final PropertyState after) throws CommitFailedException { + if (JCR_PRIMARYTYPE.equals(after.getName())) { + String nodeType = after.getValue(Type.STRING); + LOG.debug("Found {}/jcr:primaryType = {}", path, nodeType); + if (!nodeType.equals(NT_REP_AUTHORIZABLE_FOLDER) && expectAuthorizableFolder(path)) { + LOG.info("Changed {}/jcr:primaryType from {} to {}", path, nodeType, NT_REP_AUTHORIZABLE_FOLDER); + currentBuilder.setProperty(JCR_PRIMARYTYPE, NT_REP_AUTHORIZABLE_FOLDER, Type.NAME); + } + } + } + + private boolean expectAuthorizableFolder(final String path) { + return !PathUtils.denotesRoot(path) + && (PathUtils.isAncestor(path, groupsPath) || path.equals(groupsPath) + || PathUtils.isAncestor(path, usersPath) || path.equals(usersPath)); + } + + @Override + public Editor childNodeAdded(final String name, final NodeState after) throws CommitFailedException { + return childNodeChanged(name, null, after); + } + + @Override + public Editor childNodeChanged(final String name, final NodeState before, final NodeState after) throws CommitFailedException { + final String childPath = PathUtils.concat(path, name); + if (expectAuthorizableFolder(childPath)) { + LOG.debug("Found {} - descending", childPath); + return new AuthorizableFolderEditor(currentBuilder.child(name), childPath, groupsPath, usersPath); + } else { + return null; + } + } +} Modified: jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/GroupEditor.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/GroupEditor.java?rev=1792993&r1=1792992&r2=1792993&view=diff ============================================================================== --- jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/GroupEditor.java (original) +++ jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/security/GroupEditor.java Fri Apr 28 07:16:13 2017 @@ -108,7 +108,7 @@ class GroupEditor extends DefaultEditor @Override public Editor childNodeChanged(String name, NodeState before, NodeState after) { - throw new IllegalStateException("changed node during upgrade copy not expected: " + state.path + "/" + name); + return null; } @Override Added: jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java?rev=1792993&view=auto ============================================================================== --- jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java (added) +++ jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopier.java Fri Apr 28 07:16:13 2017 @@ -0,0 +1,114 @@ +/* + * 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 static org.apache.jackrabbit.JcrConstants.JCR_CREATED; +import static org.apache.jackrabbit.JcrConstants.NT_VERSION; + +import java.util.Calendar; +import java.util.Iterator; + +import org.apache.jackrabbit.oak.api.Type; +import org.apache.jackrabbit.oak.plugins.nodetype.TypePredicate; +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.apache.jackrabbit.oak.upgrade.DescendantsIterator; +import org.apache.jackrabbit.oak.upgrade.nodestate.NodeStateCopier; +import org.apache.jackrabbit.util.ISO8601; + +import static org.apache.jackrabbit.oak.plugins.version.VersionConstants.VERSION_STORE_PATH; + +import static org.apache.jackrabbit.oak.upgrade.version.VersionHistoryUtil.getRelativeVersionHistoryPath; +import static org.apache.jackrabbit.oak.upgrade.version.VersionHistoryUtil.getVersionHistoryNodeState; + +/** + * This class allows to copy the version history, optionally filtering it with a + * given date. + */ +public class VersionCopier { + + private final TypePredicate isVersion; + + private final NodeState sourceVersionStorage; + + private final NodeBuilder targetVersionStorage; + + private final NodeBuilder targetRoot; + + public VersionCopier(NodeBuilder targetRoot, NodeState sourceVersionStorage, NodeBuilder targetVersionStorage) { + this.isVersion = new TypePredicate(targetRoot.getNodeState(), NT_VERSION); + this.sourceVersionStorage = sourceVersionStorage; + this.targetVersionStorage = targetVersionStorage; + this.targetRoot = targetRoot; + } + + public static void copyVersionStorage(NodeBuilder targetRoot, NodeState sourceVersionStorage, NodeBuilder targetVersionStorage, VersionCopyConfiguration config) { + final Iterator<NodeState> versionStorageIterator = new DescendantsIterator(sourceVersionStorage, 3); + final VersionCopier versionCopier = new VersionCopier(targetRoot, sourceVersionStorage, targetVersionStorage); + + while (versionStorageIterator.hasNext()) { + final NodeState versionHistoryBucket = versionStorageIterator.next(); + for (String versionHistory : versionHistoryBucket.getChildNodeNames()) { + versionCopier.copyVersionHistory(versionHistory, config.getOrphanedMinDate()); + } + } + } + + /** + * Copy history filtering versions using passed date and returns {@code + * true} if the history has been copied. + * + * @param versionableUuid + * Name of the version history node + * @param minDate + * Only versions older than this date will be copied + * @return {@code true} if at least one version has been copied + */ + public boolean copyVersionHistory(String versionableUuid, Calendar minDate) { + final String versionHistoryPath = getRelativeVersionHistoryPath(versionableUuid); + final NodeState sourceVersionHistory = getVersionHistoryNodeState(sourceVersionStorage, versionableUuid); + final Calendar lastModified = getVersionHistoryLastModified(sourceVersionHistory); + + if (sourceVersionHistory.exists() && (lastModified.after(minDate) || minDate.getTimeInMillis() == 0)) { + NodeStateCopier.builder() + .include(versionHistoryPath) + .merge(VERSION_STORE_PATH) + .copy(sourceVersionStorage, targetVersionStorage); + return true; + } + return false; + } + + private Calendar getVersionHistoryLastModified(final NodeState versionHistory) { + Calendar youngest = Calendar.getInstance(); + youngest.setTimeInMillis(0); + for (final ChildNodeEntry entry : versionHistory.getChildNodeEntries()) { + final NodeState version = entry.getNodeState(); + if (!isVersion.apply(version)) { + continue; + } + if (version.hasProperty(JCR_CREATED)) { + final Calendar created = ISO8601.parse(version.getProperty(JCR_CREATED).getValue(Type.DATE)); + if (created.after(youngest)) { + youngest = created; + } + } + } + return youngest; + } +} Added: jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopyConfiguration.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopyConfiguration.java?rev=1792993&view=auto ============================================================================== --- jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopyConfiguration.java (added) +++ jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionCopyConfiguration.java Fri Apr 28 07:16:13 2017 @@ -0,0 +1,71 @@ +/* + * 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 java.util.Calendar; + +/** + * This class allows to configure the behaviour of the version copier. + */ +public class VersionCopyConfiguration { + + private Calendar copyVersions; + + private Calendar copyOrphanedVersions; + + public VersionCopyConfiguration() { + final Calendar epoch = Calendar.getInstance(); + epoch.setTimeInMillis(0); + this.copyVersions = epoch; + this.copyOrphanedVersions = epoch; + } + + public void setCopyVersions(Calendar copyVersions) { + this.copyVersions = copyVersions; + } + + public void setCopyOrphanedVersions(Calendar copyOrphanedVersions) { + this.copyOrphanedVersions = copyOrphanedVersions; + } + + public Calendar getVersionsMinDate() { + return copyVersions; + } + + public Calendar getOrphanedMinDate() { + if (copyVersions == null) { + return copyVersions; + } else if (copyOrphanedVersions != null && copyVersions.after(copyOrphanedVersions)) { + return copyVersions; + } else { + return copyOrphanedVersions; + } + } + + public boolean isCopyVersions() { + return copyVersions != null; + } + + public boolean skipOrphanedVersionsCopy() { + return copyVersions == null || copyOrphanedVersions == null; + } + + public boolean isCopyAll() { + return copyVersions != null && copyVersions.getTimeInMillis() == 0 && copyOrphanedVersions != null && copyOrphanedVersions.getTimeInMillis() == 0; + } + +} \ No newline at end of file Added: jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionHistoryUtil.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionHistoryUtil.java?rev=1792993&view=auto ============================================================================== --- jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionHistoryUtil.java (added) +++ jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionHistoryUtil.java Fri Apr 28 07:16:13 2017 @@ -0,0 +1,92 @@ +/* + * 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 static com.google.common.collect.Iterables.concat; +import static java.util.Collections.singleton; +import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE; +import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM; +import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONSTORAGE; +import static org.apache.jackrabbit.oak.plugins.version.VersionConstants.REP_VERSIONSTORAGE; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.jackrabbit.oak.api.Type; +import org.apache.jackrabbit.oak.spi.state.NodeBuilder; +import org.apache.jackrabbit.oak.spi.state.NodeState; + +import com.google.common.base.Joiner; + +public class VersionHistoryUtil { + + public static String getRelativeVersionHistoryPath(String versionableUuid) { + return Joiner.on('/').join(concat( + singleton(""), + getRelativeVersionHistoryPathSegments(versionableUuid), + singleton(versionableUuid))); + } + + /** + * Constructs the version history path based on the versionable's UUID. + * + * @param root The root NodeState below which to look for the version. + * @param versionableUuid The String representation of the versionable's UUID. + * @return The NodeState corresponding to the version history, or {@code null} + * if it does not exist. + */ + static NodeState getVersionHistoryNodeState(NodeState versionStorage, String versionableUuid) { + NodeState historyParent = versionStorage; + for (String segment : getRelativeVersionHistoryPathSegments(versionableUuid)) { + historyParent = historyParent.getChildNode(segment); + } + return historyParent.getChildNode(versionableUuid); + } + + static NodeBuilder getVersionHistoryBuilder(NodeBuilder versionStorage, String versionableUuid) { + NodeBuilder history = versionStorage; + for (String segment : getRelativeVersionHistoryPathSegments(versionableUuid)) { + history = history.getChildNode(segment); + } + return history.getChildNode(versionableUuid); + } + + private static List<String> getRelativeVersionHistoryPathSegments(String versionableUuid) { + final List<String> segments = new ArrayList<String>(); + for (int i = 0; i < 3; i++) { + segments.add(versionableUuid.substring(i * 2, i * 2 + 2)); + } + return segments; + } + + public static NodeState getVersionStorage(NodeState root) { + return root.getChildNode(JCR_SYSTEM).getChildNode(JCR_VERSIONSTORAGE); + } + + public static NodeBuilder getVersionStorage(NodeBuilder root) { + return root.getChildNode(JCR_SYSTEM).getChildNode(JCR_VERSIONSTORAGE); + } + + public static NodeBuilder createVersionStorage(NodeBuilder root) { + NodeBuilder vs = root.child(JCR_SYSTEM).child(JCR_VERSIONSTORAGE); + if (!vs.hasProperty(JCR_PRIMARYTYPE)) { + vs.setProperty(JCR_PRIMARYTYPE, REP_VERSIONSTORAGE, Type.NAME); + } + return vs; + } + +} Added: jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java?rev=1792993&view=auto ============================================================================== --- jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java (added) +++ jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionableEditor.java Fri Apr 28 07:16:13 2017 @@ -0,0 +1,250 @@ +/* + * 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.api.Type; +import org.apache.jackrabbit.oak.commons.PathUtils; +import org.apache.jackrabbit.oak.plugins.nodetype.TypePredicate; +import org.apache.jackrabbit.oak.plugins.version.ReadWriteVersionManager; +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.NodeBuilder; +import org.apache.jackrabbit.oak.spi.state.NodeState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.Set; + +import static com.google.common.collect.ImmutableSet.of; +import static com.google.common.collect.Sets.newHashSet; +import static org.apache.jackrabbit.JcrConstants.JCR_BASEVERSION; +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_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.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.getVersionHistoryBuilder; +import static org.apache.jackrabbit.oak.upgrade.version.VersionHistoryUtil.getVersionStorage; + +/** + * The VersionableEditor provides two possible ways to handle + * versionable nodes: + * <ul> + * <li>it can copy the version histories of versionable nodes, or</li> + * <li> + * it can skip copying version histories and remove the + * {@code mix:versionable} mixin together with any related + * properties (see {@link #removeVersionProperties(NodeBuilder)}). + * </li> + * </ul> + */ +public class VersionableEditor extends DefaultEditor { + + private static final Logger logger = LoggerFactory.getLogger(VersionableEditor.class); + + private static final Set<String> SKIPPED_PATHS = of("/oak:index", "/jcr:system/jcr:versionStorage"); + + private final Provider provider; + + private final NodeBuilder rootBuilder; + + private final NodeBuilder versionStorage; + + private final TypePredicate isReferenceable; + + private final TypePredicate isVersionable; + + private final VersionCopier versionCopier; + + private final ReadWriteVersionManager vMgr; + + private String path; + + private VersionableEditor(Provider provider, NodeBuilder rootBuilder) { + this.rootBuilder = rootBuilder; + this.versionStorage = getVersionStorage(rootBuilder); + this.vMgr = new ReadWriteVersionManager(versionStorage, rootBuilder); + + this.provider = provider; + this.isVersionable = new TypePredicate(rootBuilder.getNodeState(), MIX_VERSIONABLE); + this.isReferenceable = new TypePredicate(rootBuilder.getNodeState(), MIX_REFERENCEABLE); + this.versionCopier = new VersionCopier(rootBuilder, getVersionStorage(provider.sourceRoot), versionStorage); + this.path = "/"; + + } + + public static class Provider implements EditorProvider { + + private final NodeState sourceRoot; + + private final String workspaceName; + + private final VersionCopyConfiguration config; + + public Provider(NodeState sourceRoot, String workspaceName, VersionCopyConfiguration config) { + this.sourceRoot = sourceRoot; + this.workspaceName = workspaceName; + this.config = config; + } + + @Override + public Editor getRootEditor(NodeState before, NodeState after, NodeBuilder rootBuilder, CommitInfo info) throws CommitFailedException { + return new VersionableEditor(this, rootBuilder); + } + } + + @Override + public Editor childNodeAdded(String name, NodeState after) throws CommitFailedException { + final String path = PathUtils.concat(this.path, name); + // skip deleted nodes and well known paths that may not contain versionable nodes + if (after == null || SKIPPED_PATHS.contains(path)) { + return null; + } + + // assign path field only after checking that we don't skip this subtree + this.path = path; + + final VersionCopyConfiguration c = provider.config; + if (isVersionable.apply(after)) { + final String versionableUuid = getProperty(after, JCR_UUID, Type.STRING); + if (c.isCopyVersions() && c.skipOrphanedVersionsCopy()) { + 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())) { + copyVersionHistory(after); + } + } + + if (isVersionHistoryExists(versionableUuid)) { + setVersionablePath(versionableUuid); + } else { + NodeBuilder versionableBuilder = getNodeBuilder(rootBuilder, this.path); + removeVersionProperties(versionableBuilder); + if (isVersionable.apply(versionableBuilder.getNodeState())) { + logger.warn("Node {} is still versionable. Creating empty version history.", path); + createEmptyHistory(versionableBuilder); + } + } + } + + return this; + } + + private boolean copyVersionHistory(NodeState versionable) { + assert versionable.exists(); + + final String versionableUuid = versionable.getProperty(JCR_UUID).getValue(Type.STRING); + return versionCopier.copyVersionHistory(versionableUuid, provider.config.getVersionsMinDate()); + } + + private void setVersionablePath(String versionableUuid) { + final NodeBuilder versionHistory = VersionHistoryUtil.getVersionHistoryBuilder(versionStorage, versionableUuid); + if (!versionHistory.hasProperty(provider.workspaceName)) { + versionHistory.setProperty(provider.workspaceName, path, Type.PATH); + } + addMixin(versionHistory, MIX_REP_VERSIONABLE_PATHS); + } + + private boolean isVersionHistoryExists(String versionableUuid) { + return getVersionHistoryBuilder(versionStorage, versionableUuid).exists(); + } + + private void removeVersionProperties(final NodeBuilder versionableBuilder) { + assert versionableBuilder.exists(); + + removeMixin(versionableBuilder, MIX_VERSIONABLE); + + // we don't know if the UUID is otherwise referenced, + // so make sure the node remains referencable + if (!isReferenceable.apply(versionableBuilder.getNodeState())) { + addMixin(versionableBuilder, MIX_REFERENCEABLE); + } + + versionableBuilder.removeProperty(JCR_VERSIONHISTORY); + versionableBuilder.removeProperty(JCR_PREDECESSORS); + versionableBuilder.removeProperty(JCR_BASEVERSION); + versionableBuilder.removeProperty(JCR_ISCHECKEDOUT); + } + + private void createEmptyHistory(NodeBuilder versionable) throws CommitFailedException { + vMgr.getOrCreateVersionHistory(versionable, Collections.<String,Object>emptyMap()); + } + + @Override + public Editor childNodeChanged(String name, NodeState before, NodeState after) throws CommitFailedException { + return childNodeAdded(name, after); + } + + @Override + public Editor childNodeDeleted(String name, NodeState before) throws CommitFailedException { + return childNodeAdded(name, null); + } + + @Override + public void leave(NodeState before, NodeState after) throws CommitFailedException { + this.path = PathUtils.getParentPath(this.path); + } + + private static <T> T getProperty(NodeState state, String name, Type<T> type) { + if (state.hasProperty(name)) { + return state.getProperty(name).getValue(type); + } + return null; + } + + private static NodeBuilder getNodeBuilder(NodeBuilder root, String path) { + NodeBuilder builder = root; + for (String name : PathUtils.elements(path)) { + builder = builder.getChildNode(name); + } + return builder; + } + + private static void addMixin(NodeBuilder builder, String name) { + if (builder.hasProperty(JCR_MIXINTYPES)) { + final Set<String> mixins = newHashSet(builder.getProperty(JCR_MIXINTYPES).getValue(Type.NAMES)); + if (mixins.add(name)) { + builder.setProperty(nameProperty(JCR_MIXINTYPES, mixins)); + } + } else { + builder.setProperty(nameProperty(JCR_MIXINTYPES, of(name))); + } + } + + private static void removeMixin(NodeBuilder builder, String name) { + if (builder.hasProperty(JCR_MIXINTYPES)) { + final Set<String> mixins = newHashSet(builder.getProperty(JCR_MIXINTYPES).getValue(Type.NAMES)); + if (mixins.remove(name)) { + if (mixins.isEmpty()) { + builder.removeProperty(JCR_MIXINTYPES); + } else { + builder.setProperty(nameProperty(JCR_MIXINTYPES, mixins)); + } + } + } + } +} Added: jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionablePropertiesEditor.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionablePropertiesEditor.java?rev=1792993&view=auto ============================================================================== --- jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionablePropertiesEditor.java (added) +++ jackrabbit/oak/branches/1.0/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/version/VersionablePropertiesEditor.java Fri Apr 28 07:16:13 2017 @@ -0,0 +1,188 @@ +/* + * 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_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; +import static org.apache.jackrabbit.oak.upgrade.version.VersionHistoryUtil.getVersionStorage; + +/** + * 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 versionStorage; + + 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.versionStorage = getVersionStorage(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.versionStorage = parent.versionStorage; + 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(versionStorage.getNodeState(), node.getString(JCR_UUID)); + if (!versionHistory.exists()) { + log.warn("No version history for {}", node); + return; + } + + Set<String> updated = new HashSet<String>(); + 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<String>(); + 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); + } +} Added: jackrabbit/oak/branches/1.0/oak-upgrade/src/main/resources/logback.xml URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/resources/logback.xml?rev=1792993&view=auto ============================================================================== --- jackrabbit/oak/branches/1.0/oak-upgrade/src/main/resources/logback.xml (added) +++ jackrabbit/oak/branches/1.0/oak-upgrade/src/main/resources/logback.xml Fri Apr 28 07:16:13 2017 @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + ~ 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. + --> +<configuration> + + <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> + <!-- encoders are assigned the type + ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --> + <encoder> + <pattern>%d{dd.MM.yyyy HH:mm:ss.SSS} [%thread] *%level* %logger - %message%n</pattern> + </encoder> + </appender> + + <root level="info"> + <appender-ref ref="CONSOLE" /> + </root> +</configuration> \ No newline at end of file Added: jackrabbit/oak/branches/1.0/oak-upgrade/src/main/resources/upgrade_usage.txt URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/main/resources/upgrade_usage.txt?rev=1792993&view=auto ============================================================================== --- jackrabbit/oak/branches/1.0/oak-upgrade/src/main/resources/upgrade_usage.txt (added) +++ jackrabbit/oak/branches/1.0/oak-upgrade/src/main/resources/upgrade_usage.txt Fri Apr 28 07:16:13 2017 @@ -0,0 +1,99 @@ +# 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. + +Usage: ${command} [options] jcr2_source [destination] + (to upgrade a JCR 2 repository) + + ${command} [options] source destination + (to migrate an Oak repository) + +# jcr2_source + +It's a path to the repository directory and optionally the repository.xml file, +for instance: + + crx-quickstart/repository + my-instance/repository somewhere-else/repository.xml + +If there is no destination, repository will be converted to a segment node store +in-place. Old files will be moved to the repository/crx2 directory. + +# source / destination + +An descriptor of the Oak node store. Possible options: + + * path to the segment-tar store + * segment-old:/path/to/classic/segment + * jdbc:... (requires passing username and password as separate parameters) + * mongodb://host:port/database + +# node store options + +--cache <int> Cache size in MB (default: 256) +--disable-mmap Disable memory mapped file access for Segment Store + +--src-password Source rdb password +--src-user Source rdb user +--user Target rdb user +--password Target rdb password + +# migration options + +--copy-binaries Copy binary content. Use this to disable use of + existing DataStore in new repo +--early-shutdown Shutdown the source JCR2 repository after nodes are + copied and before the commit hooks are applied +--fail-on-error Fail completely if nodes can't be read from the + source repo +--ignore-missing-binaries Proceed with the migration even if the binaries + are missing + +# version store options + +--copy-orphaned-versions Allows to skip copying orphaned versions. Parameters: + { true | false | yyyy-mm-dd }. Defaults to true. +--copy-versions Copy the version storage. Parameters: + { true | false | yyyy-mm-dd }. Defaults to true. + +# paths options + +--include-paths Comma-separated list of paths to include during copy. +--merge-paths Comma-separated list of paths to merge during copy. +--exclude-paths Comma-separated list of paths to exclude during copy. + +# source blob store options + +--src-datastore Datastore directory to be used as a source + FileDataStore +--src-fileblobstore Datastore directory to be used as a source + FileBlobStore +--src-s3datastore Datastore directory to be used for the source S3 +--src-s3config Configuration file for the source S3DataStore + +# destination blob store options + +--datastore Datastore directory to be used as a target + FileDataStore +--fileblobstore Datastore directory to be used as a target + FileBlobStore +--s3datastore Datastore directory to be used for the target S3 +--s3config Configuration file for the target S3DataStore + +# other options + +-?, -h, --help Show help +--skip-init Don't initialize the destination repository +--skip-name-check Don't look for long-named nodes at the beginning of + the migration \ No newline at end of file Modified: jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java?rev=1792993&r1=1792992&r2=1792993&view=diff ============================================================================== --- jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java (original) +++ jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AbstractRepositoryUpgradeTest.java Fri Apr 28 07:16:13 2017 @@ -18,7 +18,11 @@ */ package org.apache.jackrabbit.oak.upgrade; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -30,75 +34,151 @@ import javax.jcr.SimpleCredentials; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +import org.apache.jackrabbit.api.JackrabbitRepository; import org.apache.jackrabbit.api.JackrabbitSession; import org.apache.jackrabbit.core.RepositoryImpl; import org.apache.jackrabbit.core.config.RepositoryConfig; -import org.apache.jackrabbit.mk.core.MicroKernelImpl; import org.apache.jackrabbit.oak.Oak; import org.apache.jackrabbit.oak.jcr.Jcr; -import org.apache.jackrabbit.oak.kernel.KernelNodeStore; +import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore; import org.apache.jackrabbit.oak.spi.state.NodeStore; import org.apache.jackrabbit.oak.stats.Clock; +import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; public abstract class AbstractRepositoryUpgradeTest { - protected static final Credentials CREDENTIALS = new SimpleCredentials("admin", "admin".toCharArray()); + public static final Credentials CREDENTIALS = new SimpleCredentials("admin", "admin".toCharArray()); + + protected static NodeStore targetNodeStore; + + private static File testDirectory; - private static Repository targetRepository; + private JackrabbitRepository targetRepository; @BeforeClass - public static void init() { + public static void init() throws InterruptedException { // ensure that we create a new repository for the next test - targetRepository = null; + targetNodeStore = null; + testDirectory = createTestDirectory(); + } + + protected static File createTestDirectory() throws InterruptedException { + final File dir = new File("target", "upgrade-" + Clock.SIMPLE.getTimeIncreasing()); + FileUtils.deleteQuietly(dir); + return dir; + } + + protected NodeStore createTargetNodeStore() { + return new SegmentNodeStore(); } @Before public synchronized void upgradeRepository() throws Exception { - if (targetRepository == null) { - File directory = new File( - "target", "upgrade-" + Clock.SIMPLE.getTimeIncreasing()); - FileUtils.deleteQuietly(directory); - + if (targetNodeStore == null) { + File directory = getTestDirectory(); File source = new File(directory, "source"); source.mkdirs(); - - InputStream repoConfig = getRepositoryConfig(); - RepositoryConfig config; - if (repoConfig == null) { - config = RepositoryConfig.install(source); - } else { - OutputStream out = FileUtils.openOutputStream(new File(source, "repository.xml")); - IOUtils.copy(repoConfig, out); - out.close(); - repoConfig.close(); - config = RepositoryConfig.create(source); - } - RepositoryImpl repository = RepositoryImpl.create(config); + RepositoryImpl repository = createSourceRepository(source); + Session session = repository.login(CREDENTIALS); try { - createSourceContent(repository); + createSourceContent(session); } finally { + session.save(); + session.logout(); repository.shutdown(); } - NodeStore target = new KernelNodeStore(new MicroKernelImpl()); - RepositoryUpgrade.copy(source, target); - targetRepository = new Jcr(new Oak(target)).createRepository(); + final NodeStore target = getTargetNodeStore(); + doUpgradeRepository(source, target); + targetNodeStore = target; + } + } + + @After + public synchronized void shutdownTargetRepository() { + if (targetRepository != null) { + targetRepository.shutdown(); + targetRepository = null; + } + } + + protected synchronized NodeStore getTargetNodeStore() { + if (targetNodeStore == null) { + targetNodeStore = createTargetNodeStore(); + } + return targetNodeStore; + } + + protected static File getTestDirectory() { + return testDirectory; + } + + protected RepositoryImpl createSourceRepository(File repositoryHome) throws IOException, RepositoryException { + InputStream repoConfig = getRepositoryConfig(); + RepositoryConfig config; + if (repoConfig == null) { + config = RepositoryConfig.install(repositoryHome); + } else { + OutputStream out = FileUtils.openOutputStream(new File(repositoryHome, "repository.xml")); + IOUtils.copy(repoConfig, out); + out.close(); + repoConfig.close(); + config = RepositoryConfig.create(repositoryHome); } + return RepositoryImpl.create(config); + } + + + protected void doUpgradeRepository(File source, NodeStore target)throws RepositoryException, IOException{ + RepositoryUpgrade.copy(source, target); } - public InputStream getRepositoryConfig() { + public InputStream getRepositoryConfig(){ return null; } public Repository getTargetRepository() { + if (targetRepository == null) { + targetRepository = (JackrabbitRepository) new Jcr(new Oak( + targetNodeStore)).createRepository(); + } return targetRepository; } - public JackrabbitSession createAdminSession() throws RepositoryException { - return (JackrabbitSession) getTargetRepository().login(CREDENTIALS); + public JackrabbitSession createAdminSession()throws RepositoryException{ + return(JackrabbitSession)getTargetRepository().login(CREDENTIALS); } - protected abstract void createSourceContent(Repository repository) throws Exception; + protected abstract void createSourceContent(Session session) throws Exception; + protected void assertExisting(final String... paths) throws RepositoryException { + final Session session = createAdminSession(); + try { + assertExisting(session, paths); + } finally { + session.logout(); + } + } + + protected void assertExisting(final Session session, final String... paths) throws RepositoryException { + for (final String path : paths) { + assertTrue("node " + path + " should exist", session.nodeExists(path)); + } + } + + protected void assertMissing(final String... paths) throws RepositoryException { + final Session session = createAdminSession(); + try { + assertMissing(session, paths); + } finally { + session.logout(); + } + } + + protected void assertMissing(final Session session, final String... paths) throws RepositoryException { + for (final String path : paths) { + assertFalse("node " + path + " should not exist", session.nodeExists(path)); + } + } } Added: jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AuthorizableFolderEditorTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AuthorizableFolderEditorTest.java?rev=1792993&view=auto ============================================================================== --- jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AuthorizableFolderEditorTest.java (added) +++ jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/AuthorizableFolderEditorTest.java Fri Apr 28 07:16:13 2017 @@ -0,0 +1,127 @@ +/* + * 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 org.apache.jackrabbit.JcrConstants; +import org.apache.jackrabbit.api.JackrabbitSession; +import org.apache.jackrabbit.api.security.user.Group; +import org.apache.jackrabbit.api.security.user.User; +import org.apache.jackrabbit.api.security.user.UserManager; +import org.apache.jackrabbit.oak.spi.security.user.UserConstants; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; +import org.junit.Test; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import java.io.InputStream; + +import static org.junit.Assert.assertThat; + +public class AuthorizableFolderEditorTest extends AbstractRepositoryUpgradeTest { + + // this repository config sets the groupsPath and usersPath to match + // this tests expectations + private static final String REPOSITORY_XML_FILE = "repository-groupmember.xml"; + + private static final String TEST_GROUP = "AuthorizableFolderEditorTest-Group"; + + private static final String TEST_USER = "AuthorizableFolderEditorTest-User"; + + private static final String HOME_PATH = "/home"; + + private static final String GROUPS_PATH = HOME_PATH + "/groups"; + + private static final String USERS_PATH = HOME_PATH + "/users"; + + private static final String CONTROL_PATH = HOME_PATH + "/control"; + + @Override + protected void createSourceContent(final Session session) throws Exception { + UserManager userMgr = ((JackrabbitSession) session).getUserManager(); + userMgr.autoSave(false); + Group group = userMgr.createGroup(TEST_GROUP); + User user = userMgr.createUser(TEST_USER, "secret"); + group.addMember(user); + session.save(); + + // simulate the error, set node types to incorrect values + Node home = session.getNode("/home"); + home.setPrimaryType(JcrConstants.NT_UNSTRUCTURED); + home.getNode("users").setPrimaryType(JcrConstants.NT_UNSTRUCTURED); + home.getNode("groups").setPrimaryType(JcrConstants.NT_UNSTRUCTURED); + home.addNode("control", JcrConstants.NT_UNSTRUCTURED); + session.save(); + } + + @Override + public InputStream getRepositoryConfig() { + return getClass().getClassLoader().getResourceAsStream(REPOSITORY_XML_FILE); + } + + @Test + public void verifyCorrectedNodeTypes() throws RepositoryException { + final Session session = createAdminSession(); + assertExisting(session, HOME_PATH, USERS_PATH, GROUPS_PATH, CONTROL_PATH); + + assertThat(session.getNode(HOME_PATH), hasNodeType(UserConstants.NT_REP_AUTHORIZABLE_FOLDER)); + assertThat(session.getNode(USERS_PATH), hasNodeType(UserConstants.NT_REP_AUTHORIZABLE_FOLDER)); + assertThat(session.getNode(GROUPS_PATH), hasNodeType(UserConstants.NT_REP_AUTHORIZABLE_FOLDER)); + assertThat(session.getNode(CONTROL_PATH), hasNodeType(JcrConstants.NT_UNSTRUCTURED)); + } + + private static Matcher<? super Node> hasNodeType(final String expectedNodeType) { + return new TypeSafeMatcher<Node>() { + + private String path; + + @Override + protected boolean matchesSafely(final Node node) { + path = getPath(node); + return getNodeTypeName(node).equals(expectedNodeType); + } + + @Override + public void describeTo(final Description description) { + description.appendText("the node " + path + " to be of type ").appendValue(expectedNodeType); + } + + @Override + protected void describeMismatchSafely(final Node node, final Description mismatchDescription) { + mismatchDescription.appendText(" was ").appendValue(getNodeTypeName(node)); + } + + private String getNodeTypeName(final Node node) { + try { + return node.getPrimaryNodeType().getName(); + } catch (RepositoryException e) { + throw new RuntimeException(e); + } + } + + private String getPath(final Node node) { + try { + return node.getPath(); + } catch (RepositoryException e) { + throw new RuntimeException(e); + } + } + }; + } +} Added: jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/BrokenVersionableTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/BrokenVersionableTest.java?rev=1792993&view=auto ============================================================================== --- jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/BrokenVersionableTest.java (added) +++ jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/BrokenVersionableTest.java Fri Apr 28 07:16:13 2017 @@ -0,0 +1,138 @@ +/* + * 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Credentials; +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; +import javax.jcr.version.VersionHistory; +import javax.jcr.version.VersionIterator; +import javax.jcr.version.VersionManager; + +import org.apache.jackrabbit.api.JackrabbitSession; +import org.apache.jackrabbit.commons.cnd.CndImporter; +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.apache.jackrabbit.oak.upgrade.util.NodeStateTestUtils; +import org.junit.After; +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 RepositoryImpl targetRepository; + + @Before + public synchronized void upgradeRepository() throws Exception { + targetNodeStore = new SegmentNodeStore(); + targetRepository = (RepositoryImpl) new Jcr(new Oak(targetNodeStore)).createRepository(); + NodeStore source = createSourceContent(); + RepositorySidegrade sidegrade = new RepositorySidegrade(source, targetNodeStore); + sidegrade.setCopyOrphanedVersions(null); + sidegrade.copy(); + } + + @After + public synchronized void shutdownRepository() { + targetRepository.shutdown(); + targetRepository = null; + targetNodeStore = null; + } + + private NodeStore createSourceContent() throws Exception { + SegmentNodeStore source = new SegmentNodeStore(); + RepositoryImpl repository = (RepositoryImpl) new Jcr(new Oak(source)).createRepository(); + Session session = repository.login(CREDENTIALS); + List<String> versionHistoryPaths = new ArrayList<String>(); + try { + CndImporter.registerNodeTypes(new StringReader("<test = 'http://jackrabbit.apache.org/ns/test'>\n" + + "[test:Versionable] > nt:unstructured, mix:versionable"), session); + + Node root = session.getRootNode(); + + Node versionable1 = root.addNode("versionable1", NT_UNSTRUCTURED); + versionable1.addMixin(MIX_VERSIONABLE); + versionable1.addNode("child", NT_UNSTRUCTURED); + + Node versionable2 = root.addNode("versionable2", "test:Versionable"); + versionable2.addNode("child", NT_UNSTRUCTURED); + + session.save(); + + VersionManager vMgr = session.getWorkspace().getVersionManager(); + vMgr.checkin("/versionable1"); + vMgr.checkin("/versionable2"); + versionHistoryPaths.add(vMgr.getVersionHistory("/versionable1").getPath()); + versionHistoryPaths.add(vMgr.getVersionHistory("/versionable2").getPath()); + } finally { + session.logout(); + repository.shutdown(); + } + + // remove version history to corrupt the JCR repository structure + NodeBuilder rootBuilder = source.getRoot().builder(); + for (String versionHistoryPath : versionHistoryPaths) { + NodeStateTestUtils.createOrGetBuilder(rootBuilder, versionHistoryPath).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(); + VersionManager vMgr = session.getWorkspace().getVersionManager(); + try { + assertFalse(session.getNode("/versionable1").isNodeType(MIX_VERSIONABLE)); + + Node versionable2 = session.getNode("/versionable2"); + assertTrue(versionable2.isNodeType(MIX_VERSIONABLE)); + VersionHistory history = vMgr.getVersionHistory(versionable2.getPath()); + VersionIterator versions = history.getAllVersions(); + assertEquals("jcr:rootVersion", versions.nextVersion().getName()); + assertFalse(versions.hasNext()); + } finally { + session.logout(); + } + } +} Added: jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyCheckpointsTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyCheckpointsTest.java?rev=1792993&view=auto ============================================================================== --- jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyCheckpointsTest.java (added) +++ jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyCheckpointsTest.java Fri Apr 28 07:16:13 2017 @@ -0,0 +1,166 @@ +/* + * 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 com.google.common.base.Joiner; +import org.apache.jackrabbit.oak.spi.state.NodeStore; +import org.apache.jackrabbit.oak.upgrade.cli.AbstractOak2OakTest; +import org.apache.jackrabbit.oak.upgrade.cli.OakUpgrade; +import org.apache.jackrabbit.oak.upgrade.cli.container.BlobStoreContainer; +import org.apache.jackrabbit.oak.upgrade.cli.container.FileDataStoreContainer; +import org.apache.jackrabbit.oak.upgrade.cli.container.NodeStoreContainer; +import org.apache.jackrabbit.oak.upgrade.cli.container.SegmentNodeStoreContainer; +import org.apache.jackrabbit.oak.upgrade.cli.parser.CliArgumentException; +import org.apache.jackrabbit.oak.upgrade.cli.parser.DatastoreArguments; +import org.apache.jackrabbit.oak.upgrade.cli.parser.MigrationCliArguments; +import org.apache.jackrabbit.oak.upgrade.cli.parser.MigrationOptions; +import org.apache.jackrabbit.oak.upgrade.cli.parser.OptionParserFactory; +import org.apache.jackrabbit.oak.upgrade.cli.parser.StoreArguments; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jcr.RepositoryException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static java.util.Arrays.asList; +import static org.junit.Assert.fail; + +@RunWith(Parameterized.class) +public class CopyCheckpointsTest extends AbstractOak2OakTest { + + private enum Result { + EXCEPTION, CHECKPOINTS_MISSING, CHECKPOINTS_COPIED + } + + private static final Logger log = LoggerFactory.getLogger(CopyCheckpointsTest.class); + + @Parameterized.Parameters(name = "{0}") + public static Collection<Object[]> data() throws IOException { + List<Object[]> params = new ArrayList<Object[]>(); + + BlobStoreContainer blob = new FileDataStoreContainer(); + params.add(new Object[]{ + "Fails on missing blobstore", + new SegmentNodeStoreContainer(blob), + new SegmentNodeStoreContainer(blob), + asList(), + Result.EXCEPTION + }); + params.add(new Object[]{ + "Suppress the warning", + new SegmentNodeStoreContainer(blob), + new SegmentNodeStoreContainer(blob), + asList("--skip-checkpoints"), + Result.CHECKPOINTS_MISSING + }); + params.add(new Object[]{ + "Source data store defined, checkpoints migrated", + new SegmentNodeStoreContainer(blob), + new SegmentNodeStoreContainer(blob), + asList("--src-datastore=" + blob.getDescription()), + Result.CHECKPOINTS_COPIED + }); + return params; + } + + private final NodeStoreContainer source; + + private final NodeStoreContainer destination; + + private final List<String> args; + + private final Result expectedResult; + + public CopyCheckpointsTest(String name, NodeStoreContainer source, NodeStoreContainer destination, List<String> args, Result expectedResult) throws IOException, CliArgumentException { + this.source = source; + this.destination = destination; + this.args = args; + this.expectedResult = expectedResult; + + this.source.clean(); + this.destination.clean(); + } + + @Override + protected NodeStoreContainer getSourceContainer() { + return source; + } + + @Override + protected NodeStoreContainer getDestinationContainer() { + return destination; + } + + @Override + protected String[] getArgs() { + List<String> result = new ArrayList<String>(args); + result.addAll(asList("--disable-mmap", source.getDescription(), destination.getDescription())); + return result.toArray(new String[result.size()]); + } + + @Before + @Override + public void prepare() throws Exception { + NodeStore source = getSourceContainer().open(); + try { + initContent(source); + } finally { + getSourceContainer().close(); + } + + String[] args = getArgs(); + log.info("oak2oak {}", Joiner.on(' ').join(args)); + try { + MigrationCliArguments cliArgs = new MigrationCliArguments(OptionParserFactory.create().parse(args)); + MigrationOptions options = new MigrationOptions(cliArgs); + StoreArguments stores = new StoreArguments(options, cliArgs.getArguments()); + DatastoreArguments datastores = new DatastoreArguments(options, stores, stores.srcUsesEmbeddedDatastore()); + OakUpgrade.migrate(options, stores, datastores); + } catch(RuntimeException e) { + if (expectedResult == Result.EXCEPTION) { + return; + } else { + throw e; + } + } + if (expectedResult == Result.EXCEPTION) { + fail("Migration should fail"); + } + createSession(); + } + + @Test + @Override + public void validateMigration() throws RepositoryException, IOException, CliArgumentException { + switch (expectedResult) { + case CHECKPOINTS_COPIED: + verifyCheckpoint(); + break; + + case CHECKPOINTS_MISSING: + verifyEmptyAsync(); + break; + } + } +} \ No newline at end of file Added: jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyNodeTypesUpgradeTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyNodeTypesUpgradeTest.java?rev=1792993&view=auto ============================================================================== --- jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyNodeTypesUpgradeTest.java (added) +++ jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyNodeTypesUpgradeTest.java Fri Apr 28 07:16:13 2017 @@ -0,0 +1,54 @@ +/* + * 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 org.apache.jackrabbit.api.JackrabbitSession; +import org.apache.jackrabbit.commons.cnd.CndImporter; +import org.junit.Test; + +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeDefinition; +import javax.jcr.nodetype.NodeType; +import javax.jcr.nodetype.NodeTypeManager; +import javax.jcr.nodetype.PropertyDefinition; +import java.io.InputStreamReader; +import java.io.Reader; + +import static org.junit.Assert.assertEquals; + +public class CopyNodeTypesUpgradeTest extends AbstractRepositoryUpgradeTest { + + @Override + protected void createSourceContent(Session session) throws Exception { + final Reader cnd = new InputStreamReader(getClass().getResourceAsStream("/test-nodetypes.cnd")); + CndImporter.registerNodeTypes(cnd, session); + } + + @Test + public void customNodeTypesAreRegistered() throws RepositoryException { + final JackrabbitSession adminSession = createAdminSession(); + final NodeTypeManager nodeTypeManager = adminSession.getWorkspace().getNodeTypeManager(); + final NodeType testFolderNodeType = nodeTypeManager.getNodeType("test:Folder"); + final NodeDefinition[] cnd = testFolderNodeType.getChildNodeDefinitions(); + final PropertyDefinition[] pd = testFolderNodeType.getPropertyDefinitions(); + assertEquals("More than one child node definition", 1, cnd.length); + assertEquals("Incorrect default primary type", "test:Folder", cnd[0].getDefaultPrimaryTypeName()); + assertEquals("More than two property definitions", 4, pd.length); + adminSession.logout(); + } +} Added: jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistorySidegradeTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistorySidegradeTest.java?rev=1792993&view=auto ============================================================================== --- jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistorySidegradeTest.java (added) +++ jackrabbit/oak/branches/1.0/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/CopyVersionHistorySidegradeTest.java Fri Apr 28 07:16:13 2017 @@ -0,0 +1,64 @@ +/* + * 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 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.memory.MemoryNodeStore; +import org.apache.jackrabbit.oak.spi.state.NodeStore; +import org.apache.jackrabbit.oak.upgrade.util.VersionCopyTestUtils.VersionCopySetup; +import org.junit.Before; + +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import java.io.IOException; + +public class CopyVersionHistorySidegradeTest extends CopyVersionHistoryTest { + + private static NodeStore sourceNodeStore; + + @Before + @Override + public void upgradeRepository() throws Exception { + if (sourceNodeStore == null) { + sourceNodeStore = new MemoryNodeStore(); + RepositoryImpl repository = (RepositoryImpl) new Jcr(new Oak(sourceNodeStore)).createRepository(); + Session session = repository.login(CREDENTIALS); + try { + createSourceContent(session); + } finally { + session.logout(); + repository.shutdown(); + } + } + } + + @Override + protected Session performCopy(VersionCopySetup setup) throws RepositoryException, IOException { + final NodeStore targetNodeStore = new MemoryNodeStore(); + final RepositorySidegrade sidegrade = new RepositorySidegrade(sourceNodeStore, targetNodeStore); + setup.setup(sidegrade.versionCopyConfiguration); + sidegrade.copy(); + + repository = (RepositoryImpl) new Jcr(new Oak(targetNodeStore)).createRepository(); + Session s = repository.login(AbstractRepositoryUpgradeTest.CREDENTIALS); + sessions.add(s); + return s; + } +}
