/** * This file is part of git-as-svn. It is subject to the license terms * in the LICENSE file found in the top-level directory of this distribution * and at http://www.gnu.org/licenses/gpl-2.0.html. No part of git-as-svn, * including this file, may be copied, modified, propagated, or distributed * except according to the terms contained in the LICENSE file. */ package svnserver.repository.locks; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; import svnserver.repository.Depth; import svnserver.repository.VcsFile; import svnserver.repository.VcsRepository; import svnserver.repository.VcsRevision; import svnserver.server.SessionContext; import java.io.IOException; import java.util.Iterator; import java.util.Map; import java.util.SortedMap; import java.util.UUID; /** * Map lock manager. * * @author Artem V. Navrotskiy <bozaro@users.noreply.github.com> */ public class TreeMapLockManager implements LockManagerWrite { private final static char SEPARATOR = ':'; @NotNull private final VcsRepository repo; @NotNull private final SortedMap<String, LockDesc> locks; public TreeMapLockManager(@NotNull VcsRepository repo, @NotNull SortedMap<String, LockDesc> locks) { this.locks = locks; this.repo = repo; } @NotNull @Override public LockDesc[] lock(@NotNull SessionContext context, @Nullable String comment, boolean stealLock, @NotNull LockTarget[] targets) throws SVNException, IOException { final LockDesc[] result = new LockDesc[targets.length]; if (targets.length > 0) { final VcsRevision revision = repo.getLatestRevision(); // Create new locks list. for (int i = 0; i < targets.length; ++i) { final LockTarget target = targets[i]; final VcsFile file = revision.getFile(target.getPath()); if (file == null) { throw new SVNException(SVNErrorMessage.create(SVNErrorCode.FS_OUT_OF_DATE, target.getPath())); } if (file.isDirectory()) { throw new SVNException(SVNErrorMessage.create(SVNErrorCode.FS_NOT_FILE, target.getPath())); } final LockDesc currentLock = locks.get(repo.getUuid() + SEPARATOR + target.getPath()); if ((!stealLock) && (currentLock != null)) { throw new SVNException(SVNErrorMessage.create(SVNErrorCode.FS_PATH_ALREADY_LOCKED, "Path is already locked by {1}: {0}", target.getPath(), currentLock.getOwner())); } if (target.getRev() < file.getLastChange().getId()) { throw new SVNException(SVNErrorMessage.create(SVNErrorCode.FS_OUT_OF_DATE, target.getPath())); } result[i] = new LockDesc(file.getFullPath(), file.getContentHash(), createLockId(), context.getUser().getUserName(), comment, 0); } // Add locks. for (LockDesc lockDesc : result) { locks.put(repo.getUuid() + SEPARATOR + lockDesc.getPath(), lockDesc); } } return result; } @NotNull @Override public Iterator<LockDesc> getLocks(@NotNull String path, @NotNull Depth depth) throws SVNException { return depth.visit(new TreeMapLockDepthVisitor(locks, repo.getUuid() + SEPARATOR + path)); } @Override public LockDesc getLock(@NotNull String path) { return locks.get(repo.getUuid() + SEPARATOR + path); } @Override public void unlock(@NotNull SessionContext context, boolean breakLock, @NotNull UnlockTarget[] targets) throws SVNException { for (UnlockTarget target : targets) { final LockDesc lock = locks.get(repo.getUuid() + SEPARATOR + target.getPath()); if ((lock == null) || (!(breakLock || lock.getToken().equals(target.getToken())))) { throw new SVNException(SVNErrorMessage.create(SVNErrorCode.FS_NO_SUCH_LOCK, target.getPath())); } } for (UnlockTarget target : targets) { locks.remove(repo.getUuid() + SEPARATOR + target.getPath()); } } @Override public void validateLocks() throws SVNException { try { final VcsRevision revision = repo.getLatestRevision(); final Iterator<Map.Entry<String, LockDesc>> iter = locks.entrySet().iterator(); while (iter.hasNext()) { final Map.Entry<String, LockDesc> entry = iter.next(); final LockDesc item = entry.getValue(); final VcsFile file = revision.getFile(item.getPath()); if ((file == null) || file.isDirectory() || (!item.getHash().equals(file.getContentHash()))) { iter.remove(); } } } catch (IOException e) { throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e)); } } @Override public void renewLocks(@NotNull LockDesc[] lockDescs) throws IOException, SVNException { final VcsRevision revision = repo.getLatestRevision(); for (LockDesc lockDesc : lockDescs) { final String pathKey = repo.getUuid() + SEPARATOR + lockDesc.getPath(); if (!locks.containsKey(pathKey)) { final VcsFile file = revision.getFile(lockDesc.getPath()); if ((file != null) && (!file.isDirectory())) { locks.put(pathKey, new LockDesc(lockDesc.getPath(), file.getContentHash(), lockDesc.getToken(), lockDesc.getOwner(), lockDesc.getComment(), lockDesc.getCreated())); } } } } private static String createLockId() { return UUID.randomUUID().toString(); } }