org.hotswap.agent.watch.vfs.WatcherVFS.java Source code

Java tutorial

Introduction

Here is the source code for org.hotswap.agent.watch.vfs.WatcherVFS.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.hotswap.agent.watch.vfs;

import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.vfs2.FileChangeEvent;
import org.apache.commons.vfs2.FileListener;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemException;
import org.hotswap.agent.logging.AgentLogger;
import org.hotswap.agent.watch.WatchEventListener;
import org.hotswap.agent.watch.Watcher;
import org.apache.commons.vfs2.FileSystemManager;
import org.apache.commons.vfs2.FileType;
import org.apache.commons.vfs2.VFS;
import org.apache.commons.vfs2.impl.DefaultFileMonitor;
import org.hotswap.agent.annotation.FileEvent;
import org.hotswap.agent.watch.WatchFileEvent;

/**
 *
 * @author LAMPO
 */
public class WatcherVFS implements Watcher {
    private final static AgentLogger LOGGER = AgentLogger.getLogger(WatcherVFS.class);
    private final FileSystemManager manager;
    private final DefaultFileMonitor fm;

    private final Map<URI, List<WatchEventListener>> listeners = new HashMap<URI, List<WatchEventListener>>();

    // keep track about which classloader requested which event
    protected Map<WatchEventListener, ClassLoader> classLoaderListeners = new HashMap<WatchEventListener, ClassLoader>();

    public WatcherVFS() throws FileSystemException {
        fm = new DefaultFileMonitor(new FileListener() {
            @Override
            public void fileCreated(FileChangeEvent fce) throws Exception {
                callListeners(fce, FileEvent.CREATE);
            }

            @Override
            public void fileDeleted(FileChangeEvent fce) throws Exception {
                callListeners(fce, FileEvent.DELETE);
            }

            @Override
            public void fileChanged(FileChangeEvent fce) throws Exception {
                callListeners(fce, FileEvent.MODIFY);
            }
        });

        fm.setRecursive(true);
        manager = VFS.getManager();
    }

    @Override
    public void addEventListener(ClassLoader classLoader, URI pathPrefix, WatchEventListener listener) {
        File path;
        try {
            // check that it is regular file
            // toString() is weird and solves HiarchicalUriException for URI like "file:./src/resources/file.txt".
            path = new File(pathPrefix);
        } catch (IllegalArgumentException e) {
            LOGGER.warning("Unable to watch for path {}, not a local regular file or directory.", pathPrefix);
            LOGGER.trace("Unable to watch for path {} exception", e, pathPrefix);
            return;
        }

        FileObject listendir;
        try {
            listendir = manager.toFileObject(path);
        } catch (FileSystemException ex) {
            LOGGER.error("Unable to create fileObject", ex);
            return;
        }
        fm.addFile(listendir);

        List<WatchEventListener> list = listeners.get(pathPrefix);
        if (list == null) {
            list = new ArrayList<WatchEventListener>();
            listeners.put(pathPrefix, list);
        }
        list.add(listener);

        if (classLoader != null) {
            classLoaderListeners.put(listener, classLoader);
        }
    }

    @Override
    public void addEventListener(ClassLoader classLoader, URL pathPrefix, WatchEventListener listener) {
        try {
            addEventListener(classLoader, pathPrefix.toURI(), listener);
        } catch (URISyntaxException e) {
            throw new RuntimeException("Unable to convert URL to URI " + pathPrefix, e);
        }
    }

    @Override
    public void closeClassLoader(ClassLoader classLoader) {
        for (Iterator<Map.Entry<WatchEventListener, ClassLoader>> entryIterator = classLoaderListeners.entrySet()
                .iterator(); entryIterator.hasNext();) {
            Map.Entry<WatchEventListener, ClassLoader> entry = entryIterator.next();
            if (entry.getValue().equals(classLoader)) {
                entryIterator.remove();
                for (List<WatchEventListener> transformerList : listeners.values()) {
                    transformerList.remove(entry.getKey());
                }
            }
        }

        LOGGER.debug("All watch listeners removed for classLoader {}", classLoader);
    }

    @Override
    public void run() {
        fm.start();
    }

    @Override
    public void stop() {
        fm.stop();
    }

    private void callListeners(final FileChangeEvent event, final FileEvent fileEvent) {
        URI uri;
        try {
            uri = event.getFile().getURL().toURI();
        } catch (FileSystemException ex) {
            LOGGER.error("FileSystemException at getFile.", ex);
            return;
        } catch (URISyntaxException ex) {
            LOGGER.error("URISyntaxException at getFile.", ex);
            return;
        }

        for (Map.Entry<URI, List<WatchEventListener>> list : listeners.entrySet()) {
            for (WatchEventListener listener : list.getValue()) {

                LOGGER.debug("uri: {} , list: {}", uri.toString(), list.getKey());
                if (uri.getRawPath().startsWith(list.getKey().getRawPath())) {
                    WatchFileEvent agentEvent = new HotswapWatchFileEvent(event, fileEvent);

                    try {
                        listener.onEvent(agentEvent);
                    } catch (Throwable e) {
                        LOGGER.error("Error in watch event '{}' listener '{}'", e, agentEvent, listener);
                    }
                }
            }
        }
    }

    /**
     * Filesystem event.
     */
    public static class HotswapWatchFileEvent implements WatchFileEvent {

        FileChangeEvent changeEvent;
        FileEvent fileEvent;

        public HotswapWatchFileEvent(FileChangeEvent event, FileEvent fileEvent) {
            this.changeEvent = event;
            this.fileEvent = fileEvent;
        }

        @Override
        public FileEvent getEventType() {
            return fileEvent;
        }

        @Override
        public URI getURI() {
            try {
                return changeEvent.getFile().getURL().toURI();
            } catch (FileSystemException ex) {
                LOGGER.warning("FileSystemException at getURI", ex);
            } catch (URISyntaxException ex) {
                LOGGER.warning("URISyntaxException at getURI", ex);
            }
            return null;
        }

        @Override
        public boolean isFile() {
            try {
                return changeEvent.getFile().getType() == FileType.FILE;
            } catch (FileSystemException ex) {
                LOGGER.warning("FileSystemException at isFile", ex);
            }
            return false;
        }

        @Override
        public boolean isDirectory() {
            try {
                return changeEvent.getFile().getType() == FileType.FOLDER;
            } catch (FileSystemException ex) {
                LOGGER.warning("FileSystemException at isFile", ex);
            }
            return false;
        }

    }
}