View Javadoc

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 }