1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.io.monitor;
18
19 import java.time.Duration;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.List;
24 import java.util.Optional;
25 import java.util.concurrent.CopyOnWriteArrayList;
26 import java.util.concurrent.ThreadFactory;
27 import java.util.stream.Stream;
28
29 import org.apache.commons.io.ThreadUtils;
30
31
32
33
34
35
36
37
38 public final class FileAlterationMonitor implements Runnable {
39
40 private static final FileAlterationObserver[] EMPTY_ARRAY = {};
41
42 private final long intervalMillis;
43 private final List<FileAlterationObserver> observers = new CopyOnWriteArrayList<>();
44 private Thread thread;
45 private ThreadFactory threadFactory;
46 private volatile boolean running;
47
48
49
50
51 public FileAlterationMonitor() {
52 this(10_000);
53 }
54
55
56
57
58
59
60
61 public FileAlterationMonitor(final long intervalMillis) {
62 this.intervalMillis = intervalMillis;
63 }
64
65
66
67
68
69
70
71
72
73 public FileAlterationMonitor(final long interval, final Collection<FileAlterationObserver> observers) {
74
75 this(interval,
76 Optional
77 .ofNullable(observers)
78 .orElse(Collections.emptyList())
79 .toArray(EMPTY_ARRAY)
80 );
81
82 }
83
84
85
86
87
88
89
90
91 public FileAlterationMonitor(final long interval, final FileAlterationObserver... observers) {
92 this(interval);
93 if (observers != null) {
94 Stream.of(observers).forEach(this::addObserver);
95 }
96 }
97
98
99
100
101
102
103 public void addObserver(final FileAlterationObserver observer) {
104 if (observer != null) {
105 observers.add(observer);
106 }
107 }
108
109
110
111
112
113
114 public long getInterval() {
115 return intervalMillis;
116 }
117
118
119
120
121
122
123
124 public Iterable<FileAlterationObserver> getObservers() {
125 return new ArrayList<>(observers);
126 }
127
128
129
130
131
132
133 public void removeObserver(final FileAlterationObserver observer) {
134 if (observer != null) {
135 observers.removeIf(observer::equals);
136 }
137 }
138
139
140
141
142 @Override
143 public void run() {
144 while (running) {
145 observers.forEach(FileAlterationObserver::checkAndNotify);
146 if (!running) {
147 break;
148 }
149 try {
150 ThreadUtils.sleep(Duration.ofMillis(intervalMillis));
151 } catch (final InterruptedException ignored) {
152
153 }
154 }
155 }
156
157
158
159
160
161
162 public synchronized void setThreadFactory(final ThreadFactory threadFactory) {
163 this.threadFactory = threadFactory;
164 }
165
166
167
168
169
170
171 public synchronized void start() throws Exception {
172 if (running) {
173 throw new IllegalStateException("Monitor is already running");
174 }
175 for (final FileAlterationObserver observer : observers) {
176 observer.initialize();
177 }
178 running = true;
179 if (threadFactory != null) {
180 thread = threadFactory.newThread(this);
181 } else {
182 thread = new Thread(this);
183 }
184 thread.start();
185 }
186
187
188
189
190
191
192 public synchronized void stop() throws Exception {
193 stop(intervalMillis);
194 }
195
196
197
198
199
200
201
202
203
204 public synchronized void stop(final long stopInterval) throws Exception {
205 if (!running) {
206 throw new IllegalStateException("Monitor is not running");
207 }
208 running = false;
209 try {
210 thread.interrupt();
211 thread.join(stopInterval);
212 } catch (final InterruptedException e) {
213 Thread.currentThread().interrupt();
214 }
215 for (final FileAlterationObserver observer : observers) {
216 observer.destroy();
217 }
218 }
219 }