/* * Copyright (c) 2016 OBiBa. All rights reserved. * * This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.obiba.git.command; import java.io.File; import java.io.IOException; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.eclipse.jgit.api.CloneCommand; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.internal.storage.file.FileRepository; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.transport.FetchResult; import org.eclipse.jgit.transport.RefSpec; import org.obiba.core.util.FileUtil; import org.obiba.git.GitException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.Lists; @Component public class GitCommandHandler { private static final Logger log = LoggerFactory.getLogger(GitCommandHandler.class); private static final List<RefSpec> DEFAULT_REF_SPEC = Lists.newArrayList( // new RefSpec("+refs/heads/*:refs/remotes/origin/*"), // new RefSpec("+refs/tags/*:refs/tags/*"), // new RefSpec("+refs/notes/*:refs/notes/*")); private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true); private final LoadingCache<String, Lock> readLocks = CacheBuilder.newBuilder() // .build(new CacheLoader<String, Lock>() { @Override public Lock load(@SuppressWarnings("NullableProblems") String key) throws Exception { log.trace("Create read lock for {}", key); return readWriteLock.readLock(); } } ); private final LoadingCache<String, Lock> writeLocks = CacheBuilder.newBuilder() // .build(new CacheLoader<String, Lock>() { @Override public Lock load(@SuppressWarnings("NullableProblems") String key) throws Exception { log.trace("Create write lock for {}", key); return readWriteLock.writeLock(); } } ); public <T> T execute(GitCommand<T> command) { lock(command); Git git = null; try { File repositoryPath = command.getRepositoryPath(); git = new Git(getLocalRepository(repositoryPath, command.getWorkPath())); fetchAllRepository(git); return command.execute(git); } catch(IOException | GitAPIException e) { throw new GitException(e); } finally { if(git != null) { git.close(); if(command.deleteClone()) deleteLocalRepository(git); } unlock(command); } } private void deleteLocalRepository(Git git) { File repoFile = git.getRepository().getWorkTree(); try { if(repoFile.exists()) FileUtil.delete(repoFile); } catch(IOException e) { log.error("Failed to remove local repository folder ({}).", e.getMessage()); } } private void lock(GitCommand<?> command) { log.trace("Lock for {}", command.getRepositoryPath().getAbsolutePath()); getLock(command).lock(); } private void unlock(GitCommand<?> command) { log.trace("Unlock for {}", command.getRepositoryPath().getAbsolutePath()); getLock(command).unlock(); } private synchronized Lock getLock(GitCommand<?> command) { try { String path = command.getRepositoryPath().getAbsolutePath(); return command instanceof GitWriteCommand ? writeLocks.get(path) : readLocks.get(path); } catch(ExecutionException e) { throw new RuntimeException(e); } } private Repository getLocalRepository(File repositoryPath, File localRepoDir) throws IOException, GitAPIException { if(!repositoryPath.exists()) { createBareRepository(repositoryPath); } String name = repositoryPath.getName(); name = name.substring(0, name.lastIndexOf(".git")); File cloneDir = new File(localRepoDir, name); Repository repository; if(!cloneDir.exists()) { CloneCommand clone = new CloneCommand(); clone.setBare(false); clone.setCloneAllBranches(true); clone.setURI("file://" + repositoryPath.getAbsolutePath()); clone.setDirectory(cloneDir); repository = clone.call().getRepository(); } else { repository = new FileRepository(new File(cloneDir, ".git")); } log.debug("Using clone of {}: {}", repositoryPath.getAbsolutePath(), repository.getWorkTree().getAbsolutePath()); return repository; } private Repository createBareRepository(File repositoryPath) throws IOException { log.debug("Create bare repository for {}", repositoryPath.getAbsolutePath()); Repository repository = new FileRepository(repositoryPath); repository.create(true); return repository; } private FetchResult fetchAllRepository(Git git) throws GitAPIException { return git.fetch().setRefSpecs(DEFAULT_REF_SPEC).call(); } }