The following document contains the results of PMD's CPD 4.3.
| File | Project | Line |
|---|---|---|
| com\github\sworisbreathing\sfmf4j\jpathwatch\WatchServiceFileMonitorServiceImpl.java | SFMF4J :: JPathWatch | 71 |
| com\github\sworisbreathing\sfmf4j\nio2\WatchServiceFileMonitorServiceImpl.java | SFMF4J :: NIO2 | 71 |
private static final Kind[] interested_types = new Kind[]{StandardWatchEventKind.ENTRY_CREATE, StandardWatchEventKind.ENTRY_DELETE, StandardWatchEventKind.ENTRY_MODIFY};
/**
* The executor service which runs the task of polling the watch service.
*/
private ExecutorService executorService;
/**
* Flag to indicate that the watch service should be stopped during
* shutdown.
*/
private volatile boolean closeWatchServiceOnShutdown = false;
/**
* Flag to indiate that the executor service should be stopped during
* shutdown.
*/
private volatile boolean shutdownExecutorServiceOnShutdown = false;
/**
* Package-protected getter (for automated testing).
*
* @return
*/
ExecutorService getExecutorService() {
return executorService;
}
/**
* Package-protected getter (for automated testing).
*
* @return
*/
WatchService getWatchService() {
return watchService;
}
/**
* The registered watch keys for each path.
*/
private final ConcurrentMap<String, WatchKey> watchKeysByPath;
/**
* The registered paths for each watch key.
*/
private final ConcurrentMap<WatchKey, String> pathsByWatchKey;
/**
* The listeners for each watch key.
*/
private final ConcurrentMap<WatchKey, Collection<SFMF4JWatchListener>> listenersByWatchKey;
/**
* Creates a new WatchServiceFileMonitorServiceImpl.
* @param watchService the watch service to use. When this argument is
* {@code null}, the watch service will be created during {@link
* #initialize()} and stopped during {@link #shutdown()}. If this argument
* is not {@code null}, then it is assumed that the watch service is running
* and will be closed elsewhere (for example, an IoC container)
* @param executorService the executor service to use. When this argument
* is {@code null}, the executor service will be created during {@link
* #initialize()} and stopped during {@link #shutdown()}. If this argument
* is not {@code null}, then it is assumed that the executor service is
* running and will be closed elsewhere (for example, in an IoC container)
*/
public WatchServiceFileMonitorServiceImpl(final WatchService watchService, final ExecutorService executorService) {
this.watchService = watchService;
this.executorService = executorService;
this.watchKeysByPath = new ConcurrentHashMap<String, WatchKey>();
this.pathsByWatchKey = new ConcurrentHashMap<WatchKey, String>();
this.listenersByWatchKey = new ConcurrentHashMap<WatchKey, Collection<SFMF4JWatchListener>>();
}
/**
* Gets the watch key for a path with lazy initialization.
* @param path the path
* @return a registered watch key for the path
* @throws IOException if lazy initialization fails
*/
private synchronized WatchKey getWatchKeyForPath(final String path) throws IOException {
WatchKey key = watchKeysByPath.get(path);
if (key == null) {
logger.debug("Lazy-instantiating watch key for path: {}", path);
key = Paths.get(path).register(watchService, interested_types);
watchKeysByPath.put(path, key);
pathsByWatchKey.put(key, path);
listenersByWatchKey.put(key, Collections.newSetFromMap(new ConcurrentHashMap<SFMF4JWatchListener, Boolean>()));
}
return key;
}
@Override
public void registerDirectoryListener(File directory, DirectoryListener directoryListener) {
String path = directory.getAbsolutePath();
try {
synchronized (this) {
WatchKey key = getWatchKeyForPath(path);
SFMF4JWatchListener listener = new SFMF4JWatchListener(directoryListener);
listenersByWatchKey.get(key).add(listener);
}
} catch (IOException ex) {
logger.error(ex.getMessage(), ex);
}
}
@Override
public void unregisterDirectoryListener(File directory, DirectoryListener directoryListener) {
String path = directory.getAbsolutePath();
synchronized (this) {
final WatchKey key = watchKeysByPath.get(path);
if (key != null) {
Collection<SFMF4JWatchListener> listeners = listenersByWatchKey.get(key);
listeners.remove(new SFMF4JWatchListener(directoryListener)); // note the equals implementation
if (listeners.isEmpty()) {
//no longer listening on that path.
cleanup(key);
} else {
logger.debug("somebody is still listening: {}", path);
}
}
}
}
/**
* Resolves a watch event with its absolute path.
* @param key the watch key (used to look up the parent path)
* @param event the event to resolve
* @return a copy of the event, with a resolved path
*/
private synchronized WatchEvent<Path> resolveEventWithCorrectPath(final WatchKey key, final WatchEvent<Path> event) {
Path correctPath = Paths.get(pathsByWatchKey.get(key));
return new ResolvedPathWatchEvent(event, correctPath);
}
/**
* Properly unregisters and removes a watch key.
* @param key the watch key
*/
@SuppressWarnings("PMD.EmptyCatchBlock")
private synchronized void cleanup(final WatchKey key) {
logger.trace("cleanUp {}", key);
try {
key.cancel();
}catch(Exception ex) {
//trap
}
Collection<SFMF4JWatchListener> listeners = listenersByWatchKey.remove(key);
if (listeners != null && !listeners.isEmpty()) {
logger.warn("Cleaning up key but listeners are still registered.");
}
String path = pathsByWatchKey.remove(key);
if (path != null) {
watchKeysByPath.remove(path);
}
}
@Override
public synchronized void initialize() {
if (watchService==null) {
logger.warn("No watch service was explicitly set. Setting one now.");
closeWatchServiceOnShutdown = true; | ||
| File | Project | Line |
|---|---|---|
| com\github\sworisbreathing\sfmf4j\jpathwatch\WatchServiceFileMonitorServiceImpl.java | SFMF4J :: JPathWatch | 242 |
| com\github\sworisbreathing\sfmf4j\nio2\WatchServiceFileMonitorServiceImpl.java | SFMF4J :: NIO2 | 246 |
return new Thread(r, "jpathwatchFileMonitorService");
}
});
}
if (watchFuture==null || watchFuture.isCancelled() || watchFuture.isDone()) {
watchFuture = executorService.submit(new Runnable() {
@Override
public void run() {
while (true) {
try {
Collection<SFMF4JWatchListener> listeners;
List<WatchEvent<?>> events;
final WatchKey key = watchService.take();
synchronized (WatchServiceFileMonitorServiceImpl.this) {
listeners = listenersByWatchKey.get(key);
if (listeners != null && !listeners.isEmpty()) {
listeners = new LinkedList<SFMF4JWatchListener>(listeners);
events = key.pollEvents();
boolean stillValid = key.reset();
if (!stillValid) {
logger.warn("Key no longer valid.");
cleanup(key);
} else {
logger.debug("Key is still valid.");
if (events != null && !events.isEmpty()) {
for (WatchEvent event : events) {
WatchEvent<Path> resolvedEvent = resolveEventWithCorrectPath(key, event);
logger.debug("Event kind={} count={} path={}", new Object[]{resolvedEvent.kind().name(), resolvedEvent.count(), resolvedEvent.context()});
for (SFMF4JWatchListener listener : listeners) {
listener.onEvent(resolvedEvent);
}
}
}
}
} else {
logger.debug("No listeners found for valid key... cleaning up.");
cleanup(key);
}
}
} catch (InterruptedException ex) {
return;
} catch (ClosedWatchServiceException ex) {
return;
}
}
}
});
}
}
@Override
@SuppressWarnings("PMD.EmptyCatchBlock")
public synchronized void shutdown() {
watchFuture.cancel(true);
watchFuture = null;
for (WatchKey key : pathsByWatchKey.keySet()) {
cleanup(key);
}
if (shutdownExecutorServiceOnShutdown) {
executorService.shutdownNow();
executorService = null;
}
if (closeWatchServiceOnShutdown) {
try {
watchService.close();
}catch(IOException ex) {
//trap
}catch(ClosedWatchServiceException ex) {
//trap
}finally {
watchService = null;
}
}
}
@Override
public synchronized boolean isMonitoringDirectory(File directory) {
ExecutorService es = getExecutorService();
return es != null && !es.isShutdown() && watchKeysByPath.containsKey(directory.getAbsolutePath());
}
} | ||
| File | Project | Line |
|---|---|---|
| com\github\sworisbreathing\sfmf4j\jpathwatch\ResolvedPathWatchEvent.java | SFMF4J :: JPathWatch | 26 |
| com\github\sworisbreathing\sfmf4j\nio2\ResolvedPathWatchEvent.java | SFMF4J :: NIO2 | 26 |
public class ResolvedPathWatchEvent extends WatchEvent<Path> {
/**
* The path.
*/
private final Path context;
/**
* The event count.
*/
private final int count;
/**
* The kind.
*/
private final Kind<Path> kind;
/**
* Copy constructor for a watch event, which ensures the path is fully
* resolved.
* @param source the event to copy
* @param parentPath the parent path
* @see Path#resolve(Path)
*/
public ResolvedPathWatchEvent(final WatchEvent<Path> source, final Path parentPath) {
if (source==null) {
throw new IllegalArgumentException("source is null");
} else if (parentPath==null) {
throw new IllegalArgumentException("parentPath is null");
} else {
this.context = parentPath.resolve(source.context());
this.count = source.count();
this.kind = source.kind();
}
}
@Override
public Path context() {
return context;
}
@Override
public int count() {
return count;
}
@Override
public Kind<Path> kind() {
return kind;
}
@Override
public int hashCode() {
int hash = 7;
hash = 53 * hash + (this.context != null ? this.context.hashCode() : 0);
hash = 53 * hash + this.count;
hash = 53 * hash + (this.kind != null ? this.kind.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ResolvedPathWatchEvent other = (ResolvedPathWatchEvent) obj;
if (this.context != other.context && (this.context == null || !this.context.equals(other.context))) {
return false;
}
if (this.count != other.count) {
return false;
}
if (this.kind != other.kind && (this.kind == null || !this.kind.equals(other.kind))) {
return false;
}
return true;
}
} | ||