| /* |
| * 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.jcr.delegate; |
| |
| import javax.jcr.InvalidItemStateException; |
| import javax.jcr.RepositoryException; |
| import javax.jcr.UnsupportedRepositoryOperationException; |
| import javax.jcr.version.LabelExistsVersionException; |
| import javax.jcr.version.VersionException; |
| |
| import org.apache.jackrabbit.oak.api.PropertyState; |
| import org.apache.jackrabbit.oak.api.Root; |
| import org.apache.jackrabbit.oak.api.Tree; |
| import org.apache.jackrabbit.oak.api.Type; |
| import org.apache.jackrabbit.oak.commons.PathUtils; |
| import org.apache.jackrabbit.oak.jcr.version.ReadWriteVersionManager; |
| import org.apache.jackrabbit.oak.jcr.version.VersionStorage; |
| import org.jetbrains.annotations.NotNull; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import static org.apache.jackrabbit.JcrConstants.JCR_BASEVERSION; |
| import static org.apache.jackrabbit.JcrConstants.JCR_FROZENMIXINTYPES; |
| import static org.apache.jackrabbit.JcrConstants.JCR_FROZENPRIMARYTYPE; |
| import static org.apache.jackrabbit.JcrConstants.JCR_FROZENUUID; |
| import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES; |
| import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE; |
| import static org.apache.jackrabbit.JcrConstants.JCR_UUID; |
| import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONHISTORY; |
| import static org.apache.jackrabbit.oak.spi.version.VersionConstants.RESTORE_PREFIX; |
| |
| /** |
| * {@code VersionManagerDelegate}... |
| */ |
| public final class VersionManagerDelegate { |
| |
| private final SessionDelegate sessionDelegate; |
| |
| private final ReadWriteVersionManager versionManager; |
| |
| public static VersionManagerDelegate create(SessionDelegate sessionDelegate) { |
| return new VersionManagerDelegate(sessionDelegate); |
| } |
| |
| private VersionManagerDelegate(SessionDelegate sessionDelegate) { |
| this.sessionDelegate = sessionDelegate; |
| this.versionManager = new ReadWriteVersionManager(sessionDelegate); |
| } |
| |
| @NotNull |
| public VersionDelegate checkin(@NotNull NodeDelegate nodeDelegate) |
| throws RepositoryException { |
| return VersionDelegate.create(sessionDelegate, |
| versionManager.checkin(getTree(nodeDelegate))); |
| } |
| |
| public void checkout(@NotNull NodeDelegate nodeDelegate) |
| throws RepositoryException { |
| // perform the operation on a fresh root because |
| // it must not save pending changes in the workspace |
| Root fresh = sessionDelegate.getContentSession().getLatestRoot(); |
| versionManager.checkout(fresh, nodeDelegate.getPath()); |
| } |
| |
| public boolean isCheckedOut(@NotNull NodeDelegate nodeDelegate) |
| throws RepositoryException { |
| return versionManager.isCheckedOut(getTree(nodeDelegate)); |
| } |
| |
| @NotNull |
| public VersionHistoryDelegate createVersionHistory(@NotNull NodeDelegate versionHistory) |
| throws RepositoryException { |
| return new VersionHistoryDelegate(sessionDelegate, getTree(versionHistory)); |
| } |
| |
| @NotNull |
| public VersionDelegate createVersion(@NotNull NodeDelegate version) |
| throws RepositoryException { |
| return VersionDelegate.create(sessionDelegate, getTree(version)); |
| } |
| |
| @NotNull |
| public VersionHistoryDelegate getVersionHistory(@NotNull NodeDelegate versionable) |
| throws RepositoryException { |
| Tree vh = versionManager.getVersionHistory(getTree(versionable)); |
| if (vh == null) { |
| throw new UnsupportedRepositoryOperationException("Node does not" + |
| " have a version history: " + versionable.getPath()); |
| } |
| return new VersionHistoryDelegate(sessionDelegate, vh); |
| } |
| |
| @NotNull |
| public VersionDelegate getBaseVersion(@NotNull NodeDelegate versionable) |
| throws RepositoryException { |
| Tree v = versionManager.getBaseVersion(getTree(versionable)); |
| if (v == null) { |
| throw new UnsupportedRepositoryOperationException("Node does not" + |
| " have a base version: " + versionable.getPath()); |
| } |
| return VersionDelegate.create(sessionDelegate, v); |
| } |
| |
| @NotNull |
| public VersionDelegate getVersionByIdentifier(@NotNull String identifier) |
| throws RepositoryException { |
| Tree t = sessionDelegate.getIdManager().getTree(identifier); |
| if (t == null || !t.exists()) { |
| throw new RepositoryException("No such Version with identifier: " + |
| identifier); |
| } |
| return VersionDelegate.create(sessionDelegate, t); |
| } |
| |
| public void restore(@NotNull NodeDelegate parent, |
| @NotNull String oakName, |
| @NotNull VersionDelegate vd) |
| throws RepositoryException { |
| NodeDelegate frozen = vd.getFrozenNode(); |
| PropertyState primaryType = frozen.getProperty( |
| JCR_FROZENPRIMARYTYPE).getPropertyState(); |
| PropertyState uuid = frozen.getProperty( |
| JCR_FROZENUUID).getPropertyState(); |
| PropertyDelegate mixinTypes = frozen.getPropertyOrNull(JCR_FROZENMIXINTYPES); |
| if (parent.getChild(oakName) == null) { |
| // create a sentinel node with a jcr:baseVersion pointing |
| // to the version to restore |
| Tree t = parent.getTree().addChild(oakName); |
| t.setProperty(JCR_PRIMARYTYPE, primaryType.getValue(Type.NAME), Type.NAME); |
| t.setProperty(JCR_UUID, uuid.getValue(Type.STRING), Type.STRING); |
| if (mixinTypes != null && mixinTypes.getPropertyState().count() > 0) { |
| t.setProperty(JCR_MIXINTYPES, |
| mixinTypes.getPropertyState().getValue(Type.NAMES), |
| Type.NAMES); |
| } |
| t.setProperty(JCR_BASEVERSION, vd.getIdentifier(), Type.REFERENCE); |
| t.setProperty(JCR_VERSIONHISTORY, vd.getParent().getIdentifier(), Type.REFERENCE); |
| } else { |
| Tree t = parent.getChild(oakName).getTree(); |
| t.setProperty(JCR_BASEVERSION, RESTORE_PREFIX + vd.getIdentifier(), Type.REFERENCE); |
| } |
| } |
| |
| /** |
| * Add a version label to the given version history. |
| * |
| * @param versionHistory the version history. |
| * @param version the version. |
| * @param oakVersionLabel the version label. |
| * @param moveLabel whether to move the label if it already exists. |
| * @throws InvalidItemStateException if any of the nodes is stale. |
| * @throws LabelExistsVersionException if moveLabel is false, and an attempt |
| * is made to add a label that already exists in this version history. |
| * @throws VersionException if the specified version does not exist in this |
| * version history or if the specified version is the root version (jcr:rootVersion). |
| * @throws RepositoryException if another error occurs. |
| */ |
| public void addVersionLabel(@NotNull VersionHistoryDelegate versionHistory, |
| @NotNull VersionDelegate version, |
| @NotNull String oakVersionLabel, |
| boolean moveLabel) |
| throws InvalidItemStateException, LabelExistsVersionException, |
| VersionException, RepositoryException { |
| // perform operation on fresh storage to not interfere |
| // with pending changes in the workspace. |
| Root fresh = sessionDelegate.getContentSession().getLatestRoot(); |
| VersionStorage storage = new VersionStorage(fresh); |
| String vhRelPath = PathUtils.relativize(VersionStorage.VERSION_STORAGE_PATH, |
| checkNotNull(versionHistory).getPath()); |
| versionManager.addVersionLabel(storage, vhRelPath, |
| checkNotNull(version).getIdentifier(), |
| checkNotNull(oakVersionLabel), |
| moveLabel); |
| } |
| |
| /** |
| * Removes a version label from the given history. |
| * |
| * @param versionHistory the version history. |
| * @param oakVersionLabel the version label. |
| * @throws InvalidItemStateException if any of the nodes is stale. |
| * @throws VersionException if the name label does not exist in this version history. |
| * @throws RepositoryException if another error occurs. |
| */ |
| public void removeVersionLabel(@NotNull VersionHistoryDelegate versionHistory, |
| @NotNull String oakVersionLabel) |
| throws InvalidItemStateException, VersionException, RepositoryException { |
| // perform operation on fresh storage to not interfere |
| // with pending changes in the workspace. |
| Root fresh = sessionDelegate.getContentSession().getLatestRoot(); |
| VersionStorage storage = new VersionStorage(fresh); |
| String vhRelPath = PathUtils.relativize(VersionStorage.VERSION_STORAGE_PATH, |
| checkNotNull(versionHistory).getPath()); |
| versionManager.removeVersionLabel(storage, vhRelPath, |
| checkNotNull(oakVersionLabel)); |
| } |
| |
| /** |
| * Removes a version from the given history. |
| * |
| * @param versionHistory the version history delegate. |
| * @param oakVersionName the version name |
| * @throws RepositoryException if an error occurs. |
| */ |
| public void removeVersion(@NotNull VersionHistoryDelegate versionHistory, |
| @NotNull String oakVersionName) throws RepositoryException { |
| // perform operation on fresh storage to not interfere |
| // with pending changes in the workspace. |
| Root fresh = sessionDelegate.getContentSession().getLatestRoot(); |
| VersionStorage storage = new VersionStorage(fresh); |
| String vhRelPath = PathUtils.relativize(VersionStorage.VERSION_STORAGE_PATH, |
| checkNotNull(versionHistory).getPath()); |
| versionManager.removeVersion(storage, vhRelPath, oakVersionName); |
| } |
| |
| //----------------------------< internal >---------------------------------- |
| |
| /** |
| * Returns the underlying tree. |
| * |
| * @param nodeDelegate the node delegate. |
| * @return the underlying tree. |
| * @throws InvalidItemStateException if the location points to a stale item. |
| */ |
| @NotNull |
| private static Tree getTree(@NotNull NodeDelegate nodeDelegate) |
| throws InvalidItemStateException { |
| return checkNotNull(nodeDelegate).getTree(); |
| } |
| |
| } |