driver733/VK-Uploader

View on GitHub
src/main/java/com/driver733/vkuploader/post/EntranceDirectory.java

Summary

Maintainability
A
0 mins
Test Coverage
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2018 Mikhail Yakushin
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.driver733.vkuploader.post;

// @checkstyle AvoidStaticImportCheck (30 lines)

import com.driver733.vkuploader.post.posts.Posts;
import com.jcabi.aspects.Immutable;
import com.jcabi.log.Logger;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.cactoos.list.StickyList;

/**
 * Tracks the specified folder for changes
 *  and acts upon them.
 *
 * @since 0.1
 */
@Immutable
public final class EntranceDirectory implements Entrance {

    /**
     * Directories to watch to changes.
     */
    private final List<Path> dirs;

    /**
     * The {@link Posts} that handle the
     *  posting of files in watched directories.
     */
    private final Posts posts;

    /**
     * A {@link WatchService}.
     */
    private final WatchService watcher;

    /**
     * A {@link Map} that stores the keys with directories` paths.
     */
    private final Map<WatchKey, Path> keys;

    /**
     * Ctor.
     * @param posts The {@link Posts} that handle the
     *  posting of files in watched directories.
     * @param dirs Directories to watch for changes.
     * @throws IOException If a {@link WatchService} cannot be created.
     */
    public EntranceDirectory(
        final Posts posts,
        final Path... dirs
    ) throws IOException {
        this.posts = posts;
        this.dirs = new StickyList<>(
            dirs
        );
        this.keys = new HashMap<>();
        this.watcher = FileSystems.getDefault().newWatchService();
    }

    /**
     * Starts watching the directories for changes.
     * @throws Exception If a directory cannot be registered.
     */
    public void start() throws Exception {
        for (final Path dir : this.dirs) {
            this.posts
                .postFromDir(dir)
                .post();
            this.registerDirectory(
                dir
            );
        }
        this.processEvents();
    }

    /**
     * Process all events for the keys that have new events.
     * @throws Exception If an error occurs while processing
     */
    private void processEvents() throws Exception {
        while (true) {
            final WatchKey key;
            key = this.watcher.take();
            final Path dir = this.keys.get(key);
            this.processSubevents(key, dir);
            this.posts
                .postFromDir(
                    dir
                ).post();
        }
    }

    /**
     * Processes subevents of the key.
     * @param key That has new events.
     * @param dir For the key.
     * @throws IOException If a subdirectory cannot be registered.
     */
    private void processSubevents(final WatchKey key, final Path dir)
        throws IOException {
        for (final WatchEvent event : key.pollEvents()) {
            final WatchEvent.Kind kind = event.kind();
            final Path name = (Path) event.context();
            final Path child = dir.resolve(name);
            Logger.debug(
                this,
                "%s: %s%n",
                event.kind()
                    .name(),
                child
            );
            if (
                kind == StandardWatchEventKinds.ENTRY_CREATE
                    && Files.isDirectory(child)
                ) {
                this.processSubevents(child);
            }
        }
    }

    /**
     * Starts watching the directory for changes.
     * @param dir EntranceDirectory to watch.
     * @throws IOException If the directory cannot be registered.
     */
    private void registerDirectory(final Path dir) throws IOException {
        final WatchKey key = dir.register(
            this.watcher,
            StandardWatchEventKinds.ENTRY_CREATE,
            StandardWatchEventKinds.ENTRY_DELETE,
            StandardWatchEventKinds.ENTRY_MODIFY
        );
        this.keys.put(key, dir);
    }

    /**
     * Starts watching the subdirectories of the provided directory for changes.
     * @param root Root directory of the subdirectories to register.
     * @throws IOException If a subdirectory cannot be registered.
     * @checkstyle RequireThisCheck (20 lines)
     * @checkstyle NonStaticMethodCheck (20 lines)
     */
    private void processSubevents(final Path root) throws IOException {
        Files.walkFileTree(
            root,
            new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult preVisitDirectory(
                    final Path subdir,
                    final BasicFileAttributes attrs
                ) throws IOException {
                    registerDirectory(subdir);
                    return FileVisitResult.CONTINUE;
                }
            });
    }

}