package org.eclipse.jetty.policy; import java.io.File; import java.io.FileInputStream; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.jetty.policy.loader.DefaultPolicyLoader; import org.eclipse.jetty.util.Scanner; import org.eclipse.jetty.util.component.AbstractLifeCycle; /** * PolicyMonitor watches a directory for files ending in the *.policy extension, * loads them and detects when they change. PolicyGrants are peeped out the * onPolicyChange method to whoever is using this monitor. * */ public abstract class PolicyMonitor extends AbstractLifeCycle { /** * the directory to be scanned for policy files. */ private String _policyDirectory; /** * instance of the scanner that detects policy files */ private Scanner _scanner; /** * true if updates to policy grants will be pushed through the * onPolicyChange() method */ private boolean _reload = true; /** * scan interval in seconds for policy file changes */ private int _scanInterval = 1; /** * specialized listener enabling waitForScan() functionality */ private LatchScannerListener _scanningListener; /** * true if the scanner has completed one cycle. */ private boolean _initialized = false; /** * record of the number of scans that have been made */ private AtomicInteger _scanCount = new AtomicInteger(0); /** * empty constructor */ public PolicyMonitor() { } /** * construtor with a predetermined directory to monitor * * @param directory */ public PolicyMonitor( String directory ) { this(); _policyDirectory = directory; } /** * set the policy directory to scan on a non-running monitor * * @param directory */ public void setPolicyDirectory( String directory ) { if (isRunning()) { throw new PolicyException("policy monitor is running, unable to set policy directory"); } _policyDirectory = directory; } /** * gets the scanner interval * * @return the scan interval */ public int getScanInterval() { return _scanInterval; } /** * sets the scanner interval on a non-running instance of the monitor * * @param scanInterval in seconds * @see Scanner#setScanInterval(int) */ public void setScanInterval( int scanInterval ) { if (isRunning()) { throw new PolicyException("policy monitor is running, unable to set scan interval"); } _scanInterval = scanInterval; } /** * true of the monitor is initialized, meaning that at least one * scan cycle has completed and any policy grants found have been chirped * * @return true if initialized */ public boolean isInitialized() { return _initialized; } /** * gets the number of times the scan has been run * * @return scan count */ public int getScanCount() { return _scanCount.get(); } /** * initiates a scan and blocks until it has been completed * * @throws Exception */ public synchronized void waitForScan() throws Exception { // wait for 2 scans for stable files CountDownLatch latch = new CountDownLatch(2); _scanningListener.setScanningLatch(latch); _scanner.scan(); latch.await(); } /** * true of reload is enabled, false otherwise * * @return true if reload is enabled */ public boolean isReloadEnabled() { return _reload; } /** * sets the monitor to reload or not, but only if the monitor isn't already running * * TODO this doesn't really _have_ to be on a non-running monitor * * @param reload */ public void setReload(boolean reload) { if (isRunning()) { throw new PolicyException("policy monitor is running, unable to set reload at this time"); } _reload = reload; } /** * processes a policy file via the default policy loader and chirps * changes to the onPolicyChange() abstract method * * @param filename */ private void processPolicyFile(String filename) { try { File policyFile = new File(filename); Set<PolicyBlock> policyBlocks = DefaultPolicyLoader.load(new FileInputStream(policyFile),JettyPolicy.getContext()); for (PolicyBlock policy : policyBlocks) { onPolicyChange(policy); } } catch (Exception e) { e.printStackTrace(); } } /** * called by the abstract lifecycle to start the monitor */ @Override protected void doStart() throws Exception { super.doStart(); _scanner = new Scanner(); List<File> scanDirs = new ArrayList<File>(); scanDirs.add(new File( _policyDirectory ) ); //System.out.println("Scanning: " + _policyDirectory ); _scanner.addListener(new Scanner.DiscreteListener() { public void fileRemoved(String filename) throws Exception { } /* will trigger when files are changed, not on load time, just when changed */ public void fileChanged(String filename) throws Exception { if (_reload && filename.endsWith("policy")) { // System.out.println("PolicyMonitor: policy file"); processPolicyFile(filename); } } public void fileAdded(String filename) throws Exception { if (filename.endsWith("policy")) { // System.out.println("PolicyMonitor: added policy file"); processPolicyFile(filename); } } }); _scanningListener = new LatchScannerListener(); _scanner.addListener(_scanningListener); _scanner.setScanDirs(scanDirs); _scanner.setReportExistingFilesOnStartup(true); _scanner.start(); _scanner.setScanInterval(_scanInterval); } /** * called by the abstract life cycle to turn off the monitor */ @Override protected void doStop() throws Exception { super.doStop(); _scanner.stop(); } /** * latch listener that can taken in a countdownlatch and notify other * blocking threads that the scan has been completed * */ private class LatchScannerListener implements Scanner.ScanCycleListener { CountDownLatch _latch; public void scanStarted(int cycle) throws Exception { } public void scanEnded(int cycle) throws Exception { _initialized = true; // just really needed the first time _scanCount.incrementAndGet(); if ( _latch != null ) { _latch.countDown(); } } public void setScanningLatch( CountDownLatch latch ) { _latch = latch; } } /** * implemented by the user of the policy monitor to handle custom logic * related to the usage of the policy grant instance/s. * * @param grant */ public abstract void onPolicyChange(PolicyBlock grant); }