I am using Watcher service to monitor a directory in Windows. If a change is done to a file, I'd like to write a timestamp to the same file. Of course, this is yet another modification to the directory and watcher then processes the event again. Is there a way to suspend watcher so I can update the file, then restart it so it can wait for the next event?
Perhaps there's a better suggestion???
package net.codejava.io;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.sql.Timestamp;
import java.util.Date;
public class DirectoryWatchDemo {
static String logfile = "C:/Temp/log.txt";
volatile static boolean suspended = false;
public static void main(String[] args) {
try {
WatchService watcher = FileSystems.getDefault().newWatchService();
Path dir = Paths.get("C:/Temp/");
dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
// System.out.println("Watch Service registered for dir: " +
// dir.getFileName());
while (true) {
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException ex) {
return;
}
if (!suspended) {
resume();
} else {
updateFile();
suspend();
}
if (key.isValid())
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
#SuppressWarnings("unchecked")
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path fileName = ev.context();
System.out.println(kind.name() + ": " + fileName);
}
boolean valid = key.reset();
if (!valid) {
System.out.println("Watch service invalid");
break;
}
}
} catch (IOException ex) {
System.err.println(ex);
}
}
private static void suspend() {
suspended = false;
}
private static void resume() {
suspended = true;
}
public static void updateFile() {
try {
File outfile = new File(logfile);
if (!outfile.exists()) {
System.out.println("No file exists...writing a new file");
outfile.createNewFile();
}
FileWriter fw = new FileWriter(outfile.getAbsoluteFile(), true);
BufferedWriter bw = new BufferedWriter(fw);
bw.write("TimeStamp: " + new Timestamp(new java.util.Date().getTime()).toString() + "\r\n");
bw.flush();
bw.close();
System.out.println("done");
} catch (IOException e) {
e.printStackTrace();
}
}
}
You need to interrupt the thread that is using the watch service, because that's the only way you get out of watcher.take(). Example with old-school threads:
public void start() {
thread = new Thread(directoryWatchDemo::watch);
thread.start();
}
public void pause() throws InterruptedException {
if (thread.isAlive()) {
thread.interrupt();
thread.join();
}
}
See ExecutorService like Executors.newSingleThreadExecutor() and Future.cancel() for more flexibility.
Alternatively, you can take() all events and ignore them based on a volatile boolean.
Related
Please help .
When you first start the server, create a thread pool in advance (5 threads) and create 5 selectors to read, pass one selector to each Runnable, and immediately send the submit function to the thread pool.
Then, create a selector that accepts the user and run it on the main thread.
The problem here is that the Selector in the main thread detects the user's request and registers the returned SocketChannel in one of the 5 Read Selectors.
And when the connection is successful, the user sends text data every second, but there is no response from the Read Selector.
I don't know why this problem occurs.. ㅜㅜ
Main.java
public class Main {
public static void main(String[] args) {
Server server = new Quarter();
}
}
Server.java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AlreadyBoundException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public abstract class Server {
protected volatile boolean isStart = true;
// 192.168.0.11 company
// 192.168.75.35 house
// 192.168.75.97 my notebook
private static final String ADDRESS = "192.168.75.35";
private static final int PORT = 1111;
private static final int BACKLOG = 1024;
protected Selector acceptSelector = null;
/** 하나의 스레드에서만 접근 할것이기 때문에 동기화 처리는 필요 없음. */
protected List<Selector> readSelectors = new ArrayList<Selector>();
protected ServerSocketChannel serverSocketChannel = null;
protected InetSocketAddress inetSocketAddress = null;
private final int nThreads = 5;
protected ExecutorService threadpool = null;
/** 현재 연결된 클라이언트 수 */
protected static int connectionCount = 0;
// volatile 캐시 메모리 문제는 아닌가?
// 뭐가 문제지?
protected void init() {
try {
this.threadpool = Executors.newFixedThreadPool(this.nThreads);
for (int i = 1; i <= this.nThreads; i++) {
Selector sel = Selector.open();
this.readSelectors.add(sel);
threadpool.submit(new SubSelector(sel));
}
this.acceptSelector = Selector.open();
this.inetSocketAddress = new InetSocketAddress(ADDRESS, PORT);
this.serverSocketChannel = ServerSocketChannel.open();
// non - blocking 으로 설정.
this.serverSocketChannel.configureBlocking(false);
System.out.println("serverSocketChannel is non-blocking.");
this.serverSocketChannel.bind(this.inetSocketAddress, BACKLOG);
System.out.println("binding is success.");
this.serverSocketChannel.register(this.acceptSelector, SelectionKey.OP_ACCEPT);
System.out.println("initing is success.");
} catch (AlreadyBoundException abe) {
// 이미 사용중인 포트에 바인딩하려면 에러
System.err.println(abe.getMessage());
this.exit();
} catch (IOException e) {
System.err.println(e.getMessage());
this.exit();
}
}
protected synchronized void exit() {
try {
this.isStart = false;
this.serverSocketChannel.close();
this.acceptSelector.close();
this.threadpool.shutdown();
for (int i = 0; i < this.readSelectors.size(); i++) {
this.readSelectors.get(i).close();
}
} catch (IOException e) {
System.err.print(e.getMessage());
}
}
protected synchronized void connectCountAdd() {
connectionCount++;
}
protected synchronized void connectCountMinus() {
connectionCount--;
}
}
Quarter.java
import java.io.IOException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class Quarter extends Server {
public Quarter() {
init();
this.initQuarter();
}
private void initQuarter() {
while (isStart) {
try {
int ready = acceptSelector.select();
if (ready <= 0) continue;
Set<SelectionKey> set = acceptSelector.selectedKeys();
Iterator<SelectionKey> selected = set.iterator();
while (selected.hasNext()) {
SelectionKey key = selected.next();
selected.remove();
if (!key.isValid()) continue;
if (key.isAcceptable()) this.accept(key);
}
}
catch (ClosedSelectorException cse) {
System.err.println(cse.getMessage());
}
catch (IOException e) {
System.err.println(e.getMessage());
}
}
}
private void accept(SelectionKey key) {
try {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverChannel.accept();
if (channel == null) return;
channel.configureBlocking(false);
int targetIndex = connectionCount % readSelectors.size();
Selector tarSel = readSelectors.get(targetIndex);
channel.register(tarSel, SelectionKey.OP_READ);
connectCountAdd();
System.out.println("Connected to: " + channel.getRemoteAddress());
System.out.println("Current Connection Count: " + connectionCount);
}
catch (ClosedChannelException cce) {
// 채널이 닫힌 경우
System.err.println(cce.getMessage());
key.cancel();
}
catch (ClosedSelectorException cse) {
// 셀렉터가 닫힌 경우
System.err.println(cse.getMessage());
key.cancel();
exit();
}
catch (IOException e) {
System.err.println(e.getMessage());
key.cancel();
exit();
}
}
}
SubSelector.java
import java.io.IOException;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;
public class SubSelector extends Server implements Runnable {
private Selector selector = null;
Charset charset = Charset.forName("UTF-8");
public SubSelector(Selector selector) {
this.selector = selector;
}
#Override
public void run() {
while (isStart) {
try {
int ready = selector.select();
if (ready <= 0) return;
Set<SelectionKey> set = this.selector.selectedKeys();
Iterator<SelectionKey> selected = set.iterator();
while (selected.hasNext()) {
SelectionKey key = selected.next();
selected.remove();
if (!key.isValid()) continue;
if (key.isReadable()) this.read(key);
}
}
catch (ClosedSelectorException cse) {
System.err.println(cse.getMessage());
}
catch (IOException e) {
System.err.println(e.getMessage());
}
}
}
private void read(SelectionKey key) {
try {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(100);
int readCount = channel.read(buffer);
if (readCount <= 0) return;
buffer.flip();
String result = charset.decode(buffer).toString();
System.out.println("client ip address: " + channel.getRemoteAddress());
System.out.println("client send message: " + result);
}
catch (NotYetConnectedException nyce) {
// 채널이 아직 연결되지 않은 경우
System.err.println(nyce.getMessage());
}
catch (SocketException se) {
System.err.println(se.getMessage());
this.disConnect(key);
}
catch (IOException e) {
// 기타 에러
System.err.println(e.getMessage());
}
}
private void disConnect(SelectionKey key) {
key.cancel();
selector.wakeup();
connectCountMinus();
System.out.println("Current Connection Count: " + connectionCount);
}
}
Client.java
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Scanner;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception {
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress("192.168.75.35", 1111));
System.out.println("is Connected: " + socket.isConnected());
while (true) {
OutputStream os = socket.getOutputStream();
os.write(Thread.currentThread().getName().getBytes());
os.flush();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Sorry, this question has probably been asked before, but I couldn't find any with an answer in the context that applies specifically enough to my problem for me to apply the solution.
Anyways, I'm working on a program that uses a file. When that file is updated, I want it to replace the File variable with the current one. I set up a main class that will work with the file, then I set up another class with a different thread that listens for the file update. When the file is updated, I want the variable in the main class to be updated.
That means that the update listener class has to have the instance of the main class, but when I try to send it during initiation of the update listener class, a warning says the main class cannot be referenced from a static context.
Here's the code:
Main Class
package me.xeyler;
import com.sun.media.jfxmedia.logging.Logger;
import javax.swing.*;
import java.awt.Color;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.*;
/**
* Created by Brigham on 10/19/2016.
*/
public class ViewerMain {
static FileHandler fileHandler;
static File skinFile;
public static void main(String[] args) {
boolean bool = false;
fileHandler = new FileHandler(this);
fileHandler.start();
while(true) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(bool);
}
}
public void setSkinFile(File skinFile) {
this.skinFile = skinFile;
}
}
File Listener Class
package me.xeyler;
import com.sun.media.jfxmedia.logging.Logger;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
/**
* Created by Brigham on 10/19/2016.
*/
public class FileHandler implements Runnable {
private Thread fileThread;
private String threadName;
WatchService watcher = null;
private ViewerMain main;
public FileHandler(ViewerMain main) {
this.main = main;
this.threadName = "FileThread";
}
public void watchFile(Path path) {
}
public void watchFile(File file) {
watchFile(Paths.get(file.getPath()));
}
public void close() {
try {
watcher.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void start () {
if (fileThread == null) {
System.out.println("Starting new thread...");
fileThread = new Thread (this, threadName);
fileThread.start();
System.out.println("Started thread: " + threadName);
}
}
#Override
public void run() {
System.out.println("Running thread...");
Path dir = Paths.get(System.getProperty("user.home"),"documents");
try {
watcher = FileSystems.getDefault().newWatchService();
WatchKey key = dir.register(watcher,
ENTRY_MODIFY);
} catch (IOException x) {
x.printStackTrace();
}
for (;;) {
// wait for key to be signaled
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException x) {
return;
}
for (WatchEvent<?> event: key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
// The filename is the
// context of the event.
WatchEvent<Path> ev = (WatchEvent<Path>)event;
Path filename = ev.context();
if (filename.endsWith("text.txt")) {
System.out.println("File has changed");
//TODO: Update File variable in ViewerMain
main.setSkinFile(filename.toFile());
}
}
// Reset the key -- this step is critical if you want to
// receive further watch events. If the key is no longer valid,
// the directory is inaccessible so exit the loop.
boolean valid = key.reset();
if (!valid) {
// TODO: Handle inaccessible directory
break;
}
}
}
}
I suspect the answer is really obvious, but thanks for the patience!
If I understand correctly, you need an instance of the ViewerMain class.
this cannot be applied in a static context.
public static void main(String[] args) {
ViewerMain viewer = new ViewerMain(); // an instance
fileHandler = new FileHandler(viewer);
Same for skinFile
public File skinFile; // Remove static
public void setSkinFile(File skinFile) {
this.skinFile = skinFile;
}
You can not do this:
public void setSkinFile(File skinFile) {
this.skinFile = skinFile;
}
since skinFile is static, it would be better if you set that property as public static File skinFile; and then you accesed the property directly from the FileHandler:
ViewerMain.skinFile = filename.toFile()
given that it is a static property you dont need an instance of the class to access it, you can use the class directly.
This question regards com.jayway.awaitility.Awaitility.
I just tried Awaitility.await() and it seems to have some odd behavior.
In the test method below if I comment out testWithFuture() and enable
testWithAwaitility(), I never see the message "end " printed out.
I see 'start ', then the program just exits, and the second
print statement never seems to be reached.
So as a work around I decided to use Settable{Future}.. If anyone else has the same issue then maybe the work-around I provide will be useful.. Even better would be to get a nice answer ;^) ! thanks in advance / chris
THE CODE:
import com.google.common.util.concurrent.SettableFuture;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static com.jayway.awaitility.Awaitility.await;
import static java.util.concurrent.TimeUnit.SECONDS;
public class AwaitTest {
static volatile boolean done = false;
public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
testWithFuture();
//testWithAwaitility();
}
private static void testWithAwaitility() {
System.out.println("start " + new Date());
new Thread(new Runnable(){
public void run(){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
done = true;
}
}).start();
await().atMost(2, SECONDS).until(new Callable() {
#Override
public Boolean call() throws Exception {
return done;
}
});
System.out.println("end " + new Date()); // NEVER Reached. i wonder why?
}
// This does what I want.
//
private static void testWithFuture() throws InterruptedException, ExecutionException, TimeoutException {
System.out.println("start testWithFuture");
final SettableFuture future = SettableFuture. create();
new Thread(new Runnable(){
public void run(){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
future.set("Hello");
}
}).start();
String result = future.get(4, TimeUnit.SECONDS);
if (! result.equals("Hello")) {
throw new RuntimeException("not equal");
} else {
System.out.println("got Hello");
}
}
}
CORRECTED CODE ->
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static com.jayway.awaitility.Awaitility.await;
import static java.util.concurrent.TimeUnit.SECONDS;
public class Sample {
static volatile boolean done = false;
public static void main(String[] args) {
testWithAwaitility();
}
private static void testWithAwaitility() {
System.out.println("start " + new Date());
new Thread(new Runnable(){
public void run(){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
done = true;
}
}).start();
try {
await().atMost(2, SECONDS).until(new Callable() {
#Override
public Boolean call() throws Exception {
return done;
}
});
} catch (Exception e) {
System.out.println("FAILED");
e.printStackTrace();
}
System.out.println("end " + new Date()); // REACHED this statement after correction
}
}
According to the documentation, await() throws a TimeoutException if the timeout is reached and the condition is not true, so your method ends at this point because the exception is propagated up through the stack. This explains the behavior. You should see a stacktrace, however.
If you want to continue executing code afterwards, it seems you would need to catch this exception.
I have tried nearly everything to get this work, but it seems that I don't get the right direction.
Here is the actual Situation: I use JSF2.2 with GlashFish7 under Netbeans7.3. My JSF application should create an second thread to run(Asynchronously) an endless loop. In this endless loop I use the WatchService (NIO) to check a specific folder for changes.
The WatchService function works fine in a single thread driven jsf page. But I will do other stuff and during the loop, so I need this methode async, but i'm not able to run it in a seperad thread.
Here is my java class:
#Stateless
public class NewFile {
#Asynchronous
public void showNewFile() throws IOException{
WatchService watchService = FileSystems.getDefault().newWatchService();
WatchKey watchKey = Paths.get("/home/user/input").register(watchService,new WatchEvent.Kind<?>[] { ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE });
while (true) {
try {
watchKey = watchService.take();
} catch (InterruptedException ex) {
System.out.println("InterruptedException: " + ex);
}
for (WatchEvent<?> watchEvent : watchKey.pollEvents()) {
System.out.println(watchEvent.kind() + " " + ((Path) watchEvent.context()));
}
watchKey.reset();
}
}
}
In this class I call the methode:
#Named("startWatcher")
public class StartWatcher {
private NewFile newFile;
public void runSearcher() throws IOException{
newFile.showNewFile();
}
}
and the relevant section from thy index.xhtml
<h:commandButton actionListener="#{startWatcher.runSearcher()}" value="test"/>
I hope you understand my problem, I know my english isn't very good. i'm looking forward to receive a hint what i'm doing wrong.
Typically, you should start a watchservice thread when your GlassFish domain is deployed by defining a #WebListener, for example:
import static java.nio.file.StandardWatchEventKinds.*;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
#WebListener
public class MyServletContextListener implements ServletContextListener {
private final NewFileRunner runner = new NewFileRunner();
#Override
public void contextInitialized(ServletContextEvent sce) {
runner.startThread();
}
#Override
public void contextDestroyed(ServletContextEvent arg0) {
runner.stopThread();
}
class NewFileRunner implements Runnable {
private volatile Thread thread;
private final WatchService watchService;
public NewFileRunner() {
watchService = FileSystems.getDefault().newWatchService();
Paths.get("/home/user/input").register(watchService, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
}
/**
* Start a worker thread to listen for directory changes.
*/
public void startThread() {
thread = new Thread(this);
thread.start();
}
/**
* Flag worker thread to stop gracefully.
*/
public void stopThread() {
if (thread != null) {
Thread runningThread = thread;
thread = null;
runningThread.interrupt();
}
}
#Override
public void run() {
Thread runningThread = Thread.currentThread();
while (runningThread == thread) {
WatchKey watchKey = null;
try {
watchKey = watchService.take();
if (watchKey != null) {
for (WatchEvent<?> watchEvent : watchKey.pollEvents()) {
System.out.println(watchEvent.kind() + " " + ((Path) watchEvent.context()));
}
watchKey.reset();
}
} catch (InterruptedException e) {
e = null;
}
}
}
}
}
Of course, I'd refactor this out into multiple classes, but this will point you in the right direction.
I am using xjc to generate classes from xsd. The generation has to happen inside the java code. Right now I have done it like this:
Process child = Runtime.getRuntime().exec(command);
try {
System.out.println("waiting...");
child.waitFor();
System.out.println("waiting ended..");
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
The output for the above program is:
waiting...
I have to use the classes after they are generated. The problem here is that the subprocess never exits and the control is never back to the java program!
Is there a way to do this without getRuntime().exec() ?
You can actually use the driver class (com.sun.tools.xjc.Driver) behind the command line tool. This worked for me:
import com.sun.tools.xjc.BadCommandLineException;
import com.sun.tools.xjc.Driver;
import com.sun.tools.xjc.XJCListener;
import org.xml.sax.SAXParseException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Generator {
public static void main(String[] args) throws BadCommandLineException, IOException {
final String targetDir = "jaxb-files";
Path path = Paths.get(targetDir);
if(!Files.exists(path)) {
Files.createDirectories(path);
}
Driver.run(new String[]{"-d", targetDir,
"D:\\dev\\onepoint\\tui\\java\\xsdjsonschema\\src\\main\\xsd\\test.xsd"}, new XJCListener() {
#Override
public void error(SAXParseException e) {
printError(e, "ERROR");
}
#Override
public void fatalError(SAXParseException e) {
printError(e, "FATAL");
}
#Override
public void warning(SAXParseException e) {
printError(e, "WARN");
}
#Override
public void info(SAXParseException e) {
printError(e, "INFO");
}
private void printError(SAXParseException e, String level) {
System.err.printf("%s: SAX Parse exception", level);
e.printStackTrace();
}
});
}
}
try this
Process child = Runtime.getRuntime().exec(command);
BufferedReader in = new BufferedReader(
new InputStreamReader(child.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}