1 //
2 // ========================================================================
3 // Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
8 //
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
11 //
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
14 //
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
17 //
18
19 package org.eclipse.jetty.policy;
20
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Set;
26 import java.util.concurrent.CountDownLatch;
27 import java.util.concurrent.atomic.AtomicInteger;
28
29 import org.eclipse.jetty.policy.loader.DefaultPolicyLoader;
30 import org.eclipse.jetty.util.Scanner;
31 import org.eclipse.jetty.util.component.AbstractLifeCycle;
32
33 /**
34 * PolicyMonitor watches a directory for files ending in the *.policy extension,
35 * loads them and detects when they change. PolicyGrants are peeped out the
36 * onPolicyChange method to whoever is using this monitor.
37 *
38 */
39 public abstract class PolicyMonitor extends AbstractLifeCycle
40 {
41
42 /**
43 * the directory to be scanned for policy files.
44 */
45 private String _policyDirectory;
46
47 /**
48 * instance of the scanner that detects policy files
49 */
50 private Scanner _scanner;
51
52 /**
53 * true if updates to policy grants will be pushed through the
54 * onPolicyChange() method
55 */
56 private boolean _reload = true;
57
58 /**
59 * scan interval in seconds for policy file changes
60 */
61 private int _scanInterval = 1;
62
63 /**
64 * specialized listener enabling waitForScan() functionality
65 */
66 private LatchScannerListener _scanningListener;
67
68 /**
69 * true if the scanner has completed one cycle.
70 */
71 private boolean _initialized = false;
72
73 /**
74 * record of the number of scans that have been made
75 */
76 private AtomicInteger _scanCount = new AtomicInteger(0);
77
78 /**
79 * empty constructor
80 */
81 public PolicyMonitor()
82 {
83
84 }
85
86 /**
87 * construtor with a predetermined directory to monitor
88 *
89 * @param directory
90 */
91 public PolicyMonitor( String directory )
92 {
93 this();
94 _policyDirectory = directory;
95 }
96
97 /**
98 * set the policy directory to scan on a non-running monitor
99 *
100 * @param directory
101 */
102 public void setPolicyDirectory( String directory )
103 {
104 if (isRunning())
105 {
106 throw new PolicyException("policy monitor is running, unable to set policy directory");
107 }
108
109 _policyDirectory = directory;
110 }
111
112 /**
113 * gets the scanner interval
114 *
115 * @return the scan interval
116 */
117 public int getScanInterval()
118 {
119 return _scanInterval;
120 }
121
122 /**
123 * sets the scanner interval on a non-running instance of the monitor
124 *
125 * @param scanInterval in seconds
126 * @see Scanner#setScanInterval(int)
127 */
128 public void setScanInterval( int scanInterval )
129 {
130 if (isRunning())
131 {
132 throw new PolicyException("policy monitor is running, unable to set scan interval");
133 }
134
135 _scanInterval = scanInterval;
136 }
137
138 /**
139 * true of the monitor is initialized, meaning that at least one
140 * scan cycle has completed and any policy grants found have been chirped
141 *
142 * @return true if initialized
143 */
144 public boolean isInitialized()
145 {
146 return _initialized;
147 }
148
149 /**
150 * gets the number of times the scan has been run
151 *
152 * @return scan count
153 */
154 public int getScanCount()
155 {
156 return _scanCount.get();
157 }
158
159 /**
160 * initiates a scan and blocks until it has been completed
161 *
162 * @throws Exception
163 */
164 public synchronized void waitForScan() throws Exception
165 {
166 // wait for 2 scans for stable files
167 CountDownLatch latch = new CountDownLatch(2);
168
169 _scanningListener.setScanningLatch(latch);
170 _scanner.scan();
171 latch.await();
172 }
173
174 /**
175 * true of reload is enabled, false otherwise
176 *
177 * @return true if reload is enabled
178 */
179 public boolean isReloadEnabled()
180 {
181 return _reload;
182 }
183
184 /**
185 * sets the monitor to reload or not, but only if the monitor isn't already running
186 *
187 * TODO this doesn't really _have_ to be on a non-running monitor
188 *
189 * @param reload
190 */
191 public void setReload(boolean reload)
192 {
193 if (isRunning())
194 {
195 throw new PolicyException("policy monitor is running, unable to set reload at this time");
196 }
197
198 _reload = reload;
199 }
200
201 /**
202 * processes a policy file via the default policy loader and chirps
203 * changes to the onPolicyChange() abstract method
204 *
205 * @param filename
206 */
207 private void processPolicyFile(String filename)
208 {
209 try
210 {
211 File policyFile = new File(filename);
212
213 Set<PolicyBlock> policyBlocks = DefaultPolicyLoader.load(new FileInputStream(policyFile),JettyPolicy.getContext());
214
215 for (PolicyBlock policy : policyBlocks)
216 {
217 onPolicyChange(policy);
218 }
219 }
220 catch (Exception e)
221 {
222 e.printStackTrace();
223 }
224 }
225
226 /**
227 * called by the abstract lifecycle to start the monitor
228 */
229 @Override
230 protected void doStart() throws Exception
231 {
232 super.doStart();
233
234 _scanner = new Scanner();
235
236 List<File> scanDirs = new ArrayList<File>();
237
238 scanDirs.add(new File( _policyDirectory ) );
239
240 //System.out.println("Scanning: " + _policyDirectory );
241
242 _scanner.addListener(new Scanner.DiscreteListener()
243 {
244
245 public void fileRemoved(String filename) throws Exception
246 {
247
248 }
249
250 /* will trigger when files are changed, not on load time, just when changed */
251 public void fileChanged(String filename) throws Exception
252 {
253 if (_reload && filename.endsWith("policy"))
254 {
255 // System.out.println("PolicyMonitor: policy file");
256 processPolicyFile(filename);
257 }
258 }
259
260 public void fileAdded(String filename) throws Exception
261 {
262 if (filename.endsWith("policy"))
263 {
264 // System.out.println("PolicyMonitor: added policy file");
265 processPolicyFile(filename);
266 }
267 }
268 });
269
270 _scanningListener = new LatchScannerListener();
271
272 _scanner.addListener(_scanningListener);
273
274 _scanner.setScanDirs(scanDirs);
275 _scanner.setReportExistingFilesOnStartup(true);
276 _scanner.start();
277 _scanner.setScanInterval(_scanInterval);
278 }
279
280 /**
281 * called by the abstract life cycle to turn off the monitor
282 */
283 @Override
284 protected void doStop() throws Exception
285 {
286 super.doStop();
287
288 _scanner.stop();
289 }
290
291 /**
292 * latch listener that can taken in a countdownlatch and notify other
293 * blocking threads that the scan has been completed
294 *
295 */
296 private class LatchScannerListener implements Scanner.ScanCycleListener
297 {
298 CountDownLatch _latch;
299
300 public void scanStarted(int cycle) throws Exception
301 {
302
303 }
304
305 public void scanEnded(int cycle) throws Exception
306 {
307 _initialized = true; // just really needed the first time
308 _scanCount.incrementAndGet();
309 if ( _latch != null )
310 {
311 _latch.countDown();
312 }
313 }
314
315 public void setScanningLatch( CountDownLatch latch )
316 {
317 _latch = latch;
318 }
319 }
320
321 /**
322 * implemented by the user of the policy monitor to handle custom logic
323 * related to the usage of the policy grant instance/s.
324 *
325 * @param grant
326 */
327 public abstract void onPolicyChange(PolicyBlock grant);
328 }