Home > Back-end >  Why would this hashmap be empty when it has values from constructor
Why would this hashmap be empty when it has values from constructor

Time:12-12

I am attempting to create a config handler that will automatically parse the updated config or remove the config if deleted. The watcher itself is listed below. The issue I am having is with the files HashMap. In my constructor I check for already existing configs and add it to the hash map. This section is fine. However, when the timer executes and calls the run() method the statement: Long storedModified = files.get(f); is null causing every file to be an 'add' action.

public abstract class DirWatcher extends TimerTask {

    private final File folder;
    public HashMap<File, Long> files = new HashMap<>(); //Keeps track of modified time

    public DirWatcher(String path) {
        this.folder = new File(path);
        System.out.println("Watching files on path: "   path);
        //gets initial files
        File[] startingFiles = this.folder.listFiles(file -> file.getName().endsWith(".json"));

        if(startingFiles == null || startingFiles.length < 1) return;

        for (File file : startingFiles) {
            System.out.println("Starting: File is "   file.getName());
            files.put(file, file.lastModified());
        }
        System.out.println(files); //Has values

    }

    public final void run() {
        System.out.println(files); // = {} but should have those values from constructor
        HashSet<File> checkedFiles = new HashSet<>(); //This is to check for deleted files.
        for(File f : getConfigFiles()) { //Check the dir because there could be new files
            Long storedModified = files.get(f); //see if we currently track it.
            checkedFiles.add(f);
            if(storedModified == null) { //if not, it is a new file.
                files.put(f, f.lastModified());
                onUpdate(f, "add");
            }
            else if(storedModified != f.lastModified()) { //It is an update to the file
                files.put(f, f.lastModified()); //add it to tracker
                onUpdate(f, "modified");
            }
        }
        //Now check for deleted files. We can remove it from files hashmap and stop tracking.
        Set<File> ref = files.keySet();
        ref.removeAll(checkedFiles);
        for (File deletedFile : ref) {
            files.remove(deletedFile); //remove from tracking.
            onUpdate(deletedFile, "delete");

        }
    }
public File[] getConfigFiles() {
        return folder.listFiles(file -> file.getName().endsWith(".json"));
    }
}

Implementation: Note that files here actually prints the config files it has found so far.

public ConfigHandler(Instance instance) { //Instance just gets me the folder.
    this.configDir = instance.getDataFolder();
    this.path = this.configDir.getAbsolutePath();
    System.out.println(this.configDir);

    TimerTask configWatch = new DirWatcher(this.path) {
        @Override
        protected void onUpdate(File file, String action) {
            System.out.println("[FILE WATCHER] Found updated file with action:"   action   ". File is "   file.getName());
            System.out.println(files);
        }
    };
    Timer timer = new Timer();
    timer.schedule(configWatch, new Date(), 5000);

}

I have been logging values at various points but I still don't understand why this hashmap is empty when my run() method is called.


EDIT: moved checking for deleted files outside of the for loop & add getConfigFiles method.

CodePudding user response:

It maybe a multi-threading question.
The class Timer has a field called thread, and the code is private final TimerThread thread = new TimerThread(queue); If you are using the class java.util.Timer.

In your code, timer.schedule(configWatch, new Date(), 5000); means do first call immediately. This is a synchronization problem, you can try this: private HashMap<File, Long> files = new ConcurrentHashMap<>();
ConcurrentHashMap is a thread-safe Map.

This is another way to watch file changes: Watching a Directory for Changes in Java
It's also need another thread to do this job.

And, you can design a command to allow user reload config file manually.

Update

After I review your log, I found this:

[21:24:12 INFO]: [STDOUT] [RUN]: {C:\Users\user\Desktop\1.19 paper\plugins\HeadHunting\Untitled-1.json=1670799307531} in me.sam.Config.ConfigHandler$1@3f7c8e5b
[21:24:17 INFO]: [STDOUT] [RUN]: {} in me.sam.Config.ConfigHandler$1@3f7c8e5b

In first run, the files field has value, in second run, it's empty.
I think, the problem is here.

//Now check for deleted files. We can remove it from files hashmap and stop tracking.
        Set<File> ref = files.keySet();
        ref.removeAll(checkedFiles);
        for (File deletedFile : ref) {
            files.remove(deletedFile); //remove from tracking.
            onUpdate(deletedFile, "delete");

        }

Here is some code from jdk, java.util.HashMap

    public Set<K> keySet() {
        Set<K> ks = keySet;
        if (ks == null) {
            ks = new KeySet();
            keySet = ks;
        }
        return ks;
    }

    // and the KeySet.remove method code
    public final boolean remove(Object key) {
        return removeNode(hash(key), key, null, false, true) != null;
    }

I think, maybe you should do like this.

Set<File> ref = new HashSet<>(files.keySet());
ref.removeAll(checkedFiles);
// ...
  •  Tags:  
  • java
  • Related