Java tutorial
// Copyright (C) 2017 The Android Open Source Project // // Licensed 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 com.googlesource.gerrit.plugins.lfs.locks; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.flogger.FluentLogger; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.CurrentUser; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import com.googlesource.gerrit.plugins.lfs.LfsDateTime; import com.googlesource.gerrit.plugins.lfs.LfsGson; import com.googlesource.gerrit.plugins.lfs.locks.LfsLocksHandler.LfsLockExistsException; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Reader; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collection; import java.util.Optional; import java.util.stream.Stream; import org.eclipse.jgit.internal.storage.file.LockFile; import org.eclipse.jgit.lfs.errors.LfsException; class LfsProjectLocks { interface Factory { LfsProjectLocks create(Project.NameKey project); } private static final FluentLogger log = FluentLogger.forEnclosingClass(); private final LfsGson gson; private final PathToLockId toLockId; private final String project; private final Path locksPath; private final Cache<String, LfsLock> locks; @Inject LfsProjectLocks(LfsGson gson, PathToLockId toLockId, LfsLocksPathProvider locksPath, @Assisted Project.NameKey project) { this.gson = gson; this.toLockId = toLockId; this.project = project.get(); this.locksPath = Paths.get(locksPath.get(), this.project); this.locks = CacheBuilder.newBuilder().build(); } void load() { if (!Files.exists(locksPath)) { return; } try (Stream<Path> stream = Files.list(locksPath)) { stream.filter(Files::isRegularFile).forEach(path -> { if (!Files.isReadable(path)) { log.atWarning().log("Lock file [%s] in project %s is not readable", path, project); return; } try (Reader in = Files.newBufferedReader(path)) { LfsLock lock = gson.fromJson(in, LfsLock.class); locks.put(lock.id, lock); } catch (IOException e) { log.atWarning().withCause(e).log("Reading lock [%s] failed", path); } }); } catch (IOException e) { log.atWarning().withCause(e).log("Reading locks in project %s failed", project); } } Optional<LfsLock> getLock(String lockId) { return Optional.ofNullable(locks.getIfPresent(lockId)); } LfsLock createLock(CurrentUser user, LfsCreateLockInput input) throws LfsException { log.atFine().log("Create lock for %s in project %s", input.path, project); String lockId = toLockId.apply(input.path); LfsLock lock = locks.getIfPresent(lockId); if (lock != null) { throw new LfsLockExistsException(lock); } lock = new LfsLock(lockId, input.path, LfsDateTime.now(), new LfsLockOwner(user.getUserName().get())); LockFile fileLock = new LockFile(locksPath.resolve(lockId).toFile()); try { if (!fileLock.lock()) { log.atWarning().log("Cannot lock path [%s] in project %s", input.path, project); throw new LfsLockExistsException(lock); } } catch (IOException e) { String error = String.format("Locking path [%s] in project %s failed with error %s", input.path, project, e.getMessage()); log.atWarning().log(error); throw new LfsException(error); } try { try (OutputStreamWriter out = new OutputStreamWriter(fileLock.getOutputStream())) { gson.toJson(lock, out); } catch (IOException e) { String error = String.format("Locking path [%s] in project %s failed during write with error %s", input.path, project, e.getMessage()); log.atWarning().log(error); throw new LfsException(error); } if (!fileLock.commit()) { String error = String.format("Committing lock to path [%s] in project %s failed", input.path, project); log.atWarning().log(error); throw new LfsException(error); } // put lock object to cache while file lock is being hold so that // there is no chance that other process performs lock operation // in the meantime (either cache returns with existing object or // LockFile.lock fails on locking attempt) locks.put(lockId, lock); } finally { fileLock.unlock(); } return lock; } void deleteLock(LfsLock lock) throws LfsException { LockFile fileLock = new LockFile(locksPath.resolve(lock.id).toFile()); try { if (!fileLock.lock()) { String error = String.format("Deleting lock on path [%s] in project %s is not possible", lock.path, project); log.atWarning().log(error); throw new LfsException(error); } } catch (IOException e) { String error = String.format("Getting lock on path [%s] in project %s failed with error %s", lock.path, project, e.getMessage()); log.atWarning().log(error); throw new LfsException(error); } try { Files.deleteIfExists(locksPath.resolve(lock.id)); locks.invalidate(lock.id); } catch (IOException e) { String error = String.format("Deleting lock on path [%s] in project %s failed with error %s", lock.path, project, e.getMessage()); log.atWarning().log(error); throw new LfsException(error); } finally { fileLock.unlock(); } } Collection<LfsLock> getLocks() { return locks.asMap().values(); } }