com.meltmedia.cadmium.core.git.DelayedGitServiceInitializer.java Source code

Java tutorial

Introduction

Here is the source code for com.meltmedia.cadmium.core.git.DelayedGitServiceInitializer.java

Source

/**
 *    Copyright 2012 meltmedia
 *
 *    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.meltmedia.cadmium.core.git;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>Manages delayed initialization of a common {@link GitService} reference, as well as switching of repositories.</p>
 * 
 * @author John McEntire
 *
 */
public class DelayedGitServiceInitializer implements Closeable {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * The class member variable to hold the common reference.
     */
    protected GitService git;

    /**
     * Enforces the delayed initialization of the common {@link GitService} by causing Threads to wait for the GitService to be initialized.
     */
    private CountDownLatch latch;

    /**
     * The Object that manages all read/write locks used by an instance of this class.
     */
    private final ReentrantReadWriteLock locker = new ReentrantReadWriteLock();

    private final ReadLock readLock = locker.readLock();
    private final WriteLock writeLock = locker.writeLock();

    /**
     * Basic constructor that creates a CountDownLatch with a count of 1.
     */
    public DelayedGitServiceInitializer() {
        latch = new CountDownLatch(1);
    }

    /**
     * <p>Retrieves the common reference to a GitService.</p>
     * <p>If the reference has not been set yet, any calling threads will block until the reference is set or the thread is interrupted.</p>
     * 
     * @return The common reference to {@link GitService}.
     * @throws Exception Thrown if the current thread has been interrupted while waiting for the GitService to initialize.
     */
    public GitService getGitService() throws Exception {
        logger.trace("Getting git service.");
        latch.await();
        readLock.lock();
        return git;
    }

    /**
     * <p>Retrieves the common reference to a GitService without blocking.</p>
     * <p>If the reference has not been set yet, any calling threads will not block. This will aquire a read lock on the git service.</p>
     * 
     * @return The common reference to {@link GitService}.
     * @throws Exception Thrown if the GitService is not initialized.
     */
    public GitService getGitServiceNoBlock() throws Exception {
        if (git == null) {
            throw new IllegalStateException("The git service is not yet initialized.");
        }
        logger.trace("Getting git service.");
        readLock.lock();
        return git;
    }

    /**
     * <p>Switches the current reference to point to a new remote git repository.</p>
     * <p>This method will block if the common reference has not been set or there are any threads currently using the referenced git service.</p>
     * 
     * @param uri The uri to the remote repository.
     * @throws Exception
     */
    public synchronized void switchRepository(String uri) throws Exception {
        File oldRepo = null;
        File gitDir = null;
        try {
            writeLock.lock();
            latch.await();
            if (!git.getRemoteRepository().equalsIgnoreCase(uri)) {
                logger.debug("Switching repo from {} to {}", git.getRemoteRepository(), uri);
                gitDir = new File(git.getBaseDirectory());
                IOUtils.closeQuietly(git);
                git = null;
                oldRepo = new File(gitDir.getParentFile(), "old-git-checkout");
                FileUtils.deleteQuietly(oldRepo);
                FileUtils.copyDirectory(gitDir, oldRepo);
                FileUtils.deleteDirectory(gitDir);
                git = GitService.cloneRepo(uri, gitDir.getAbsolutePath());
            }
        } catch (InterruptedException ie) {
            logger.warn("Thread interrupted, must be shutting down!", ie);
            throw ie;
        } catch (Exception e) {
            logger.warn("Failed to switch repository. Rolling back!");
            if (git != null) {
                IOUtils.closeQuietly(git);
                git = null;
            }
            FileUtils.deleteQuietly(gitDir);
            FileUtils.copyDirectory(oldRepo, gitDir);
            git = GitService.createGitService(gitDir.getAbsolutePath());
            throw e;
        } finally {
            FileUtils.deleteQuietly(oldRepo);
            writeLock.unlock();
        }
    }

    /**
     * Releases the read lock on this class and must be called from the same thread that called the {@link DelayedGitServiceInitializer.getGitService()}.
     */
    public void releaseGitService() {
        readLock.unlock();
    }

    /**
     * Sets the common reference an releases any threads waiting for the reference to be set.
     * 
     * @param git 
     */
    public void setGitService(GitService git) {
        if (git != null) {
            logger.debug("Setting git service");
            this.git = git;
            latch.countDown();
        }
    }

    /**
     * Releases any used resources and waiting threads.
     * 
     * @throws IOException
     */
    @Override
    public void close() throws IOException {
        if (git != null) {
            IOUtils.closeQuietly(git);
            git = null;
        }
        latch.countDown();
        try {
            latch.notifyAll();
        } catch (Exception e) {
            logger.trace("Failed to notifyAll");
        }
        try {
            locker.notifyAll();
        } catch (Exception e) {
            logger.trace("Failed to notifyAll");
        }
    }

}