Java tutorial
/* * 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; } } }