Monitoring a file for changes during runtime in Java can be
accomplished by using the WatchService API, which is part of the java.nio.file
package. This API allows you to watch for changes in a directory, such as file
modifications, deletions, or creations.
Here is an example of how to use WatchService to monitor a
file for changes:
Step-by-Step Implementation
- Setup
the WatchService: Create a WatchService and register the directory
containing the file you want to monitor.
- Monitor
for Changes: Continuously monitor the directory for events such as
file modification, creation, or deletion.
- Handle
the Event: Once an event is detected, check if it pertains to the file
you're interested in.
Example Code
java
import java.io.IOException; import java.nio.file.*; import static java.nio.file.StandardWatchEventKinds.*; public class FileMonitor { public static void main(String[] args)
{ // Path of the
directory containing the file to monitor Path path =
Paths.get("path/to/your/directory"); // File name to
monitor String fileName
= "file-to-monitor.txt"; try {
WatchService watchService = FileSystems.getDefault().newWatchService();
path.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
System.out.println("Monitoring the directory for changes...");
while (true) {
WatchKey key;
try {
key = watchService.take(); // Wait for an event
} catch (InterruptedException ex) {
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 filePath = ev.context();
// Check if the event is related to our specific file
if (filePath.toString().equals(fileName)) {
System.out.println("Event detected: " + kind + " on file
" + filePath);
if (kind == ENTRY_MODIFY) {
System.out.println("The file " + filePath + " has been
modified.");
} else if (kind == ENTRY_CREATE) {
System.out.println("The file " + filePath + " has been
created.");
} else if (kind == ENTRY_DELETE) {
System.out.println("The file " + filePath + " has been
deleted.");
}
}
}
// Reset the key -- this step is critical to receive further watch events.
boolean valid = key.reset();
if (!valid) {
break;
}
} } catch
(IOException e) {
e.printStackTrace(); } } } |
How It Works
Ø WatchService:
This is the key class used to monitor file system changes.
Ø Path:
Represents the path to the directory where the file resides.
Ø register:
Registers the directory with the WatchService to listen for specific types of
events (creation, deletion, modification).
Ø while
(true): Keeps the application running, continuously monitoring for changes.
Ø pollEvents:
Retrieves and processes all pending events for the WatchKey.
Notes
- Directory
Monitoring: The WatchService monitors the directory, not individual
files. However, by checking the filename in the event, you can monitor
specific files.
- Resetting
the WatchKey: After processing the events, it's crucial to reset the
WatchKey so that it can receive further events.
- Performance:
This solution is efficient for monitoring directories or a small number of
files. For large directories or multiple files, consider optimizing the
code.
This approach should work well for monitoring file changes
in real-time during the runtime of your Java application.
Approach 2 using Thread
If you want to monitor a file for changes using only Java
threads (without relying on the WatchService API), you can manually check the
file's last modified timestamp at regular intervals in a separate thread. This
approach is simpler but less efficient and flexible compared to using
WatchService.
Here’s how you can implement this:
Example Code
java
import java.io.File; public class FileMonitorThread { private static class FileWatcher
implements Runnable { private final
File file; private long
lastModifiedTime; public
FileWatcher(String filePath) {
this.file = new File(filePath);
if (file.exists()) {
this.lastModifiedTime = file.lastModified();
} else {
System.out.println("File does not exist initially.");
this.lastModifiedTime = 0;
} } @Override public void
run() {
while (!Thread.currentThread().isInterrupted()) {
if (file.exists()) {
long currentModifiedTime = file.lastModified();
if (currentModifiedTime != lastModifiedTime) {
lastModifiedTime = currentModifiedTime;
System.out.println("The file " + file.getName() + " has been
modified at " + lastModifiedTime);
}
} else {
System.out.println("File has been deleted or does not exist.");
break;
}
try {
Thread.sleep(1000); // Check every second
} catch (InterruptedException e) {
System.out.println("File monitoring thread interrupted.");
Thread.currentThread().interrupt();
}
} } } public static void main(String[] args)
{ // File path to
monitor String filePath
= "path/to/your/file.txt"; // Create and
start the file monitoring thread Thread
fileWatcherThread = new Thread(new FileWatcher(filePath));
fileWatcherThread.start(); // Main program
logic continues here...
System.out.println("Main program is running..."); // Simulate
doing something else in the main thread try {
Thread.sleep(10000); // Main thread sleeps for 10 seconds } catch
(InterruptedException e) {
e.printStackTrace(); } // If you want
to stop the monitoring, interrupt the thread
fileWatcherThread.interrupt();
System.out.println("Main program has finished."); } } |
Explanation
- FileWatcher
Class:
ü
The FileWatcher class implements Runnable and is
responsible for monitoring a specific file.
ü
It keeps track of the last modified time of the
file (lastModifiedTime).
ü
In the run method, it checks if the file's
lastModified timestamp has changed compared to the stored lastModifiedTime.
- Thread
Creation:
ü
In the main method, an instance of FileWatcher
is created with the path to the file you want to monitor.
ü
A new Thread is created with this FileWatcher as
its task and then started using fileWatcherThread.start().
- Checking
for Changes:
ü
The thread repeatedly checks the file's last
modified time every second (using Thread.sleep(1000)).
ü
If a change is detected, it updates the stored
timestamp and prints a message indicating that the file has been modified.
- Handling
File Deletion:
ü
The code checks if the file exists. If it
doesn't, the thread prints a message and stops monitoring.
- Interrupting
the Thread:
ü
You can interrupt the monitoring thread from the
main thread using fileWatcherThread.interrupt() to stop it when necessary.
Usage Notes
Ø Polling
Interval: The polling interval (1 second in this example) can be adjusted
depending on how frequently you want to check for file changes. A shorter
interval checks more frequently but uses more resources.
Ø Performance:
This method is not as efficient as using WatchService, especially if you need
to monitor multiple files or directories. It continuously polls the file
system, which can be resource-intensive.
Ø Thread
Safety: If you are dealing with multiple threads accessing the file, you
may need to ensure thread safety depending on your application's requirements.
This approach is simple and works well for small-scale
scenarios where you only need to monitor one or a few files and where the
inefficiencies of polling are acceptable.
Real Life Example using thread:
First Download some jar file
com.fasterxml.jackson.databind.jar
jackson-annotations-2.1.2.jar
jackson-core-2.8.1.jar
java
MongoCluster
package com.kartik.beans; import java.util.List; public class MongoCluster { private List<ClusterNode> cluster; private String dbName; private String dbType; private List<Long> cobrand; /** * @return the cluster */ public List<ClusterNode> getCluster() { return cluster; } /** * @param cluster
* the
cluster to set */ public void setCluster(List<ClusterNode>
cluster) { this.cluster = cluster; } /** * @return the dbName */ public String getDbName() { return dbName; } /** * @param dbName
* the
dbName to set */ public void setDbName(String dbName) { this.dbName = dbName; } /** * @return the dbType */ public String getDbType() { return dbType; } /** * @param dbType
* the
dbType to set */ public void setDbType(String dbType) { this.dbType = dbType; } /** * @return the cobrand */ public List<Long> getCobrand() { return cobrand; } /** * @param cobrand
* the
cobrand to set */ public void setCobrand(List<Long> cobrand) { this.cobrand = cobrand; } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return "MongoCluster [cluster=" + cluster
+ ", dbName=" + dbName + ", dbType=" + dbType +
", cobrand=" + cobrand + "]"; } /* * (non-Javadoc) * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((cluster == null) ? 0 :
cluster.hashCode()); result = prime * result + ((cobrand == null) ? 0 :
cobrand.hashCode()); result = prime * result + ((dbName == null) ? 0 :
dbName.hashCode()); result = prime * result + ((dbType == null) ? 0 :
dbType.hashCode()); return result; } /* * (non-Javadoc) * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; MongoCluster other = (MongoCluster) obj; if (cluster == null) { if (other.cluster != null) return false; } else if (!cluster.equals(other.cluster)) return false; if (cobrand == null) { if (other.cobrand != null) return false; } else if (!cobrand.equals(other.cobrand)) return false; if (dbName == null) { if (other.dbName != null) return false; } else if (!dbName.equals(other.dbName)) return false; if (dbType == null) { if (other.dbType != null) return false; } else if (!dbType.equals(other.dbType)) return false; return true; } } |
java
package com.kartik.beans; import java.util.List; public class MongoPool { private List<MongoCluster> databases; /** * @return the databases */ public List<MongoCluster> getDatabases() { return databases; } /** * @param databases
* the
databases to set */ public void setDatabases(List<MongoCluster>
databases) { this.databases = databases; } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return "MongoPool [databases=" +
databases + "]"; } /* * (non-Javadoc) * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((databases == null) ? 0 :
databases.hashCode()); return result; } /* * (non-Javadoc) * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; MongoPool other = (MongoPool) obj; if (databases == null) { if (other.databases != null) return false; } else if (!databases.equals(other.databases)) return false; return true; } } |
java
package com.kartik.file.iread; import java.io.File; import com.kartik.beans.MongoPool; public interface IFileRead { MongoPool jsonToObject(File fileName); MongoPool fileMonitor(); } |
java
package com.kartik.file.iread.impl; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import com.fasterxml.jackson.core.JsonGenerationException; import
com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.kartik.beans.ClusterNode; import com.kartik.beans.MongoCluster; import com.kartik.beans.MongoPool; import com.kartik.file.iread.IFileRead; public class IFileReadImpl implements IFileRead{ @Override public MongoPool jsonToObject(File fileName) { ObjectMapper mapper = new ObjectMapper(); try { MongoPool mm=createDummyObject(); String jsonInString =
mapper.writeValueAsString(mm); System.out.println(jsonInString); MongoPool mapObject =
mapper.readValue(fileName, MongoPool.class); /*List<MongoPool> mapObject =
mapper.readValue(fileName, new
TypeReference<ArrayList<MongoPool>>() { });*/ System.out.println("Java object created
from JSON String :"); //Convert Map to JSON
String json = mapper.writeValueAsString(mapObject);
//Print JSON output
System.out.println(json);
return mapObject; } catch (JsonGenerationException ex) { ex.printStackTrace(); } catch (JsonMappingException ex) { ex.printStackTrace(); } catch (IOException ex) { ex.printStackTrace(); } return null; } /*@Override public boolean fileMonitor() { // Create the monitor FileMonitor monitor = new FileMonitor(1000); // Add some files to listen for monitor.addFile(new
File("d:\\book.txt")); // monitor.addFile(new
File("d:\\cobrand.txt")); // Add a dummy listener return monitor.addListener(monitor.new
TestListener()); // Avoid program exit //while (!false); }*/ @Override public MongoPool fileMonitor() { File fileName =new File("d:\\book.txt"); return jsonToObject(fileName); } private MongoPool createDummyObject() { List<MongoCluster> mn=new
ArrayList<MongoCluster>(); MongoPool mmm=new MongoPool(); MongoCluster staff = new MongoCluster(); List<ClusterNode> ll=new
ArrayList<ClusterNode>(); ClusterNode cl=new ClusterNode(); cl.setHost("localhost"); cl.setPort(7373); ll.add(cl); ClusterNode cll=new ClusterNode(); cll.setHost("123.02.10.22"); cll.setPort(7373); ll.add(cll); staff.setDbName("accdb"); staff.setCluster(ll); staff.setDbType("oltp"); List<Long> abc=new ArrayList<Long>(); abc.add(12345L); abc.add(5342L); staff.setCobrand(abc); mn.add(staff); mmm.setDatabases(mn); return mmm; } } |
Java
package com.kartik.file.monitor; import java.io.File; public interface FileChangeListener { /** * Invoked when a file changes. * * @param fileName
* name of changed file. */ public boolean fileChanged(File file); } |
Java
package com.kartik.file.monitor; import java.io.File; import java.io.IOException; import java.util.HashMap; import com.kartik.beans.MongoPool; import com.kartik.file.iread.impl.IFileReadImpl; public class FileMonitor implements Runnable{ private static final long SLEEP_TIME = 1000l;//one
second private HashMap<File, Long> filesMap; public static MongoPool map = null; @Override public void run() { final String fileName = "d:\\book.txt"; while(true){ try { long currentModifiedTime =
checkForModification(fileName); File file=new File(fileName); long lastModifiedTime =0l; if(filesMap==null){ filesMap =new
HashMap<File, Long>(); }else{ lastModifiedTime = ((Long)
filesMap.get(file)).longValue(); } if (currentModifiedTime !=
lastModifiedTime){ IFileReadImpl im=new
IFileReadImpl(); map=im.fileMonitor(); filesMap.put(file, new
Long(currentModifiedTime)); } Thread.sleep(SLEEP_TIME); } catch (InterruptedException ignore) { } catch (IOException e) { throw new
FileMonitorException("Scanfile Error", e); } } } private long checkForModification(String fileName)
throws IOException{ File scanFile = new File(fileName); if (!scanFile.exists()){ scanFile.createNewFile(); return 0l; } else if (!scanFile.canRead()){ throw new FileMonitorException("Scan
file can not be read."); } else if (!scanFile.isFile()){ throw new FileMonitorException("Invalid
scan file."); } else { return scanFile.lastModified(); } } public FileMonitor(){ } public FileMonitor(MongoPool map){ this.setMap(map); } /** * @return the map */ public static MongoPool getMap() { return map; } /** * @param map the map to set */ public static void setMap(MongoPool map) { FileMonitor.map = map; } } |
Java
package com.kartik.file.monitor; public class FileMonitorException extends
RuntimeException{ private static final long serialVersionUID =
-1122954555834923178L; public FileMonitorException(String msg, Throwable t)
{ super(msg, t); } public FileMonitorException(String msg) { super(msg); } } |
Java
package com.kartik.main.demo; import java.io.File; import com.kartik.beans.MongoPool; import com.kartik.file.iread.impl.IFileReadImpl; import com.kartik.file.monitor.FileMonitor; public class Main { public static MongoPool map = null; public static void main(String[] args) { System.out.println("Befor Hash Map
data"); IFileReadImpl im=new IFileReadImpl(); File fileName=new File("d:\\book.txt"); map=im.jsonToObject(fileName); FileMonitor fm=new FileMonitor(); Thread t = new Thread(fm); t.start(); map=fm.getMap(); System.out.println("wrwrer"); } } |
Book.txt file
{ "databases":[ { "cluster":[
{"host":"localhost","port":27017} ], "dbName":"documenttorage", "dbType":"oltp", "brandName":[123497,534294] }, { "cluster":[
{"host":"localhost","port":27017} ], "dbName":"test", "dbType":"ycc", " brandName":[35436,465629] } ] } |
0 Comments