001/**
002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com).
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * http://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.jboot.utils;
017
018import java.io.File;
019import java.io.IOException;
020import java.util.HashMap;
021import java.util.Map;
022import java.util.Timer;
023import java.util.TimerTask;
024
025
026public abstract class FileScanner {
027
028    public static final String ACTION_INIT = "init";
029    public static final String ACTION_ADD = "add";
030    public static final String ACTION_DELETE = "delete";
031    public static final String ACTION_UPDATE = "update";
032
033    private Timer timer;
034    private TimerTask task;
035    private String rootDir;
036    private int interval;
037    private boolean running = false;
038    private boolean inited = false;
039
040    private final Map<String, TimeSize> preScan = new HashMap<>();
041    private final Map<String, TimeSize> curScan = new HashMap<>();
042
043    public FileScanner(String rootDir, int interval) {
044        if (rootDir == null) {
045            throw new IllegalArgumentException("The parameter rootDir can not be null.");
046        }
047        this.rootDir = rootDir;
048        if (interval <= 0) {
049            throw new IllegalArgumentException("The parameter interval must more than zero.");
050        }
051        this.interval = interval;
052    }
053
054    public abstract void onChange(String action, String file);
055
056    protected void working() {
057        if (!rootDir.contains(",")) {
058            scan(new File(rootDir.trim()));
059        } else {
060            String[] paths = rootDir.split(",");
061            for (String path : paths) {
062                scan(new File(path.trim()));
063            }
064        }
065
066        compare();
067
068        preScan.clear();
069        preScan.putAll(curScan);
070        curScan.clear();
071    }
072
073    protected void scan(File file) {
074        if (file == null || !file.exists()) {
075            return;
076        }
077
078        if (file.isFile()) {
079            try {
080                curScan.put(file.getCanonicalPath(), new TimeSize(file));
081            } catch (IOException e) {
082                e.printStackTrace();
083            }
084        } else if (file.isDirectory()) {
085            File[] fs = file.listFiles();
086            if (fs != null && fs.length > 0) {
087                for (File f : fs) {
088                    scan(f);
089                }
090            }
091        }
092    }
093
094    protected void compare() {
095
096        if (!inited) {
097            for (Map.Entry<String, TimeSize> entry : curScan.entrySet()) {
098                onChange(ACTION_INIT, entry.getKey());
099            }
100            inited = true;
101            return;
102        }
103
104        for (Map.Entry<String, TimeSize> entry : curScan.entrySet()) {
105            if (preScan.get(entry.getKey()) == null) {
106                onChange(ACTION_ADD, entry.getKey());
107            }
108        }
109
110        for (Map.Entry<String, TimeSize> entry : preScan.entrySet()) {
111            if (curScan.get(entry.getKey()) == null) {
112                onChange(ACTION_DELETE, entry.getKey());
113            }
114        }
115
116
117        for (Map.Entry<String, TimeSize> entry : curScan.entrySet()) {
118            TimeSize pre = preScan.get(entry.getKey());
119            if (pre != null && !pre.equals(entry.getValue())) {
120                onChange(ACTION_UPDATE, entry.getKey());
121            }
122        }
123
124    }
125
126    public void start() {
127        if (!running) {
128            timer = new Timer("Jboot-File-Scanner", true);
129            task = new TimerTask() {
130                public void run() {
131                    working();
132                }
133            };
134            timer.schedule(task, 0, 1000L * interval);
135            running = true;
136        }
137    }
138
139    public void stop() {
140        if (running) {
141            timer.cancel();
142            task.cancel();
143            running = false;
144        }
145    }
146
147    class TimeSize {
148
149        final long time;
150        final long size;
151
152        public TimeSize(File file) {
153            this.time = file.lastModified();
154            this.size = file.length();
155        }
156
157
158        @Override
159        public int hashCode() {
160            return (int) (time ^ size);
161        }
162
163        @Override
164        public boolean equals(Object o) {
165            if (o instanceof TimeSize) {
166                TimeSize ts = (TimeSize) o;
167                return ts.time == this.time && ts.size == this.size;
168            }
169            return false;
170        }
171
172        @Override
173        public String toString() {
174            return "TimeSize{" +
175                    "time=" + time +
176                    ", size=" + size +
177                    '}';
178        }
179    }
180}
181
182
183