001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.jci.monitor;
019    
020    import java.io.File;
021    import java.util.Collections;
022    import java.util.HashMap;
023    import java.util.Map;
024    
025    import org.apache.commons.logging.Log;
026    import org.apache.commons.logging.LogFactory;
027    
028    /**
029     * It's a runnable that spawns of a monitoring thread triggering the
030     * the observers and managing the their listeners.
031     * 
032     * @author tcurdt
033     */
034    public final class FilesystemAlterationMonitor implements Runnable {
035    
036        private final Log log = LogFactory.getLog(FilesystemAlterationMonitor.class);
037    
038        private final Object observersLock = new Object();
039        private Map<File, FilesystemAlterationObserver> observers = Collections.unmodifiableMap(new HashMap<File, FilesystemAlterationObserver>());
040        
041        /** delay between calls to {@link FilesystemAlterationObserver#checkAndNotify()}, default 3000 ms */
042        private volatile long delay = 3000; // volatile because shared with daemon thread
043        private Thread thread = null;
044    
045        private volatile boolean running = true;
046            
047        public FilesystemAlterationMonitor() {
048        }
049    
050    
051        public void start() {
052            thread = new Thread(this);
053            thread.setName("Filesystem Alteration Monitor");
054            thread.setDaemon(true);
055            thread.start();
056        }
057    
058    
059        public void stop() {
060            running = false;
061    
062            if (thread != null) {
063                try {
064                    thread.join(delay);
065                } catch (InterruptedException e) {
066                }
067            }
068        }
069    
070    
071        /**
072         * Set the delay between calls to the observers.
073         *  
074         * @param pDelay the delay in milliseconds (default if not set 3000 ms)
075         */
076        public void setInterval( final long pDelay ) {
077            delay = pDelay;
078        }
079    
080    
081        public void addListener( final File pRoot, final FilesystemAlterationListener pListener ) {
082    
083            FilesystemAlterationObserver observer;
084    
085            synchronized (observersLock) {
086                observer = observers.get(pRoot);
087    
088                if (observer == null) {
089                    final Map<File, FilesystemAlterationObserver> newObservers = new HashMap<File, FilesystemAlterationObserver>(observers);
090                    observer = new FilesystemAlterationObserverImpl(pRoot);
091                    newObservers.put(pRoot, observer);
092                    observers = Collections.unmodifiableMap(newObservers);
093                }
094            }
095    
096            observer.addListener(pListener);
097        }
098       
099        public void removeListener( final FilesystemAlterationListener pListener ) {
100            synchronized (observersLock) {
101                for (FilesystemAlterationObserver observer : observers.values()) {
102                    observer.removeListener(pListener);
103                    // FIXME: remove observer if there are no listeners?
104                }
105            }
106        }
107    
108        public FilesystemAlterationListener[] getListenersFor( final File pRoot  ) {
109            final FilesystemAlterationObserver observer = observers.get(pRoot);
110    
111            if (observer == null) {
112                return new FilesystemAlterationListener[0];
113            }
114    
115            return observer.getListeners();
116        }
117    
118    
119        public void run() {
120            log.debug("fam running");
121            
122            while (running) {
123    
124                for (FilesystemAlterationObserver observer : observers.values()) {
125                    observer.checkAndNotify();
126                }
127    
128                try {
129                    Thread.sleep(delay);
130                } catch (final InterruptedException e) {
131                }
132            }
133            
134            log.debug("fam exiting");
135        }
136    
137    }