All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.arpnetworking.configuration.triggers.DirectoryTrigger Maven / Gradle / Ivy

Go to download

(Re)Aggregates host level statistics across clusters and writes both host and cluster statistics to various destinations.

There is a newer version: 1.13.7
Show newest version
/*
 * Copyright 2014 Groupon.com
 *
 * 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.arpnetworking.configuration.triggers;

import com.arpnetworking.commons.builder.OvalBuilder;
import com.arpnetworking.configuration.Trigger;
import com.arpnetworking.logback.annotations.LogValue;
import com.arpnetworking.steno.LogValueMapFactory;
import com.arpnetworking.steno.Logger;
import com.arpnetworking.steno.LoggerFactory;
import com.google.common.base.MoreObjects;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import net.sf.oval.constraint.NotNull;

import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * Trigger implementation based on the contents of a directory.
 * Configurable to either monitor all content or only specific content. The
 * FileTrigger is used to determine whether a file changed. This
 * is intended to monitor the files in a single directory and is not designed
 * to monitor a directory tree (e.g. it is not recursive).
 *
 * Note: If you are monitoring a directory in which files are created
 * and removed this implementation needs to be modified to purge file trigger
 * instances when a file is removed!
 *
 * @author Ville Koskela (ville dot koskela at inscopemetrics dot com)
 */
public final class DirectoryTrigger implements Trigger {

    @Override
    public boolean evaluateAndReset() {
        final boolean exists = _directory.exists() && _directory.isDirectory();

        // Track directory contents
        if (exists) {
            for (final File file : MoreObjects.firstNonNull(_directory.listFiles(), EMPTY_FILE_ARRAY)) {
                // Note: To save memory in cases where a directory contains
                // temporary files (e.g. create, destroy and never recreate)
                // we should remove any triggers for files that no longer exist
                // also noting that a deletion should trigger a reload!
                // TODO(vkoskela): Implement memory saving [MAI-444]
                if (!_evaluationFileTriggers.containsKey(file.getName()) && isFileMonitored(file)) {
                    LOGGER.debug()
                            .setMessage("Monitoring new file")
                            .addData("file", file)
                            .log();
                    _evaluationFileTriggers.put(
                            file.getName(),
                            new FileTrigger.Builder()
                                    .setFile(file)
                                    .build());
                }
            }
        } else {
            _evaluationFileTriggers.clear();
        }

        // Check files for changes
        boolean fileChanged = false;
        for (final FileTrigger fileTrigger : _evaluationFileTriggers.values()) {
            fileChanged = fileChanged || fileTrigger.evaluateAndReset();
        }

        // Handle directory creation/removal
        if (!_exists.isPresent()) {
            LOGGER.debug()
                    .setMessage("Trigger initialized")
                    .addData("directory", _directory)
                    .addData("exists", exists)
                    .log();

            _exists = Optional.of(exists);
            return true;
        } else if (_exists.get() != exists) {
            LOGGER.debug()
                    .setMessage("Directory created or removed")
                    .addData("directory", _directory)
                    .addData("exists", exists)
                    .log();

            _exists = Optional.of(exists);
            return true;
        }

        if (fileChanged) {
            LOGGER.debug()
                    .setMessage("Directory contents changed")
                    .addData("directory", _directory)
                    .addData("exists", true)
                    .log();
        }
        return fileChanged;
    }

    private boolean isFileMonitored(final File file) {
        if (_fileNames.isEmpty() && _fileNamePatterns.isEmpty()) {
            return true;
        } else {
            if (_fileNames.contains(file.getName())) {
                return true;
            }
            for (final Pattern pattern : _fileNamePatterns) {
                if (pattern.matcher(file.getName()).matches()) {
                    return true;
                }
            }
        }
        return false;
    }


    /**
     * Generate a Steno log compatible representation.
     *
     * @return Steno log compatible representation.
     */
    @LogValue
    public Object toLogValue() {
        return LogValueMapFactory.builder()
                .put("directory", _directory)
                .put("exists", _exists)
                .put("fileNames", _fileNames)
                .put("fileNamePatterns", _fileNamePatterns)
                .put("evaluationFileTriggers", _evaluationFileTriggers)
                .build();
    }

    @Override
    public String toString() {
        return toLogValue().toString();
    }

    private DirectoryTrigger(final Builder builder) {
        _directory = builder._directory;
        _fileNames = Sets.newHashSet(builder._fileNames);
        _fileNamePatterns = builder._fileNamePatterns;
    }

    private final File _directory;
    private final Set _fileNames;
    private final List _fileNamePatterns;

    private Optional _exists = Optional.empty();
    private final Map _evaluationFileTriggers = Maps.newHashMap();

    private static final File[] EMPTY_FILE_ARRAY = new File[0];
    private static final Logger LOGGER = LoggerFactory.getLogger(DirectoryTrigger.class);

    /**
     * Builder for DirectoryTrigger.
     */
    public static final class Builder extends OvalBuilder {

        /**
         * Public constructor.
         */
        public Builder() {
            super(DirectoryTrigger::new);
        }

        /**
         * Set the directory.
         *
         * @param value The directory.
         * @return This Builder instance.
         */
        public Builder setDirectory(final File value) {
            _directory = value;
            return this;
        }

        /**
         * Set the Collection of file names. Optional. Default is
         * an empty list (e.g. all files). Cannot be null.
         *
         * Note: Both the file names and file name patterns must be
         * empty (e.g. unset) in order to consider all files in the directory.
         *
         * @param value The Collection of file names.
         * @return This Builder instance.
         */
        public Builder setFileNames(final Collection value) {
            _fileNames = Lists.newArrayList(value);
            return this;
        }

        /**
         * Add a file name.
         *
         * @param value The file name.
         * @return This Builder instance.
         */
        public Builder addFileName(final String value) {
            if (_fileNames == null) {
                _fileNames = Lists.newArrayList(value);
            } else {
                _fileNames.add(value);
            }
            return this;
        }

        /**
         * Set the Collection of file name patterns. Optional.
         * Default is an empty list (e.g. all files). Cannot be null.
         *
         * Note: Both the file names and file name patterns must be
         * empty (e.g. unset) in order to consider all files in the directory.
         *
         * @param value The Collection of file name patterns.
         * @return This Builder instance.
         */
        public Builder setFileNamePatterns(final Collection value) {
            _fileNamePatterns = Lists.newArrayList(value);
            return this;
        }

        /**
         * Add a file name pattern.
         *
         * @param value The file name pattern.
         * @return This Builder instance.
         */
        public Builder addFileNamePattern(final Pattern value) {
            if (_fileNamePatterns == null) {
                _fileNamePatterns = Lists.newArrayList(value);
            } else {
                _fileNamePatterns.add(value);
            }
            return this;
        }

        @NotNull
        private File _directory;
        @NotNull
        private List _fileNames = Lists.newArrayList();
        @NotNull
        private List _fileNamePatterns = Lists.newArrayList();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy