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

io.mangoo.build.Watcher Maven / Gradle / Ivy

There is a newer version: 9.1.0-Beta4
Show newest version
/**
 * Copyright (C) 2012-2015 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 io.mangoo.build;

import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;

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.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * This is a refactored version of
 * WatchAndRestartMachine.java from the Ninja Web Framework
 *
 * Original source code can be found here:
 * https://github.com/ninjaframework/ninja/blob/develop/ninja-maven-plugin/src/main/java/ninja/build/WatchAndRestartMachine.java
 *
 * @author svenkubiak
 *
 */
@SuppressWarnings({"unchecked"})
public class Watcher implements Runnable {
    private static final Logger LOG = LogManager.getLogger(Watcher.class);
    private final Trigger trigger;
    private final Set includes;
    private final Set excludes;
    private final WatchService watchService;
    private final Map watchKeys;
    private final AtomicInteger takeCount;
    private boolean shutdown;

    @SuppressWarnings("all")
    public Watcher(Set watchDirectory, Set includes, Set excludes, Trigger trigger) throws IOException {
        this.watchService = FileSystems.getDefault().newWatchService();
        this.watchKeys = new HashMap<>();
        this.includes = includes; //NOSONAR
        this.excludes = excludes; //NOSONAR
        this.trigger = trigger;
        this.takeCount = new AtomicInteger(0);
        for (Path path: watchDirectory) {
            registerAll(path);
        }
    }

    public void doShutdown() {
        this.shutdown = true;
    }

    @SuppressWarnings("all")
    private void registerAll(final Path path) throws IOException {
        Files.walkFileTree(path, new SimpleFileVisitor() {
            @Override
            public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes attrs) throws IOException {
                register(path);
                return FileVisitResult.CONTINUE;
            }
        });
    }

    @SuppressWarnings("all")
    private void register(Path path) throws IOException {
        WatchKey watchKey = path.register(
                watchService,
                new WatchEvent.Kind[]{
                        StandardWatchEventKinds.ENTRY_CREATE,
                        StandardWatchEventKinds.ENTRY_MODIFY,
                        StandardWatchEventKinds.ENTRY_DELETE
                });

        watchKeys.put(watchKey, path);
    }

    @Override
    @SuppressWarnings("all")
    public void run() {
        for (;;) {
            WatchKey watchKey;
            try {
                watchKey = watchService.take();
                takeCount.incrementAndGet();
            } catch (InterruptedException e) {
                if (!shutdown) {
                    LOG.error("Unexpectedly interrupted while waiting for take()", e);
                }
                return;
            }

            Path path = watchKeys.get(watchKey);
            if (path == null) {
                LOG.error("WatchKey not recognized!!");
                continue;
            }

            handleEvents(watchKey, path);

            if (!watchKey.reset()) {
                watchKeys.remove(watchKey);
                if (watchKeys.isEmpty()) {
                    break;
                }
            }
        }
    }

    @SuppressWarnings("all")
    private void handleEvents(WatchKey watchKey, Path path) {
        for (WatchEvent watchEvent : watchKey.pollEvents()) {
            WatchEvent.Kind watchEventKind = watchEvent.kind();
            if (OVERFLOW.equals(watchEventKind)) {
                continue;
            }

            WatchEvent ev = (WatchEvent) watchEvent;
            Path name = ev.context();
            Path child = path.resolve(name);

            if (ENTRY_MODIFY.equals(watchEventKind) && !child.toFile().isDirectory()) {
                handleNewOrModifiedFile(child);
            }

            if (ENTRY_CREATE.equals(watchEventKind)) {
                if (!child.toFile().isDirectory()) {
                    handleNewOrModifiedFile(child);
                }
                try {
                    if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
                        registerAll(child);
                    }
                } catch (IOException e) {
                    LOG.error("Something fishy happened. Unable to register new dir for watching", e);
                }
            }
        }
    }

    @SuppressWarnings("all")
    public void handleNewOrModifiedFile(Path path) {
        String absolutePath = path.toFile().getAbsolutePath();
        
        if (isAsset(absolutePath)) {
            Minification.minify(absolutePath);
        }
        
        RuleMatch match = matchRule(includes, excludes, absolutePath);
        if (match.proceed) {
            this.trigger.trigger();
        }
    }

    private boolean isAsset(String absolutePath) {
        if (StringUtils.isBlank(absolutePath)) {
            return false;
        }

        return !absolutePath.contains("min") && ( absolutePath.endsWith("css") || absolutePath.endsWith("js") );
    }

    public enum RuleType {
        NONE,
        INCLUDE,
        EXCLUDE
    }

    public static class RuleMatch {
        private final boolean proceed;

        public RuleMatch(boolean proceed) {
            this.proceed = proceed;
        }
        
        public boolean doProceed() {
            return this.proceed;
        }
    }

    public static RuleMatch matchRule(Set includes, Set excludes, String string) {
        if (includes != null) {
            for (String include : includes) {
                if (string.matches(include)) {
                    return new RuleMatch(true);
                }
            }
        }

        if (excludes != null) {
            for (String exclude : excludes) {
                if (string.matches(exclude)) {
                    return new RuleMatch(false);
                }
            }
        }

        return new RuleMatch(true);
    }

    public static boolean checkIfWouldBeExcluded(Set patterns, String string) {
        return !matchRule(null, patterns, string).doProceed();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy