Example of how Java can implement file change listening and java change listening

Source: Internet
Author: User

Example of how Java can implement file change listening and java change listening

If logback is used as the log output component in an application, most of the configurations will be 'logback. xml file. In the production environment, modify the logback directly. the Log Level in the xml file can take effect without restarting the application. How can this function be implemented?

If logback is used as the log output component in an application, most logback configurations are performed. xml file. In the production environment, modify the logback directly. the Log Level in the xml file takes effect without restarting the application.

So how is this function implemented?

I. Problem description and analysis

To solve the problem above, we will first throw an actual case. In my personal website Z +, all gadgets are dynamically added and hidden through the configuration file, because there is only one server, the configuration file is saved in a directory of the server.

Currently, when the content of this file changes, the application can perceive this change, reload the file content, and update the internal cache of the application.

One of the easiest ways to think of is to poll and determine whether the file has been modified. If so, reload the file and refresh the memory. The main concerns are as follows:

  1. How to round-robin?
  2. How can I determine whether a file is modified?
  3. Is the service unavailable due to configuration exceptions? (That is, fault tolerance, which is not associated with this topic, but is important ...)

II. Design and Implementation

After the problem is abstracted, the corresponding solution is clearer.

  1. How to round-robin? -- Timer and ScheduledExecutorService can all be implemented
  2. How to Determine file modification? -- Obtain the last modification time of the object according to java. io. File # lastModified.

A simple implementation is easier:

Public class FileUpTest {private long lastTime; @ Test public void testFileUpdate () {File file = new File ("/tmp/alarmConfig "); // first, the last modification timestamp of the file is lastTime = file. lastModified (); // scheduled task, which is used to determine whether the file changes every second. That is, to determine whether the lastModified changes ScheduledExecutorService scheduledExecutorService = Executors. newScheduledThreadPool (1); scheduledExecutorService. scheduleAtFixedRate (new Runnable () {@ Override public void run () {If (file. lastModified ()> lastTime) {System. out. println ("file update! Time: "+ file. lastModified (); lastTime = file. lastModified () ;}}, 0, 1, TimeUnit. SECONDS); try {Thread. sleep (1000*60);} catch (InterruptedException e) {e. printStackTrace ();}}}

The above is a very simple and basic implementation that can basically meet our needs. What is the problem with this implementation?

What if an exception occurs during scheduled task execution?

Slightly modify the above Code

public class FileUpTest {  private long lastTime;  private void ttt() {    throw new NullPointerException();  }  @Test  public void testFileUpdate() {    File file = new File("/tmp/alarmConfig");    lastTime = file.lastModified();    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);    scheduledExecutorService.scheduleAtFixedRate(new Runnable() {      @Override      public void run() {        if (file.lastModified() > lastTime) {          System.out.println("file update! time : " + file.lastModified());          lastTime = file.lastModified();          ttt();        }      }    }, 0, 1, TimeUnit.SECONDS);    try {      Thread.sleep(1000 * 60 * 10);    } catch (InterruptedException e) {      e.printStackTrace();    }  }}

In actual tests, it is found that the above Code is triggered only when the code is modified for the first time, but the modified Code is no longer effective. That is, after an exception is thrown, the scheduled task will no longer be executed, this problem is mainly caused by ScheduledExecutorService.

View the source code comment of ScheduledExecutorService.

If any execution of the task encounters an exception, subsequent executions are suppressed. otherwise, the task will only terminate via cancellation or termination of the executor. that is, if an exception occurs during the execution of the scheduled task, the subsequent task will not be executed.

Therefore, when using this posture, you must ensure that your task will not throw an exception, or you will not be able to play it later.

The corresponding solution is also relatively simple, just catch it

III. Advanced Edition

The above is a basic implementation version. Of course, in the java circle, many common requirements can be found to use corresponding open-source tools. Of course, this is no exception, we should also Compare apache series.

First, maven dependency

<dependency>  <groupId>commons-io</groupId>  <artifactId>commons-io</artifactId>  <version>2.6</version></dependency>

This tool mainly uses the FileAlterationObserver, FileAlterationListener, and FileAlterationMonitor classes to implement related requirements. Of course, it is easy to use, so it is not clear how to explain it, let's take a look at the code copied from quick-alarm, an open-source project of mine.

Public class PropertiesConfListenerHelper {public static boolean registerConfChangeListener (File file, Function <File, Map <String, AlarmConfig> func) {try {// polling interval 5 seconds long interval = TimeUnit. SECONDS. toMillis (5); // because the listener is based on a directory, you can directly obtain the File's root directory file dir = File. getParentFile (); // create a file observer to filter FileAlterationObserver observer = new FileAlterationObserver (dir, FileFilterUtils. and (FileFilterUtils. FileFileFilter (), FileFilterUtils. nameFileFilter (file. getName (); // sets the object change listener observer. addListener (new MyFileListener (func); FileAlterationMonitor monitor = new FileAlterationMonitor (interval, observer); monitor. start (); return true;} catch (Exception e) {log. error ("register properties change listener error! E: {} ", e); return false;} static final class MyFileListener extends FileAlterationListenerAdaptor {private Function <File, Map <String, AlarmConfig> func; public MyFileListener (Function <File, Map <String, AlarmConfig> func) {this. func = func ;}@ Override public void onFileChange (File file) {Map <String, AlarmConfig> ans = func. apply (file); // If loading fails, print a log. warn ("PropertiesConfig changed! Reload ans: {}", ans );}}}

For the above implementation, we will briefly describe the following points:

  1. This file listening is based on the directory, and then you can set a filter to implement the listening of corresponding file changes.
  2. For example, in the above registerConfChangeListener method, the imported file is a specific configuration file. Therefore, when constructing the parameter, the directory is removed, and the file name is removed as a filter.
  3. The second parameter is the jdk8 syntax, in which the content of the configuration file is read and mapped to the corresponding object.

A problem: what if an exception is thrown during execution of the func method?

The actual test result is the same as the above. After an exception is thrown, you still have to note that do not run the exception

Let's take a brief look at the above implementation logic and deduct the core module directly.

public void run() {  while(true) {    if(this.running) {      Iterator var1 = this.observers.iterator();      while(var1.hasNext()) {        FileAlterationObserver observer = (FileAlterationObserver)var1.next();        observer.checkAndNotify();      }      if(this.running) {        try {          Thread.sleep(this.interval);        } catch (InterruptedException var3) {          ;        }        continue;      }    }    return;  }}

Basically, we can see from the above that the entire implementation logic is different from the method of our first scheduled task. Here, we use threads and endless loops directly and use sleep internally to suspend the task, so when an exception occurs, it is equivalent to throwing it directly, and the thread will kneel down.

Supplement JDK version

Jdk1.7 provides a WatchService, which can also be used to monitor file changes. I have never touched it before, so that I can know about it. Then I searched for usage and found it quite simple, we can see that some blog posts show that they are event-driven and more efficient. The following is a simple demo.

@ Testpublic void testFileUpWather () throws IOException {// description. The listener must be in the Path path = Paths. get ("/tmp"); WatchService watcher = FileSystems. getDefault (). newWatchService (); path. register (watcher, ENTRY_MODIFY); new Thread ()-> {try {while (true) {WatchKey key = watcher. take (); for (WatchEvent <?> Event: key. pollEvents () {if (event. kind () = OVERFLOW) {// the event may be lost or discarded continue;} Path fileName = (Path) event. context (); System. out. println ("file update:" + fileName);} if (! Key. reset () {// reset WatchKey break; }}} catch (Exception e) {e. printStackTrace ();}}). start (); try {Thread. sleep (1000*60*10);} catch (InterruptedException e) {e. printStackTrace ();}}

IV. Summary

Using Java to monitor configuration file changes involves two main points.

  1. Round robin: Timer (Timer, ScheduledExecutorService), thread endless loop + sleep
  2. File Modification: File # lastModified

In general, this implementation is relatively simple. No matter whether it is a custom implementation or dependent on commos-io, there is not much technical cost, but it should be noted that:

  1. Do not throw an exception in the callback Method for scheduled tasks or file changes !!!

To avoid this problem, we can implement Asynchronous Message notification using EventBus. When the file changes, we can send a message, then, add a @ Subscribe annotation to the specific method of re-loading the file content. This not only achieves decoupling, it also avoids service exceptions caused by exceptions (if you are interested in this implementation, you can comment)

The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.