Watchservice not being triggered [duplicate] - java

This code works fine in Linux but not in Windows 7: to get file contents update I have to click on the output file. Where is the trick?
I am using Windows 7 prof, NetBeans IDE 8.0 RC1 (Build 201402242200) updated to version NetBeans 8.0 Patch 1.1, JDK 1.8
package watchfilethreadmod;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.file.StandardWatchEventKinds.*;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class WatchFileThreadMod {
static class WatchFile {
String fileName;
long lastFilePos;
RandomAccessFile file;
public WatchFile(String _fileName, RandomAccessFile _file) {
fileName = _fileName;
lastFilePos = 0;
file = _file;
}
}
public static void shutDownListener(Thread thread) {
Thread thr = thread;
if (thr != null) {
thr.interrupt();
}
}
private static class MyWatchQueueReader implements Runnable {
/**
* the watchService that is passed in from above
*/
private WatchService myWatcher;
public ArrayList<WatchFile> threadFileToWatch;
public String dirPath;
public MyWatchQueueReader(String _dirPath, WatchService myWatcher, ArrayList<WatchFile> _threadFileToWatch) {
this.myWatcher = myWatcher;
this.threadFileToWatch = _threadFileToWatch;
this.dirPath = _dirPath;
}
private void openFile(WatchFile obj) {
try {
System.out.println("Open file "+obj.fileName);
obj.file = new RandomAccessFile(dirPath + "/" + obj.fileName, "r");
} catch (FileNotFoundException e) {
obj.file = null;
System.out.println("filename " + obj.fileName + " non trovato");
}
obj.lastFilePos = 0;
}
private void process(WatchEvent evt) {
String thisLine;
ArrayList<WatchFile> auxList = threadFileToWatch;
for (WatchFile obj : auxList) {
if (obj.fileName.equals(evt.context().toString())) {
if (obj.file == null) {
openFile(obj);
}
try {
obj.file.seek(obj.lastFilePos);
} catch (IOException e) {
System.err.println("Seek error: " + e);
}
try {
thisLine = obj.file.readLine();
if ((thisLine == null)&&(evt.kind() == ENTRY_MODIFY)) {
System.out.printf("---> thisLine == null received %s event for file: %s\n",
evt.kind(), evt.context());
obj.file.close();
System.out.println("Close file "+obj.fileName);
openFile(obj);
thisLine = obj.file.readLine();
}
while (thisLine != null) { // while loop begins here
if (thisLine.length() > 0) {
if (thisLine.substring(thisLine.length() - 1).equals("*")) {
obj.lastFilePos = obj.file.getFilePointer();
System.out.println(obj.fileName + ": " + thisLine);
}
}
thisLine = obj.file.readLine();
} // end while
} // end try
catch (IOException e) {
System.err.println("Error: " + e);
}
}
}
}
/**
* In order to implement a file watcher, we loop forever ensuring
* requesting to take the next item from the file watchers queue.
*/
#Override
public void run() {
try {
// get the first event before looping
WatchKey key = myWatcher.take();
while (key != null) {
// we have a polled event, now we traverse it and
// receive all the states from it
for (WatchEvent event : key.pollEvents()) {
WatchEvent.Kind eventType = event.kind();
if (eventType == OVERFLOW) {
continue;
}
process(event);
}
key.reset();
key = myWatcher.take();
}
} catch (InterruptedException e) {
ArrayList<WatchFile> auxList = threadFileToWatch;
for (WatchFile obj : auxList) {
if (obj.file != null) {
try {
obj.file.close();
System.out.println("chiusura file " + obj.fileName);
} catch (IOException ex) {
System.out.println("errore in chiusura file");
Logger.getLogger(WatchFileThreadMod.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
//e.printStackTrace();
}
System.out.println("Stopping thread");
}
}
public static void main(String[] args) throws Exception {
// get the directory we want to watch, using the Paths singleton class
//Path toWatch = Paths.get(DIRECTORY_TO_WATCH);
ArrayList<WatchFile> fileToWatch = new ArrayList<>();
String filename;
RandomAccessFile file;
fileToWatch.add(new WatchFile("EURUSD.rlt", new RandomAccessFile(args[0] + "/EURUSD.rlt", "r")));
filename = "EURCHF2.rlt";
try {
file = new RandomAccessFile(args[0] + "/" + filename, "r");
} catch (FileNotFoundException e) {
file = null;
System.out.println("filename " + filename + " non trovato");
}
fileToWatch.add(new WatchFile(filename, file));
fileToWatch = fileToWatch;
Path toWatch = Paths.get(args[0]);
if (toWatch == null) {
throw new UnsupportedOperationException("Directory not found");
}
// Sanity check - Check if path is a folder
try {
Boolean isFolder = (Boolean) Files.getAttribute(toWatch,
"basic:isDirectory", NOFOLLOW_LINKS);
if (!isFolder) {
throw new IllegalArgumentException("Path: " + toWatch + " is not a folder");
}
} catch (IOException ioe) {
// Folder does not exists
ioe.printStackTrace();
}
// make a new watch service that we can register interest in
// directories and files with.
WatchService myWatcher = toWatch.getFileSystem().newWatchService();
// start the file watcher thread below
MyWatchQueueReader fileWatcher = new MyWatchQueueReader(args[0], myWatcher, fileToWatch);
Thread processingThread = new Thread(fileWatcher, "FileWatcher");
processingThread.start();
toWatch.register(myWatcher, ENTRY_CREATE, ENTRY_MODIFY);
}
}
Edit: reduced code as requested.
Edit 2: file path
Edit 3: Metatrader code I am using to write data
#property strict
int file_handle;
string InpFileName = _Symbol + ".rlt"; // File name
input string InpDirectoryName = "Data"; // Folder name
int OnInit()
{
ResetLastError();
file_handle = FileOpen(InpDirectoryName + "//" + InpFileName, FILE_SHARE_READ|FILE_WRITE|FILE_TXT|FILE_ANSI);
if(file_handle == INVALID_HANDLE) {
PrintFormat("Failed to open %s file, Error code = %d", InpFileName, GetLastError());
ExpertRemove();
}
return INIT_SUCCEEDED;
}
void OnTick()
{
// file_handle = FileOpen(InpDirectoryName + "//" + InpFileName, FILE_SHARE_READ|FILE_WRITE|FILE_TXT|FILE_ANSI);
// Datetime), Bid, Volume
// string s = FileRead()
string s = TimeToStr(TimeGMT()) + "|" + Bid + "|" + Volume[0];
FileWriteString(file_handle, s + "|*\r\n");
FileFlush(file_handle);
//FileClose(file_handle);
}
void OnDeinit(const int reason)
{
FileClose(file_handle);
}
Edit 4: Screencast to better show my issue: data updates only when I click on the output file
Watch Service does not update

First of all, a premise : I'm answering this question primarily for future users of WatchService, which (like me) could experience this problem (i.e. on some systems events are signaled way after they occur).
The problem is that the implementation of this feature in Java is native, so it is platform-dependant (take a look at https://docs.oracle.com/javase/7/docs/api/java/nio/file/WatchService.html, under the section 'platform dependencies').
In particular, on Windows 7 (and MacOSX afaict) the implementation uses polling to retrieve the changes from the filesystem, so you can't rely on the 'liveliness' of notifications from a WatchService. The notifications will eventually be signaled, but there are no guarantees on when it will happen.
I don't have a rigorous solution to this problem, but after a lot of trial and error I can describe what works for me :
First, when writing to a file that is registered (i.e. 'watched'), I try to flush the content every time I can and update the 'last modified' attribute on the file, e.g. :
try (FileWriter writer = new FileWriter(outputFile)) {
writer.write("The string to write");
outputFile.setLastModified(System.currentTimeMillis());
writer.flush();
}
Second, I try to 'trigger' the refresh from code (I know it's not good code, but in this case, I'm just happy it works 99% of the time)
Thread.sleep(2000);
// in case I've just created a file and I'm watching the ENTRY_CREATE event on outputDir
outputDir.list();
or (if watching ENTRY_MODIFY on a particular file in outputDir)
Thread.sleep(2000);
outputFile.length();
In both cases, a sleep call simply 'gives the time' to the mechanism underlying the WatchService to trigger, even though 2 seconds are probably a lot more than it is needed.

Probably missing quotes on file path.

Related

Unexplained halt of execution

I am working on a Java process that contains 2 threads: one for reading a file's contents and adding them in one shared blocking queue; and one for retrieving the lines from the blocking queue and sending them through the network (under a specified send rate). The two classes I have are the following:
Updated Code below
Producer Thread:
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
public class SourceFileProducer implements Runnable {
private File file;
private BufferedReader reader;
private ArrayBlockingQueue<String> buffer;
private String fileName;
private String endMarker;
public SourceFileProducer(ArrayBlockingQueue<String> buffer,
String endMarker, String fileName) {
this.buffer = buffer;
this.endMarker = endMarker;
file = new File(fileName);
if(file.exists()) {
try {
reader = new BufferedReader(new FileReader(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
this.fileName = fileName;
}
#Override
public void run() {
System.out.println("SourceFileProducer thread-" + Thread.currentThread().getId() + " initiating with source file: " + fileName);
String line = "";
try {
while((line = reader.readLine()) != null) {
try {
buffer.put(line);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
buffer.put(endMarker);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("SourceFileProducer thread-" + Thread.currentThread().getId() + " scanned and buffered the whole file.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
and the Consumer thread:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
public class SourceFileConsumer implements Runnable {
private ArrayBlockingQueue<String> buffer;
private BufferedReader socketInput;
private PrintWriter socketOutput;
private Socket client;
private ServerSocket serverSocket;
private long checkpoint[] = null;
private int rate[] = null;
private String endMarker;
public SourceFileConsumer(ArrayBlockingQueue<String> buffer, String endMarker,
ServerSocket serverSocket, Socket client, long checkpoint[], int rate[]) {
this.buffer = buffer;
this.endMarker = endMarker;
this.client = client;
try {
socketOutput = new PrintWriter(client.getOutputStream(), true);
socketInput = new BufferedReader(new InputStreamReader(client.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
this.checkpoint = new long[checkpoint.length];
this.rate = new int[rate.length];
for(int i = 0; i < checkpoint.length; i++) {
this.checkpoint[i] = checkpoint[i];
this.rate[i] = rate[i];
}
this.serverSocket = serverSocket;
}
#Override
public void run() {
String line = null;
long start = System.currentTimeMillis();
int index = 0;
boolean fileScanFlag = true;
while(fileScanFlag) {
long startTimestamp = System.currentTimeMillis();
long interval = (startTimestamp - start) / 1000L;
if(interval >= checkpoint[index]) {
if(index < checkpoint.length - 1) {
if(interval >= checkpoint[index + 1]) {
index += 1;
System.out.println("SourceFileConsumer thread-" + Thread.currentThread().getId() +
" progressed to checkpoint " + index + " with rate: " + rate[index]);
}
}
}
int counter = 0;
while(counter < rate[index]) {
try {
line = buffer.take();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
if(line == endMarker) {
fileScanFlag = false;
break;
}
if(socketOutput != null && socketOutput.checkError()) {
System.out.println("SourceFileConsumer Thread-" + Thread.currentThread().getId() + " detected broken link...");
try {
client = serverSocket.accept();
socketOutput = new PrintWriter(client.getOutputStream(), true);
socketInput = new BufferedReader(new InputStreamReader(client.getInputStream()));
} catch(IOException e) {
e.printStackTrace();
}
System.out.println("SourceFileConsumer Thread-" + Thread.currentThread().getId() + " re-established connection...");
}
if(socketOutput != null)
socketOutput.println(line);
counter += 1;
}
long endTimestamp = System.currentTimeMillis();
if(endTimestamp - startTimestamp <= 1000) {
System.out.println("thread-" + Thread.currentThread().getId() + " input rate: " + counter +
", wait time: " + (1000 - (endTimestamp - startTimestamp)));
try {
Thread.sleep((1000 - (endTimestamp - startTimestamp)));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
if(socketInput != null && socketOutput != null && client != null) {
try {
socketInput.close();
socketOutput.close();
client.close();
} catch(IOException e) {
e.printStackTrace();
}
}
System.out.println("SourceFileConsumer Thread-" + Thread.currentThread().getId() + " transfer complete.");
}
}
The problem is that, after a while, both threads hang and no tuples are sent. When I run a top command in my Linux machine, I see that the Java process, in which the two threads are running in, uses a really small amount of CPU time. Why is this happening? Is this a problem with starvation? I think that starvation can be avoided by using the LinkedBlockingQueue.
Any hints?
Thanks,
Nick
That’s quite a lot of code, especially within your consumer. So it’s not possible to preclude that there are multiple errors. I recommend to simplify your code to narrow the problem, e.g. test your producer-consumer hand-off and the network operations independently.
One obvious problem is that you are trying to signal the end of a file via an AtomicBoolean but your consumer isn’t actually testing it before taking items. If you look at the place where it takes items, there is an inner loop:
while(counter < rate[index]) {
try {
line = buffer.take();
…
Since the producer has no influence on the counter < rate[index] condition, it has no control over how many lines the consumer will attempt to take before checking the state of the fileScanFlag.
But even if you try to fix this by checking the boolean flag right before take, the result is broken due to possible race conditions. The atomic boolean and the blocking queue are both thread-safe on their own but your combination of the two is not.
Putting the last item on the queue and setting the flag are two distinct operations. Right in-between these two actions, the consumer can take the last item, recheck the flag and find it being false and go to the next attempt to take while the producer is about to set it to true.
One solution is to change the order of the operations on the consumer side, which requires resorting to polling:
polling: for(;;) {
line = buffer.poll(timeout, timeOutUnit); // control the cpu consumption via timeout
if(line!=null) break polling;
if(fileScanFlag.get()) break outerLoop;
}
An alternative is not to use two different communication constructs. Instead of maintaining a boolean flag, place an end marker object to the queue once the file reached an end. This is one of the rare cases, where using the identity of a String rather than equals is appropriate:
public class SourceFileProducer implements Runnable {
private String endMarker;
…
public SourceFileProducer(LinkedBlockingQueue<String> buffer,
String endMarker, String fileName) {
this.buffer = buffer;
this.endMarker = endMarker;
…
#Override
public void run() {
System.out.println("SourceFileProducer thread-" + Thread.currentThread().getId()
+ " initiating with source file: " + fileName);
String line;
try {
while((line = reader.readLine()) != null) buffer.put(line);
} catch (IOException|InterruptedException e) {
e.printStackTrace();
}
buffer.put(endMarker);
}
 
public class SourceFileConsumer implements Runnable {
private String endMarker;
…
public SourceFileConsumer(LinkedBlockingQueue<String> buffer, String endMarker,
ServerSocket serverSocket, Socket client, long checkpoint[], int rate[]) {
this.buffer = buffer;
this.endMarker = endMarker;
…
line = buffer.take();
if(line==endMarker) break;
The value of the end marker doesn’t matter but it’s object identity. Hence, the code which creates the two threads must contain something like:
// using new to ensure unique identity
private static final String EOF = new String("end of file");
…
new SourceFileProducer(queue, EOF, …)
new SourceFileConsumer(queue, EOF, …)
The new operator guarantees to produce an object with a unique identity, therefore, comparing that marker object with any other String, i.e. the lines returned by BufferedReader, via == will always evaluate to false. Care must be taken not to let the marker object escape to code not knowing about its special role.

Watchservice in windows 7 does not work

This code works fine in Linux but not in Windows 7: to get file contents update I have to click on the output file. Where is the trick?
I am using Windows 7 prof, NetBeans IDE 8.0 RC1 (Build 201402242200) updated to version NetBeans 8.0 Patch 1.1, JDK 1.8
package watchfilethreadmod;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.file.StandardWatchEventKinds.*;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class WatchFileThreadMod {
static class WatchFile {
String fileName;
long lastFilePos;
RandomAccessFile file;
public WatchFile(String _fileName, RandomAccessFile _file) {
fileName = _fileName;
lastFilePos = 0;
file = _file;
}
}
public static void shutDownListener(Thread thread) {
Thread thr = thread;
if (thr != null) {
thr.interrupt();
}
}
private static class MyWatchQueueReader implements Runnable {
/**
* the watchService that is passed in from above
*/
private WatchService myWatcher;
public ArrayList<WatchFile> threadFileToWatch;
public String dirPath;
public MyWatchQueueReader(String _dirPath, WatchService myWatcher, ArrayList<WatchFile> _threadFileToWatch) {
this.myWatcher = myWatcher;
this.threadFileToWatch = _threadFileToWatch;
this.dirPath = _dirPath;
}
private void openFile(WatchFile obj) {
try {
System.out.println("Open file "+obj.fileName);
obj.file = new RandomAccessFile(dirPath + "/" + obj.fileName, "r");
} catch (FileNotFoundException e) {
obj.file = null;
System.out.println("filename " + obj.fileName + " non trovato");
}
obj.lastFilePos = 0;
}
private void process(WatchEvent evt) {
String thisLine;
ArrayList<WatchFile> auxList = threadFileToWatch;
for (WatchFile obj : auxList) {
if (obj.fileName.equals(evt.context().toString())) {
if (obj.file == null) {
openFile(obj);
}
try {
obj.file.seek(obj.lastFilePos);
} catch (IOException e) {
System.err.println("Seek error: " + e);
}
try {
thisLine = obj.file.readLine();
if ((thisLine == null)&&(evt.kind() == ENTRY_MODIFY)) {
System.out.printf("---> thisLine == null received %s event for file: %s\n",
evt.kind(), evt.context());
obj.file.close();
System.out.println("Close file "+obj.fileName);
openFile(obj);
thisLine = obj.file.readLine();
}
while (thisLine != null) { // while loop begins here
if (thisLine.length() > 0) {
if (thisLine.substring(thisLine.length() - 1).equals("*")) {
obj.lastFilePos = obj.file.getFilePointer();
System.out.println(obj.fileName + ": " + thisLine);
}
}
thisLine = obj.file.readLine();
} // end while
} // end try
catch (IOException e) {
System.err.println("Error: " + e);
}
}
}
}
/**
* In order to implement a file watcher, we loop forever ensuring
* requesting to take the next item from the file watchers queue.
*/
#Override
public void run() {
try {
// get the first event before looping
WatchKey key = myWatcher.take();
while (key != null) {
// we have a polled event, now we traverse it and
// receive all the states from it
for (WatchEvent event : key.pollEvents()) {
WatchEvent.Kind eventType = event.kind();
if (eventType == OVERFLOW) {
continue;
}
process(event);
}
key.reset();
key = myWatcher.take();
}
} catch (InterruptedException e) {
ArrayList<WatchFile> auxList = threadFileToWatch;
for (WatchFile obj : auxList) {
if (obj.file != null) {
try {
obj.file.close();
System.out.println("chiusura file " + obj.fileName);
} catch (IOException ex) {
System.out.println("errore in chiusura file");
Logger.getLogger(WatchFileThreadMod.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
//e.printStackTrace();
}
System.out.println("Stopping thread");
}
}
public static void main(String[] args) throws Exception {
// get the directory we want to watch, using the Paths singleton class
//Path toWatch = Paths.get(DIRECTORY_TO_WATCH);
ArrayList<WatchFile> fileToWatch = new ArrayList<>();
String filename;
RandomAccessFile file;
fileToWatch.add(new WatchFile("EURUSD.rlt", new RandomAccessFile(args[0] + "/EURUSD.rlt", "r")));
filename = "EURCHF2.rlt";
try {
file = new RandomAccessFile(args[0] + "/" + filename, "r");
} catch (FileNotFoundException e) {
file = null;
System.out.println("filename " + filename + " non trovato");
}
fileToWatch.add(new WatchFile(filename, file));
fileToWatch = fileToWatch;
Path toWatch = Paths.get(args[0]);
if (toWatch == null) {
throw new UnsupportedOperationException("Directory not found");
}
// Sanity check - Check if path is a folder
try {
Boolean isFolder = (Boolean) Files.getAttribute(toWatch,
"basic:isDirectory", NOFOLLOW_LINKS);
if (!isFolder) {
throw new IllegalArgumentException("Path: " + toWatch + " is not a folder");
}
} catch (IOException ioe) {
// Folder does not exists
ioe.printStackTrace();
}
// make a new watch service that we can register interest in
// directories and files with.
WatchService myWatcher = toWatch.getFileSystem().newWatchService();
// start the file watcher thread below
MyWatchQueueReader fileWatcher = new MyWatchQueueReader(args[0], myWatcher, fileToWatch);
Thread processingThread = new Thread(fileWatcher, "FileWatcher");
processingThread.start();
toWatch.register(myWatcher, ENTRY_CREATE, ENTRY_MODIFY);
}
}
Edit: reduced code as requested.
Edit 2: file path
Edit 3: Metatrader code I am using to write data
#property strict
int file_handle;
string InpFileName = _Symbol + ".rlt"; // File name
input string InpDirectoryName = "Data"; // Folder name
int OnInit()
{
ResetLastError();
file_handle = FileOpen(InpDirectoryName + "//" + InpFileName, FILE_SHARE_READ|FILE_WRITE|FILE_TXT|FILE_ANSI);
if(file_handle == INVALID_HANDLE) {
PrintFormat("Failed to open %s file, Error code = %d", InpFileName, GetLastError());
ExpertRemove();
}
return INIT_SUCCEEDED;
}
void OnTick()
{
// file_handle = FileOpen(InpDirectoryName + "//" + InpFileName, FILE_SHARE_READ|FILE_WRITE|FILE_TXT|FILE_ANSI);
// Datetime), Bid, Volume
// string s = FileRead()
string s = TimeToStr(TimeGMT()) + "|" + Bid + "|" + Volume[0];
FileWriteString(file_handle, s + "|*\r\n");
FileFlush(file_handle);
//FileClose(file_handle);
}
void OnDeinit(const int reason)
{
FileClose(file_handle);
}
Edit 4: Screencast to better show my issue: data updates only when I click on the output file
Watch Service does not update
First of all, a premise : I'm answering this question primarily for future users of WatchService, which (like me) could experience this problem (i.e. on some systems events are signaled way after they occur).
The problem is that the implementation of this feature in Java is native, so it is platform-dependant (take a look at https://docs.oracle.com/javase/7/docs/api/java/nio/file/WatchService.html, under the section 'platform dependencies').
In particular, on Windows 7 (and MacOSX afaict) the implementation uses polling to retrieve the changes from the filesystem, so you can't rely on the 'liveliness' of notifications from a WatchService. The notifications will eventually be signaled, but there are no guarantees on when it will happen.
I don't have a rigorous solution to this problem, but after a lot of trial and error I can describe what works for me :
First, when writing to a file that is registered (i.e. 'watched'), I try to flush the content every time I can and update the 'last modified' attribute on the file, e.g. :
try (FileWriter writer = new FileWriter(outputFile)) {
writer.write("The string to write");
outputFile.setLastModified(System.currentTimeMillis());
writer.flush();
}
Second, I try to 'trigger' the refresh from code (I know it's not good code, but in this case, I'm just happy it works 99% of the time)
Thread.sleep(2000);
// in case I've just created a file and I'm watching the ENTRY_CREATE event on outputDir
outputDir.list();
or (if watching ENTRY_MODIFY on a particular file in outputDir)
Thread.sleep(2000);
outputFile.length();
In both cases, a sleep call simply 'gives the time' to the mechanism underlying the WatchService to trigger, even though 2 seconds are probably a lot more than it is needed.
Probably missing quotes on file path.

Java File handles won't close

Yes this question has been asked before however the issue is a little more complex it seems. I have used all solutions from previous questions that relate to this.
Relates to: Freeing Java File Handles
, Java keeps file locks no matter what
package me.test;
import java.io.File;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
public class Test {
Logger log = Logger.getAnonymousLogger();
FileHandler handle;
final static String newline = System.lineSeparator();
/**
* #param args
*/
public static void main(String[] args) {
Test t = new Test();
t.run();
}
public void run()
{
for (int i = 0; i < 6; i++) {
testLogs();
change();
}
testLogs();
if (handle != null)
{
handle.close();
log.removeHandler(handle);
}
}
public static FileHandler craftFileHandler(File file, boolean append)
{
if (file == null)
return null;
FileHandler fh = null;
try {
fh = new FileHandler(file.getPath(), append);
fh.setFormatter(new Formatter() {
#Override
public String format(LogRecord record) {
return "[test] " + "[" + record.getLevel().toString() + "]" + String.format(record.getMessage(), record.getParameters()) + newline;
}
});
return new FileHandler(file.getPath(), append);
} catch (Exception e) {
if (fh != null)
fh.close();
return null;
}
}
public void change()
{
if (handle != null)
{
handle.flush();
handle.close();
log.removeHandler(handle);
}
handle = null;
File f = new File("log.log");
handle = craftFileHandler(f, true);
System.out.println(f.getAbsolutePath());
if (handle != null)
log.addHandler(handle);
}
public void testLogs()
{
if (log == null)
{
log = Logger.getLogger("test");
log.setLevel(Level.ALL);
}
log.info("This is info #1");
log.warning("Warning 1");
log.info("meh info again.");
log.severe("SEVERE HELL YA NICE TEST");
log.info("You sure its good here?");
log.info("Handler count " + log.getHandlers().length);
}
}
This code is meant to be a test code. I made this test file so I can figure out how to fix this issue on a project I have.
The reason I have a loop of this is because the issue occurs too fast to explain. So a loop was the best way to simulate it. In my project there is a config for a Log file to choose where to put it. But if the file is not changed in config on its reload. It tends to leave the file locked and create extra files EVERY reload
I would like to get this working. If this starts working properly. Then I can properly implement it on my project.
You are getting multiple files created because you are creating a FileHandler and never closing it.
fh = new FileHandler(file.getPath(), append);
...
return new FileHandler(file.getPath(), append);
The fix?
return fh;
Finally or not makes absolutely no difference. In this case, you actually do want to be closing in the catch block since nothing will be able to close it if you don't.
Always close resources inside finally block-
try {
fh = new FileHandler(file.getPath(), append);
fh.setFormatter(new Formatter() {
#Override
public String format(LogRecord record) {
return "[test] " + "[" + record.getLevel().toString() + "]" + String.format(record.getMessage(), record.getParameters()) + newline;
}
});
return new FileHandler(file.getPath(), append);
} catch (Exception e) {
return null;
// never close in catch
} finally {
// lastly close anything that may be open
if (fh != null){
try {
fh.close();
} catch (Exception ex){
// error closing
}
}
}
After use the log method close all handlers.
this.logger.log(Level.SEVERE, (exception.getClass().getName() + ": " + exception.getMessage()) + "\r\n" + exception.getCause() + "\r\n" + "\r\n");
for (Handler handler : this.logger.getHandlers())
{
handler.close();
}
Well, one issue is here:
try {
fh = new FileHandler(file.getPath(), append);
fh.setFormatter(new Formatter() {
#Override
public String format(LogRecord record) {
return "[test] " + "[" + record.getLevel().toString() + "]" + String.format(record.getMessage(), record.getParameters()) + newline;
}
});
return new FileHandler(file.getPath(), append);
} catch (Exception e) {
if (fh != null)
fh.close();
return null;
You never close the file in the try statement, only closing it if there is an error. You should close the file as soon as you done with it:
try {
fh = new FileHandler(file.getPath(), append);
fh.setFormatter(new Formatter() {
#Override
public String format(LogRecord record) {
return "[test] " + "[" + record.getLevel().toString() + "]" + String.format(record.getMessage(), record.getParameters()) + newline;
}
});
//close fh
fh.close();
return new FileHandler(file.getPath(), append);
} catch (Exception e) {
if (fh != null)
fh.close();
return null;

Directory watching for changes in java

I am using WatchService to watch change in directory, in particular creation of new file in directory. Below is my code -
package watcher;
import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
import java.io.*;
public class Watch {
public static void main(String[] args) throws IOException {
Path dir = Paths.get("c:\\mk\\");
WatchService service = FileSystems.getDefault().newWatchService();
WatchKey key = dir.register(service, ENTRY_CREATE);
System.out.println("Watching directory: "+dir.toString());
for(;;){
WatchKey key1;
try {
key1 = service.take();
} catch (InterruptedException x) {
break;
}
for (WatchEvent<?> event: key1.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
if (kind == OVERFLOW) {
continue;
}
WatchEvent<Path> ev = (WatchEvent<Path>)event;
Path filename = ev.context();
Path child = dir.resolve(filename);
System.out.println("New file: "+child.toString()+" created.");
try{
FileInputStream in = new FileInputStream(child.toFile());
System.out.println("File opened for reading");
in.close();
System.out.println("File Closed");
}catch(Exception x){
x.printStackTrace();
}
}
boolean valid = key.reset();
if (!valid) {
break;
}
}
}
}
When I create file inside "mk" directory, I am getting notification for that. But when I copy some file in this directory, I am getting exception on opening that copied file.
My guess is Windows Copier dialog has still locked that file and I was unable to open that file. So basically I want to know is how to get notified for file has been closed by other process.
Output of above code is like -
Watching directory: c:\mk
New file: c:\mk\New Text Document (2).txt created.
File opened for reading
File Closed
New file: c:\mk\Config.class created.
java.io.FileNotFoundException: c:\mk\Config.class (The process cannot access the file because it is being used by another process)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at watcher.Watch.main(Watch.java:36)
New file: c:\mk\New Text Document (3).txt created.
File opened for reading
File Closed
Files "New Text Document (2).txt" and "New Text Document (3).txt" I have created but file "Config.class" I have copied from other directory.
Please help me on this.
I got this working by implementing algorithm: Watcher thread will put file names in BlockingQueue and other thread will poll this queue, takes file names, try few times to open file. If file gets opened, Windows Copier has released file lock and we can proceed. So when other threads finds file has been unlocked, other thread will put this file name in processed queue, from where my application will retrieve file name. Also the other thread while checking for file unlock by opening file, if it runs long time for unlocking file, we can place back this file name in BlockingQueue and process other file names, former can be processed later.
Solution: Hope this may help to other:
package dirwatch;
import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
import static java.nio.file.LinkOption.*;
import java.nio.file.attribute.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
public class WatchDir {
private final WatchService watcher;
private final Map<WatchKey,Path> keys;
private final boolean recursive;
private boolean trace = false;
private BlockingQueue<String> fileProcessingQueue;
//******* processedFileQueue **** will be used by other threads to retrive unlocked files.. so I have
// kept as public final
public final BlockingQueue<String> processedFileQueue;
private volatile boolean closeProcessingThread;
private volatile boolean closeWatcherThread;
private void processFiles(){
System.out.println("DirWatchProcessingThread Started");
String fileName;
outerLoop: while(!closeProcessingThread || !fileProcessingQueue.isEmpty()){
try{
fileName = fileProcessingQueue.poll(1000, TimeUnit.MILLISECONDS);
}catch(InterruptedException ie){
fileName = null;
}
if(fileName == null || fileName.equals("")){
continue outerLoop;
}
long startTime = System.currentTimeMillis();
innerLoop: while(true){
FileInputStream fis = null;
File file = new File(fileName);
try{
fis = new FileInputStream(fileName);
break innerLoop;
}catch(FileNotFoundException fnfe){
if(!file.exists() || file.isDirectory()){
System.out.println("File: '"+fileName+"has been deleted in file system or it is not file. Not processing this file.");
continue outerLoop;
}
try{
Thread.sleep(WatchDirParameters.millisToPuaseForFileLock);
}catch(InterruptedException ie){
}
if((System.currentTimeMillis() - startTime) > WatchDirParameters.millisToSwapFileForUnlocking){
if(fileProcessingQueue.offer(fileName)){
continue outerLoop;
}else{
startTime = System.currentTimeMillis();
continue innerLoop;
}
}
}finally{
if(fis != null){
try{
fis.close();
}catch(IOException ioe){
ioe.printStackTrace();
}
}
}
}
System.out.println("Queuing File: "+fileName);
processedLoop:while(true){
try{
if(processedFileQueue.offer(fileName, 1000, TimeUnit.MILLISECONDS)){
break processedLoop;
}
}catch(InterruptedException ie){
//ie.printStackTrace();
}
}
}
closeWatcherThread = true;
closeProcessingThread = true;
System.out.println("DirWatchProcessingThread Exited");
}
/**
* Process all events for keys queued to the watcher
*/
private void processEvents(){
System.out.println("DirWatcherThread started.");
while(!closeWatcherThread) {
// wait for key to be signalled
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException x) {
// if we are returning from these method, it means we no longer wants to watch directory
// we must close thread which may be waiting for file names in queue
continue;
}catch(ClosedWatchServiceException cwse){
break;
}
Path dir = keys.get(key);
if (dir == null) {
System.err.println("WatchKey not recognized!!");
continue;
}
try{
for (WatchEvent<?> event: key.pollEvents()) {
WatchEvent.Kind kind = event.kind();
if (kind == OVERFLOW) {
continue;
}
// Context for directory entry event is the file name of entry
WatchEvent<Path> ev = cast(event);
Path name = ev.context();
Path child = dir.resolve(name);
if(kind.equals(ENTRY_CREATE)){
// if directory is created, and watching recursively, then
// register it and its sub-directories
if (recursive) {
try {
if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
registerAll(child);
continue;
}
} catch (IOException x) {
// ignore to keep sample readbale
}
}
while(true){
if(fileProcessingQueue.remainingCapacity() < 2){
// if only one last can be inserted then don't queue this we need 1 empty space in queue
// for swaping file names..
// sleep for some time so processing thread may have made some rooms to queue in fileQueue
// this logic will not create any problems as only one this thread is inserting in queue
try{
Thread.sleep(200);
}catch(InterruptedException ie){
}
continue;
}
if(!fileProcessingQueue.offer(child.toString())){
// couldn't queue this element by whatever reason.. we will try to enqueue again by continuing loop
continue;
}else{
// file name has been queued in queue
break;
}
}
}
}
// reset key and remove from set if directory no longer accessible
boolean valid = key.reset();
if (!valid) {
keys.remove(key);
// all directories are inaccessible
if (keys.isEmpty()) {
break;
}
}
}catch(ClosedWatchServiceException cwse){
break;
}
}
closeProcessingThread = true;
closeWatcherThread = true;
System.out.println("DirWatcherThread exited.");
}
public void stopWatching(){
try{
watcher.close();
}catch(IOException ioe){
}
closeProcessingThread = true;
closeWatcherThread = true;
}
public static WatchDir watchDirectory(String dirName, boolean recursive) throws InvalidPathException, IOException, Exception{
try{
Path dir = Paths.get(dirName);
final WatchDir watchDir = new WatchDir(dir, recursive);
watchDir.closeProcessingThread = false;
watchDir.closeWatcherThread = false;
new Thread(new Runnable() {
public void run() {
watchDir.processFiles();
}
}, "DirWatchProcessingThread").start();
new Thread(new Runnable() {
public void run() {
watchDir.processEvents();
}
}, "DirWatcherThread").start();
return watchDir;
}catch(InvalidPathException ipe){
throw ipe;
}catch(IOException ioe){
throw ioe;
}catch(Exception e){
throw e;
}
}
#SuppressWarnings("unchecked")
private static <T> WatchEvent<T> cast(WatchEvent<?> event) {
return (WatchEvent<T>)event;
}
/**
* Register the given directory with the WatchService
*/
private void register(Path dir) throws IOException {
//WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
WatchKey key = dir.register(watcher, ENTRY_CREATE);
if (trace) {
Path prev = keys.get(key);
if (prev == null) {
System.out.format("register: %s\n", dir);
} else {
if (!dir.equals(prev)) {
System.out.format("update: %s -> %s\n", prev, dir);
}
}
}
keys.put(key, dir);
}
/**
* Register the given directory, and all its sub-directories, with the
* WatchService.
*/
private void registerAll(final Path start) throws IOException {
// register directory and sub-directories
Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
#Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
register(dir);
return FileVisitResult.CONTINUE;
}
});
}
/**
* Creates a WatchService and registers the given directory
*/
private WatchDir(Path dir, boolean recursive) throws IOException {
fileProcessingQueue = new ArrayBlockingQueue<String>(WatchDirParameters.fileQueueSize, false);
processedFileQueue = new ArrayBlockingQueue<String>(WatchDirParameters.fileQueueSize, false);
this.watcher = FileSystems.getDefault().newWatchService();
this.keys = new HashMap<WatchKey,Path>();
this.recursive = recursive;
//CreateTxtFile.createFile(dir, 1);
if (recursive) {
System.out.format("Scanning %s ...\n", dir);
registerAll(dir);
System.out.println("Done.");
} else {
register(dir);
}
// enable trace after initial registration
this.trace = true;
}
}
Parameter Class:
package dirwatch;
public class WatchDirParameters {
public static final int millisToPuaseForFileLock = 200;
public static final int fileQueueSize = 500;
public static final int millisToSwapFileForUnlocking = 2000;
}
Made an updated version of the file provided by #UDPLover that is built for use in a high rate of file access environment I converted the to process queue into a HashMap<String, WatchEvent> to carry the watch event over to pass to an abstract method inside of the file blocking checker itself. Also made a print() method that allows anything printed to console by the WatchCore to be enabled or disabled. The file polling for loop from the original example was updated to use the JDK8 function for loop, made all parts threaded/interrupted. This is untested as of yet, will update with fixes when I can test it.
package filewatcher;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.ClosedWatchServiceException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* #author Nackloose
* http://stackoverflow.com/questions/13998379/directory-watching-for-changes-in-java
*/
public abstract class WatchCore extends Thread {
//make class a thread by default
/**
* After the WatchCore recieves an event for a file and deems it unlocked,
* it will be passed to this function
*
* #param e WatchEvent for the file, after it has been affirmed to be
* unlocked.
*/
public abstract void onEventAndUnlocked(WatchEvent e);
private final WatchService watcher;
private final Map<WatchKey, Path> keys;
private final boolean recursive;
private boolean trace = false;
//converted to HashMap to remove the limitation as I need this in a high rate of file access enviroment.
//as well as to carry the event passed for that folder into the block check itself.
//got rid of the finished queue and made events pass to the abstract void above
private final HashMap<String, WatchEvent> fileProcessingQueue;
//create a varible to keep track of the thread checking the file blocking, so we can start and stop it.
private final WatchBlocker blocker;
public WatchCore(String dir) throws IOException {
//defaultly dont recurse
this(dir, false);
}
public WatchCore(String dir, boolean recursive) throws IOException {
this(Paths.get(dir), recursive);
}
public WatchCore(Path dir) throws IOException {
//defaultly dont recurse
this(dir, false);
}
public WatchCore(Path dir, boolean recursive) throws IOException {
fileProcessingQueue = new HashMap<>();
this.watcher = FileSystems.getDefault().newWatchService();
this.keys = new HashMap<>();
this.recursive = recursive;
//CreateTxtFile.createFile(dir, 1);
if (recursive) {
print("Scanning %s ...", dir);
registerAll(dir);
print("Done.");
} else {
register(dir);
}
// enable trace after initial registration
this.trace = true;
//start the thread to process files to be checked for file blocking
blocker = new WatchBlocker();
}
#SuppressWarnings("unchecked")
private static <T> WatchEvent<T> cast(WatchEvent<?> event) {
return (WatchEvent<T>) event;
}
#Override
public synchronized void start() {
//start up our thread _FIRST_
super.start();
//then start the blocking thread
blocker.start();
}
#Override
public void interrupt() {
//Everything backwards, stop the blocker _FIRST_
blocker.interrupt();
//then stop our thread.
super.interrupt();
}
/**
* Register the given directory with the WatchService
*/
private void register(Path dir) throws IOException {
//WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
WatchKey key = dir.register(watcher, ENTRY_CREATE);
if (trace) {
Path prev = keys.get(key);
if (prev == null) {
print("register: %s\n", dir);
} else {
if (!dir.equals(prev)) {
print("update: %s -> %s\n", prev, dir);
}
}
}
keys.put(key, dir);
}
/**
* Register the given directory, and all its sub-directories, with the
* WatchService.
*/
private void registerAll(final Path start) throws IOException {
// register directory and sub-directories
Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
#Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
register(dir);
return FileVisitResult.CONTINUE;
}
});
}
/**
* Process all events for keys queued to the watcher
*/
#Override
public void run() {
//this was previous called processEvents()
//pruned any un-nessicary continues, labels, and labels on breaks, a lot of them
//were redundant
print("DirWatcherThread started.");
//as long as we're not interrupted we keep working
while (!interrupted()) {
// wait for key to be signalled
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException x) {
// if we are returning from these method, it means we no longer wants to watch directory
// we must close thread which may be waiting for file names in queue
continue;
} catch (ClosedWatchServiceException cwse) {
break;
}
Path dir = keys.get(key);
if (dir == null) {
printe("WatchKey not recognized!!");
continue;
}
try {
//converted to functional for loop.
key.pollEvents().stream().filter((event) -> {
WatchEvent.Kind kind = event.kind();
return !(kind == OVERFLOW); //make sure we do the filter
}).forEach((event) -> {
WatchEvent.Kind kind = event.kind();
// Context for directory entry event is the file name of entry
WatchEvent<Path> ev = cast(event);
Path name = ev.context();
Path child = dir.resolve(name);
if (kind.equals(ENTRY_CREATE)) {
// if directory is created, and watching recursively, then
// register it and its sub-directories
if (recursive) {
try {
if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
registerAll(child);
return; //continue;
}
} catch (IOException x) {
// ignore to keep sample readbale
}
}
fileProcessingQueue.put(child.toString(), ev);
}
});
// reset key and remove from set if directory no longer accessible
boolean valid = key.reset();
if (!valid) {
keys.remove(key);
// all directories are inaccessible
if (keys.isEmpty()) {
break;
}
}
} catch (ClosedWatchServiceException cwse) {
break;
}
}
print("DirWatcherThread exited.");
}
/**
*
* #author
* http://stackoverflow.com/questions/13998379/directory-watching-for-changes-in-java
* Nackloose
*/
private class WatchBlocker extends Thread {
#Override
public synchronized void start() {
//get it going
super.start();
}
#Override
public void interrupt() {
//interupt our thread
super.interrupt();
}
#Override
public void run() {
//this was perviously processFiles()
//pruned any un-nessicary continues, labels, and labels on breaks, a lot of them
//were redundant
print("DirWatchProcessingThread Started");
Entry<String, WatchEvent> fileEvent;
outerLoop:
//as long as we're not interrupted we keep working
while (!interrupted()) {
if (fileProcessingQueue.isEmpty()) {
try {
Thread.sleep(WatchCoreParameters.timeToIdle);
} catch (InterruptedException ex) {
Logger.getLogger(WatchCore.class.getName()).log(Level.SEVERE, null, ex);
}
continue;
}
fileEvent = fileProcessingQueue.entrySet().iterator().next();
fileProcessingQueue.remove(fileEvent.getKey());
long startTime = System.currentTimeMillis();
while (true) {
FileInputStream fis = null;
File file = new File(fileEvent.getKey());
try {
fis = new FileInputStream(fileEvent.getKey());
break;
} catch (FileNotFoundException fnfe) {
if (!file.exists() || file.isDirectory()) {
print("File: '" + fileEvent + "has been deleted in file system or it is not file. Not processing this file.");
continue outerLoop;
}
try {
Thread.sleep(WatchCoreParameters.millisToPauseForFileLock);
} catch (InterruptedException ie) {
}
if ((System.currentTimeMillis() - startTime) > WatchCoreParameters.millisToSwapFileForUnlocking) {
fileProcessingQueue.put(fileEvent.getKey(), fileEvent.getValue());
}
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
}
print("Queuing File: " + fileEvent);
//pass the unlocked file event to the abstract method
onEventAndUnlocked(fileEvent.getValue());
}
print("DirWatchProcessingThread Exited");
}
}
/**
*
* #author
* http://stackoverflow.com/questions/13998379/directory-watching-for-changes-in-java
* Nackloose
*/
public static class WatchCoreParameters {
public static int timeToIdle = 2000, millisToPauseForFileLock = 200,
millisToSwapFileForUnlocking = 2000;
public static boolean verbose = false;
}
//<editor-fold defaultstate="collapsed" desc="Printing methods">
private void print(String s) {
//defaultly we're not writing an error
print(s, false);
}
public static final void print(String s, boolean error) {
//check verbosity, exit if none.
if (!WatchCoreParameters.verbose) {
return;
}
//if this is an error, assign System.err to a temp varible
//otherise assign System.out for normal printing
PrintStream out = (!error ? System.out : System.err);
if (s.contains("\n")) { // check to see if theirs a new line
out.print(s); //print accordingly
} else {
out.println(s); //print accordingly
}
}
public static final void printe(String s) {
//shortcut/convenience method for printing an error
print(s, true);
}
public static final void print(String s, Object... formatObj) {
//check verbosity, exit if none.
if (!WatchCoreParameters.verbose) {
return;
}
//format the object into the string, and if no newline is there, add it.
System.out.format(s + (s.contains("\n") ? "" : "\n"), formatObj);
}
//</editor-fold>
}

MP3 won't stream with JMF

Basic idea is to access .mp3 file and send it through RTP stream to other client, who will want to play that song.
Here is RTPServer.java, which I found online and modified it to my liking.
package server;
import java.net.InetAddress;
import javax.media.rtp.*;
import javax.media.rtp.rtcp.*;
import javax.media.*;
import javax.media.protocol.*;
import javax.media.control.*;
public class RTPServer implements ControllerListener, Runnable {
private boolean realized = false;
private boolean configured = false;
private String ipAddress;
Processor p;
MediaLocator src;
public static void main (String[] args) {
RTPServer rtp = new RTPServer("192.168.1.101", "04 - Blue.mp3");
Thread t = new Thread(rtp);
t.start();
}
public RTPServer(String ip, String song) {
ipAddress = ip;
String srcFile = "Muzika\\" + song;
src = new MediaLocator("file:" + srcFile);
}
private void setTrackFormat(Processor p) {
// Get the tracks from the processor
TrackControl [] tracks = p.getTrackControls();
// Do we have atleast one track?
if (tracks == null || tracks.length < 1) {
System.out.println("Couldn't find tracks in processor");
System.exit(1);
}
// Set the output content descriptor to RAW_RTP
// This will limit the supported formats reported from
// Track.getSupportedFormats to only valid RTP formats.
ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW_RTP);
p.setContentDescriptor(cd);
Format supported[];
Format chosen;
boolean atLeastOneTrack = false;
// Program the tracks.
for (int i = 0; i < tracks.length; i++) {
Format format = tracks[i].getFormat();
System.out.println("Trenutni format je " +format.getEncoding());
if (tracks[i].isEnabled()) {
supported = tracks[i].getSupportedFormats();
for (int n = 0; n < supported.length; n++)
System.out.println("Supported format: " + supported[n]);
// We've set the output content to the RAW_RTP.
// So all the supported formats should work with RTP.
// We'll just pick the first one.
if (supported.length > 0) {
chosen = supported[0]; // this is where I tried changing formats
tracks[i].setFormat(chosen);
System.err.println("Track " + i + " is set to transmit as: " +chosen);
atLeastOneTrack = true;
} else
tracks[i].setEnabled(false);
} else
tracks[i].setEnabled(false);
}
}
private void transmit(Processor p) {
try {
DataSource output = p.getDataOutput();
PushBufferDataSource pbds = (PushBufferDataSource) output;
RTPManager rtpMgr = RTPManager.newInstance();
SessionAddress localAddr, destAddr;
SendStream sendStream;
int port = 42050;
SourceDescription srcDesList[];
localAddr = new SessionAddress( InetAddress.getLocalHost(), port);
InetAddress ipAddr = InetAddress.getByName(ipAddress);
destAddr = new SessionAddress( ipAddr, port);
rtpMgr.initialize(localAddr);
rtpMgr.addTarget(destAddr);
sendStream = rtpMgr.createSendStream(output, 0);
sendStream.start();
System.err.println( "Created RTP session: " + ipAddress + " " + port);
p.start();
} catch(Exception e) {
e.printStackTrace();
}
}
public synchronized void controllerUpdate(ControllerEvent evt) {
if (evt instanceof RealizeCompleteEvent) {
realized = true;
} else if (evt instanceof ConfigureCompleteEvent) {
configured = true;
} else if (evt instanceof EndOfMediaEvent) {
System.exit(0);
} else {
// System.out.println(evt.toString());
}
}
public void run() {
try {
p = Manager.createProcessor(src);
p.addControllerListener(this);
p.configure();
while (! configured) {
try {
Thread.currentThread().sleep(100L);;
} catch (InterruptedException e) {
// ignore
}
}
setTrackFormat(p);
p.setContentDescriptor(new ContentDescriptor(ContentDescriptor.RAW_RTP));
p.realize();
while (! realized) {
try {
Thread.currentThread().sleep(100L);;
} catch (InterruptedException e) {
// ignore
}
}
transmit(p);
} catch(Exception e) {
e.printStackTrace();
System.exit(1);
}
}
}
And here is receiving end, RTPClient:
package client;
import javax.media.*;
public class RTPClient implements ControllerListener, Runnable {
Player p;
MediaLocator src;
public static void main(String[] args) {
RTPClient rtp = new RTPClient("192.168.1.100");
Thread t = new Thread(rtp);
t.start();
}
public RTPClient(String ip) {
String srcUrl = "rtp://" + ip + ":42050/audio/1";
DataSink sink;
src = new MediaLocator(srcUrl);
}
public void run() {
try {
p = Manager.createPlayer(src);
p.addControllerListener(this);
p.start();
} catch(Exception e) {
e.printStackTrace();
System.exit(1);
}
}
public synchronized void controllerUpdate(ControllerEvent evt) {
if (evt instanceof EndOfMediaEvent) {
System.exit(0);
} else {
System.out.println(evt.toString());
}
}
}
I figured, it successfully sends the whatever file I choose, but when I send .mp3, Client won't play it. I get:
RTP Handler internal error:
javax.media.ControllerErrorEvent[source=com.sun.media.content.unknown.Handler#9ed927,message=Internal
module com.sun.media.BasicRendererModule#1386000: failed to handle a data
format change!]
Interesting thing is, .wav is sent perfectly. So my guess was is the format set prior to sending. And I tried changing format to some other supported format, but then I get bunch of other errors.
Failed to build a graph for the given custom options.
Failed to realize: com.sun.media.ProcessEngine#eee36c
Cannot build a flow graph with the customized options:
Unable to transcode format: mpegaudio, 48000.0 Hz, 16-bit, Stereo, LittleEndian, Signed, 20000.0 frame rate, FrameSize=11264 bits
to: ULAW/rtp, 8000.0 Hz, 8-bit, Stereo
outputting to: RAW/RTP
Error: Unable to realize com.sun.media.ProcessEngine#eee36c
Finally, I opened JMStudio (the built-in app for sending/receiving media streams in JMF), and when I try to stream .mp3, I get exact same error as when running my app. JMF is set up fine, I checked PATH and CLASSPATH, also I installed mp3plugin which is also setup fine. Everything seems fine, but it just doesn't work! At least .mp3 is not.
So, how can I make .mp3 "go to the other end"?
Solved.
All I had to do is add these lines in constructor for sender/receiver.
Format input1 = new AudioFormat(AudioFormat.MPEGLAYER3);
Format input2 = new AudioFormat(AudioFormat.MPEG);
Format output = new AudioFormat(AudioFormat.LINEAR);
PlugInManager.addPlugIn(
"com.sun.media.codec.audio.mp3.JavaDecoder",
new Format[]{input1, input2},
new Format[]{output},
PlugInManager.CODEC);
Might help somebody else with this problem :)
Still don't know why JMStudio isn't working... Not that I care anymore.
My environment cannot detect the newly added plugin. I would have to hardcode the codec into the track. It works but the mp3 is cluttering. .wav is perfectly fine though.
javax.media.Codec codec = (javax.media.Codec) (Class.forName(plugins.get(0)).newInstance());
com.sun.media.codec.audio.mp3.JavaDecoder decoder = new com.sun.media.codec.audio.mp3.JavaDecoder();
Codec[] cc = new Codec[2];
cc[0] = codec;
cc[1] = decoder;
try {
tracks[0].setCodecChain(cc);
} catch (UnsupportedPlugInException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NotConfiguredError e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
There are a couple things to do to make the code in question works:
put mp3plugin.jar in the classpath. It is a mp3 plugin for JMF. You may find it online.
put the following code in the main method to register the newly added plugin.
Format input1 = new AudioFormat(AudioFormat.MPEGLAYER3);
Format input2 = new AudioFormat(AudioFormat.MPEG);
Format output = new AudioFormat(AudioFormat.LINEAR);
PlugInManager.addPlugIn(
"com.sun.media.codec.audio.mp3.JavaDecoder",
new Format[]{input1, input2},
new Format[]{output},
PlugInManager.CODEC);
set the track format to AduioFormat.DVI_RTP in the RTPServer.java to convert your mp3 music to a format that RTPClient can play.
Before
if (supported.length > 0) {
chosen = supported[0]; // this is where I tried changing formats
tracks[i].setFormat(chosen);
System.err.println("Track " + i + " is set to transmit as: " +chosen);
atLeastOneTrack = true;
} else
After ( replace "chosen" with "new AudioFormat(AudioFormat.DVI_RTP)" )
if (supported.length > 0) {
chosen = supported[0]; // this is where I tried changing formats
tracks[i].setFormat(new AudioFormat(AudioFormat.DVI_RTP));
atLeastOneTrack = true;
} else
Then everything should work just fine.
Here is my RTPServer
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import javax.media.rtp.*;
import javax.media.rtp.rtcp.*;
import javax.media.*;
import javax.media.protocol.*;
import javax.media.control.*;
import javax.media.format.AudioFormat;
public class RTPServerMP3 implements ControllerListener {
private String ipAddress;
Processor p;
public static void main(String[] args) throws NoProcessorException, IOException {
Format input1 = new AudioFormat(AudioFormat.MPEGLAYER3);
Format input2 = new AudioFormat(AudioFormat.MPEG);
Format output = new AudioFormat(AudioFormat.LINEAR);
PlugInManager.addPlugIn(
"com.sun.media.codec.audio.mp3.JavaDecoder",
new Format[]{input1, input2},
new Format[]{output},
PlugInManager.CODEC);
RTPServerMP3 rtp = new RTPServerMP3("192.168.1.86");
rtp.p = Manager.createProcessor(new MediaLocator((new File( "roar_of_future.mp3")).toURL()));
rtp.p.addControllerListener(rtp);
rtp.p.configure();
}
public RTPServerMP3(String ip) throws MalformedURLException {
ipAddress = ip;
}
private void setTrackFormat(Processor p) {
// Get the tracks from the processor
TrackControl[] tracks = p.getTrackControls();
// Do we have atleast one track?
if (tracks == null || tracks.length < 1) {
System.out.println("Couldn't find tracks in processor");
System.exit(1);
}
// Set the output content descriptor to RAW_RTP
// This will limit the supported formats reported from
// Track.getSupportedFormats to only valid RTP formats.
ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW_RTP);
p.setContentDescriptor(cd);
Format supported[];
Format chosen;
boolean atLeastOneTrack = false;
// Program the tracks.
for (int i = 0; i < tracks.length; i++) {
Format format = tracks[i].getFormat();
System.out.println("seeing format " + format.getEncoding() + " for track " + i);
if (tracks[i].isEnabled()) {
supported = tracks[i].getSupportedFormats();
for (int n = 0; n < supported.length; n++)
System.out.println("Supported format: " + supported[n]);
// We've set the output content to the RAW_RTP.
// So all the supported formats should work with RTP.
// We'll just pick the first one.
if (supported.length > 0) {
chosen = supported[0]; // this is where I tried changing formats
tracks[i].setFormat(new AudioFormat(AudioFormat.DVI_RTP));
System.err.println("Track " + i + " is set to transmit as: " + chosen);
atLeastOneTrack = true;
} else
tracks[i].setEnabled(false);
} else
tracks[i].setEnabled(false);
}
}
private void transmit(Processor p) {
try {
DataSource output = p.getDataOutput();
PushBufferDataSource pbds = (PushBufferDataSource) output;
RTPManager rtpMgr = RTPManager.newInstance();
SessionAddress localAddr, destAddr;
SendStream sendStream;
int port = 49150;
SourceDescription srcDesList[];
localAddr = new SessionAddress(InetAddress.getLocalHost(), port/2+10);
InetAddress ipAddr = InetAddress.getByName(ipAddress);
destAddr = new SessionAddress(ipAddr, port);
rtpMgr.initialize(localAddr);
rtpMgr.addTarget(destAddr);
sendStream = rtpMgr.createSendStream(output, 0);
sendStream.start();
System.err.println("Created RTP session: " + ipAddress + " " + port);
p.start();
} catch (Exception e) {
e.printStackTrace();
}
}
public synchronized void controllerUpdate(ControllerEvent evt) {
if (evt instanceof RealizeCompleteEvent) {
transmit(p);
} else if (evt instanceof ConfigureCompleteEvent) {
setTrackFormat(p);
p.setContentDescriptor(new ContentDescriptor(ContentDescriptor.RAW_RTP));
p.realize();
} else if (evt instanceof EndOfMediaEvent) {
System.exit(0);
}
}
}
Here is my RTPClient
import java.io.IOException;
import javax.media.*;
public class RTPClientMP3 {
public static void main(String[] args) throws NoPlayerException, CannotRealizeException, IOException {
String srcUrl = "rtp://192.168.1.86:49150/audio/1";
MediaLocator src = new MediaLocator(srcUrl);
Player player = Manager.createRealizedPlayer(src);
player.start();
}
}

Categories