/* # Licensed Materials - Property of IBM # Copyright IBM Corp. 2015 */ package com.ibm.streamsx.topology.file; import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; import static java.nio.file.StandardWatchEventKinds.OVERFLOW; import java.io.File; import java.io.FileFilter; import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; import com.ibm.streams.operator.OperatorContext; import com.ibm.streams.operator.model.OutputPortSet; import com.ibm.streams.operator.model.Parameter; import com.ibm.streams.operator.model.PrimitiveOperator; import com.ibm.streams.operator.samples.patterns.ProcessTupleProducer; import com.ibm.streams.operator.types.RString; @PrimitiveOperator @OutputPortSet(cardinality = 1) public class DirectoryWatcher extends ProcessTupleProducer implements FileFilter { private String directory; private File dirFile; private final Set<String> seenFiles = new HashSet<>(); protected String getDirectory() { return directory; } @Override public synchronized void initialize(OperatorContext context) throws Exception { super.initialize(context); dirFile = new File(getDirectory()); if (!dirFile.isAbsolute()) dirFile = new File(getOperatorContext().getPE().getDataDirectory(), getDirectory()); } @Parameter public void setDirectory(String directory) { this.directory = directory; } protected void sortAndSubmit(List<File> files) throws Exception { if (files.size() > 1) { Collections.sort(files, new Comparator<File>() { @Override public int compare(File o1, File o2) { return Long.compare(o1.lastModified(), o2.lastModified()); } }); } for (File file : files) { if (accept(file)) { getOutput(0).submitAsTuple(new RString(file.getAbsolutePath())); seenFiles.add(file.getName()); } } } @SuppressWarnings("unchecked") @Override protected void process() throws Exception { Path dir = dirFile.toPath(); WatchService watcher = FileSystems.getDefault().newWatchService(); dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE); sortAndSubmit(Arrays.asList(dirFile.listFiles(this))); for (;!Thread.interrupted();) { WatchKey key; try { key = watcher.take(); } catch (InterruptedException e) { // shutdown has been requested return; } List<File> newFiles = new ArrayList<>(); boolean needFullScan = false; for (WatchEvent<?> watchEvent : key.pollEvents()) { if (ENTRY_CREATE == watchEvent.kind()) { Path newPath = ((WatchEvent<Path>) watchEvent).context(); File newFile = toFile(newPath); if (accept(newFile)) newFiles.add(newFile); } else if (ENTRY_DELETE == watchEvent.kind()) { Path deletedPath = ((WatchEvent<Path>) watchEvent) .context(); File deletedFile = toFile(deletedPath); seenFiles.remove(deletedFile.getName()); } else if (OVERFLOW == watchEvent.kind()) { needFullScan = true; } } key.reset(); if (needFullScan) { Collections.addAll(newFiles, dirFile.listFiles(this)); } sortAndSubmit(newFiles); } } private File toFile(Path path) { if (path.isAbsolute()) return path.toFile(); return new File(dirFile, path.getFileName().toString()); } @Override public boolean accept(File pathname) { return !seenFiles.contains(pathname.getName()); } }