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