org.gradle.api.changedetection.state.DefaultDirectoryStateChangeDetecter.java Source code

Java tutorial

Introduction

Here is the source code for org.gradle.api.changedetection.state.DefaultDirectoryStateChangeDetecter.java

Source

/*
 * Copyright 2009 the original author or authors.
 *
 * 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 org.gradle.api.changedetection.state;

import org.gradle.api.changedetection.digest.*;
import org.gradle.api.changedetection.ChangeProcessor;
import org.gradle.api.GradleException;
import org.gradle.api.io.IoFactory;
import org.gradle.util.ThreadUtils;
import org.gradle.util.Clock;
import org.gradle.util.queues.BlockingQueueItemProducer;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.*;
import java.util.concurrent.*;

/**
 * @author Tom Eyckmans
 */
class DefaultDirectoryStateChangeDetecter implements DirectoryStateChangeDetecter {

    private final File directoryToProcess;
    private final IoFactory ioFactory;
    private final DirectoryStateBuilder directoryStateBuilder;
    private final DigesterCache digesterCache;
    private final DigesterUtil digesterUtil;
    private final DirectoryListFileCreator directoryListFileCreator;
    private final StateFileUtil stateFileUtil;
    private final BlockingQueue<StateChangeEvent> stateChangeEventQueue;
    private final BlockingQueueItemProducer<StateChangeEvent> changeProcessorEventProducer;
    private final List<DirectoryStateDigestComparator> directoryStateDigestComparators;
    private ExecutorService threadPool;
    private final StateFileChangeListenerUtil stateFileChangeListenerUtil;
    private final StateComparator stateComparator;

    DefaultDirectoryStateChangeDetecter(final File directoryToProcess, IoFactory ioFactory,
            DirectoryStateBuilder directoryStateBuilder, DigesterCache digesterCache, DigesterUtil digesterUtil,
            DirectoryListFileCreator directoryListFileCreator, StateFileUtil stateFileUtil,
            BlockingQueue<StateChangeEvent> stateChangeEventQueue,
            BlockingQueueItemProducer<StateChangeEvent> changeProcessorEventProducer,
            List<DirectoryStateDigestComparator> directoryStateDigestComparators,
            StateFileChangeListenerUtil stateFileChangeListenerUtil, StateComparator stateComparator) {
        if (directoryToProcess == null)
            throw new IllegalArgumentException("directoryToProcess is null!");
        if (!directoryToProcess.exists())
            throw new IllegalArgumentException("directoryToProcess does not exists!");
        if (!directoryToProcess.isDirectory())
            throw new IllegalArgumentException("directoryToProcess is not a directory!");

        this.directoryToProcess = directoryToProcess;
        this.ioFactory = ioFactory;
        this.directoryStateBuilder = directoryStateBuilder;
        this.digesterCache = digesterCache;
        this.digesterUtil = digesterUtil;
        this.directoryListFileCreator = directoryListFileCreator;
        this.stateFileUtil = stateFileUtil;
        this.stateChangeEventQueue = stateChangeEventQueue;
        this.changeProcessorEventProducer = changeProcessorEventProducer;
        this.directoryStateDigestComparators = directoryStateDigestComparators;
        this.stateFileChangeListenerUtil = stateFileChangeListenerUtil;
        this.stateComparator = stateComparator;
    }

    public File getDirectoryToProcess() {
        return directoryToProcess;
    }

    public void detectChanges(ChangeProcessor changeProcessor) {
        Clock c = new Clock();
        try {
            int lowestLevel = 0;
            try {
                lowestLevel = directoryListFileCreator.createDirectoryListFiles(directoryToProcess);
            } catch (IOException e) {
                throw new GradleException("failed to create directory list files", e);
            }

            // Calculate the digests of files and directories
            Map<String, DirectoryState> previousLevelDirectoryStates = Collections
                    .unmodifiableMap(new HashMap<String, DirectoryState>());
            Map<String, DirectoryState> currentLevelDirectoryStates = new ConcurrentHashMap<String, DirectoryState>();
            for (int levelIndex = lowestLevel; levelIndex >= 0; levelIndex--) {
                final File directoryLevelListFile = stateFileUtil.getDirsListFile(levelIndex);
                threadPool = ThreadUtils.newFixedThreadPool(4);

                if (directoryLevelListFile.exists()) {
                    final File stateFile = stateFileUtil
                            .getNewDirsStateFile(stateFileUtil.getDirsStateFilename(levelIndex));
                    final StateFileWriter newDirectoriesStateFileWriter = new StateFileWriter(ioFactory, stateFile);

                    BufferedReader directoryListFileReader = null;

                    try {
                        directoryListFileReader = new BufferedReader(new FileReader(directoryLevelListFile));

                        String absoluteDirectoryPath = null;
                        while ((absoluteDirectoryPath = directoryListFileReader.readLine()) != null) {
                            final DirectoryState directoryState = directoryStateBuilder
                                    .directory(new File(absoluteDirectoryPath)).getDirectoryState();

                            final DirectoryStateDigestCalculator digestCalculator = new DirectoryStateDigestCalculator(
                                    directoryState, digesterCache, digesterUtil, this, currentLevelDirectoryStates,
                                    previousLevelDirectoryStates, ioFactory);

                            threadPool.submit(digestCalculator);
                        }

                        // each directory level has to be processed before continueing with the next(higher) level
                        ThreadUtils.shutdown(threadPool);

                        final List<DirectoryState> currentLevelDirectoryStatesList = new ArrayList<DirectoryState>(
                                currentLevelDirectoryStates.values());

                        Collections.sort(currentLevelDirectoryStatesList);
                        // if one of the directory state processors failed -> change detection fails
                        // This is a simple not so efficient way, we could fail earlier but that will make everything more complicated

                        for (final DirectoryState directoryState : currentLevelDirectoryStatesList) {
                            final Throwable failureCause = directoryState.getFailureCause();
                            if (failureCause != null)
                                throw new GradleException("Failed to detect changes", failureCause);
                            else {
                                newDirectoriesStateFileWriter.addDigest(directoryState.getRelativePath(),
                                        directoryState.getDigest());
                            }
                        }

                    } catch (IOException e) {
                        throw new GradleException("failed to detect changes (dirs."
                                + newDirectoriesStateFileWriter.getStateFile().getAbsolutePath()
                                + ".state write failed)", e);
                    } finally {
                        IOUtils.closeQuietly(directoryListFileReader);
                        FileUtils.deleteQuietly(directoryLevelListFile);
                        newDirectoriesStateFileWriter.close();
                    }
                }

                previousLevelDirectoryStates = Collections
                        .unmodifiableMap(new HashMap<String, DirectoryState>(currentLevelDirectoryStates));
                currentLevelDirectoryStates = new ConcurrentHashMap<String, DirectoryState>();
            }

            // Compare new and old directory state + notify DirectoryStateChangeDetecterListener
            try {
                boolean keepComparing = true;
                int currentLevel = 0;

                final StateChangeEventDispatcher stateChangeEventDispatcher = new StateChangeEventDispatcher(
                        stateChangeEventQueue, 100L, TimeUnit.MILLISECONDS, changeProcessor);
                final Thread changeProcessorEventThread = new Thread(stateChangeEventDispatcher);
                changeProcessorEventThread.start();

                threadPool = ThreadUtils.newFixedThreadPool(4);

                while (keepComparing && currentLevel <= lowestLevel) {
                    keepComparing = stateComparator.compareState(this, currentLevel);

                    currentLevel++;
                }

                ThreadUtils.shutdown(threadPool);

                while (!stateChangeEventQueue.isEmpty()) {
                    Thread.yield();
                }
                stateChangeEventDispatcher.stopConsuming();

                ThreadUtils.join(changeProcessorEventThread);

                for (DirectoryStateDigestComparator directoryStateDigestComparator : directoryStateDigestComparators) {
                    final Throwable failureCause = directoryStateDigestComparator.getFailureCause();
                    if (failureCause != null)
                        throw new GradleException("failed to compare directory state", failureCause);
                }
            } catch (IOException e) {
                throw new GradleException("failed to compare new and old state", e);
            }

            // Remove old directory state
            try {
                FileUtils.deleteDirectory(stateFileUtil.getOldDirectoryStateDir());
            } catch (IOException e) {
                throw new GradleException("failed to clean old state", e);
            }
            // Move new to old directory state
            try {
                FileUtils.moveDirectory(stateFileUtil.getNewDirectoryStateDir(),
                        stateFileUtil.getOldDirectoryStateDir());
            } catch (IOException e) {
                throw new GradleException("failed to transfer current state to old state", e);
            }
        } finally {
            System.out.println(c.getTime());
        }
    }

    void submitDirectoryStateDigestComparator(final DirectoryStateDigestComparator directoryStateDigestComparator) {
        threadPool.submit(directoryStateDigestComparator);
        directoryStateDigestComparators.add(directoryStateDigestComparator);
    }

    BlockingQueueItemProducer<StateChangeEvent> getChangeProcessorEventProducer() {
        return changeProcessorEventProducer;
    }

    StateFileUtil getStateFileUtil() {
        return stateFileUtil;
    }

    public StateFileChangeListenerUtil getStateFileChangeListenerUtil() {
        return stateFileChangeListenerUtil;
    }
}