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.listeners;
019    
020    import java.io.File;
021    import java.util.ArrayList;
022    import java.util.Collection;
023    
024    import org.apache.commons.jci.monitor.FilesystemAlterationListener;
025    import org.apache.commons.jci.monitor.FilesystemAlterationObserver;
026    import org.apache.commons.logging.Log;
027    import org.apache.commons.logging.LogFactory;
028    
029    /**
030     * AbstractFilesystemAlterationListener provides some convenience methods helping to
031     * implement a FilesystemAlterationListener.
032     * @author tcurdt
033     */
034    public abstract class AbstractFilesystemAlterationListener implements FilesystemAlterationListener {
035    
036        private final Log log = LogFactory.getLog(AbstractFilesystemAlterationListener.class);
037    
038        private final Collection<File> createdFiles = new ArrayList<File>();
039        private final Collection<File> changedFiles = new ArrayList<File>();
040        private final Collection<File> deletedFiles = new ArrayList<File>();
041        private final Collection<File> createdDirectories = new ArrayList<File>();
042        private final Collection<File> changedDirectories = new ArrayList<File>();
043        private final Collection<File> deletedDirectories = new ArrayList<File>();
044    
045        
046        private final static class Signal {
047            public boolean triggered;
048        }
049    
050        private final Signal eventSignal = new Signal();
051        private final Signal checkSignal = new Signal();
052        
053        protected FilesystemAlterationObserver observer;
054    
055        public void onDirectoryCreate( final File pDir ) {
056            createdDirectories.add(pDir);
057        }
058        public void onDirectoryChange( final File pDir ) {
059            changedDirectories.add(pDir);
060        }
061        public void onDirectoryDelete( final File pDir ) {
062            deletedDirectories.add(pDir);
063        }
064    
065        public void onFileCreate( final File pFile) {
066            createdFiles.add(pFile);
067        }
068        public void onFileChange( final File pFile ) {
069            changedFiles.add(pFile);
070        }
071        public void onFileDelete( final File pFile ) {
072            deletedFiles.add(pFile);
073        }
074    
075    
076        public Collection<File> getChangedDirectories() {
077            return changedDirectories;
078        }
079    
080        public Collection<File> getChangedFiles() {
081            return changedFiles;
082        }
083    
084        public Collection<File> getCreatedDirectories() {
085            return createdDirectories;
086        }
087    
088        public Collection<File> getCreatedFiles() {
089            return createdFiles;
090        }
091    
092        public Collection<File> getDeletedDirectories() {
093            return deletedDirectories;
094        }
095    
096        public Collection<File> getDeletedFiles() {
097            return deletedFiles;
098        }
099    
100        protected void signals() {
101            if (createdFiles.size() > 0 || createdDirectories.size() > 0 ||
102                changedFiles.size() > 0 || changedDirectories.size() > 0 ||
103                deletedFiles.size() > 0 || deletedDirectories.size() > 0) {
104    
105                log.debug("event signal");
106    
107                synchronized(eventSignal) {
108                    eventSignal.triggered = true;
109                    eventSignal.notifyAll();
110                }
111            }
112    
113            log.debug("check signal");
114    
115            synchronized(checkSignal) {
116                checkSignal.triggered = true;
117                checkSignal.notifyAll();
118            }
119        }
120    
121        public void onStart( final FilesystemAlterationObserver pObserver ) {
122            observer = pObserver;
123    
124            createdFiles.clear();
125            changedFiles.clear();
126            deletedFiles.clear();
127            createdDirectories.clear();
128            changedDirectories.clear();
129            deletedDirectories.clear();
130        }
131    
132        public void onStop( final FilesystemAlterationObserver pObserver ) {
133            signals();
134            observer = null;
135        }
136            
137        public void waitForEvent() throws Exception {
138            synchronized(eventSignal) {
139                eventSignal.triggered = false;
140            }
141            log.debug("waiting for change");
142            if (!waitForSignal(eventSignal, 10)) {
143                throw new Exception("timeout");
144            }
145        }
146        
147        /**
148         * we don't reset the signal so if there was a check it is
149         * already true and exit immediatly otherwise it will behave just
150         * like waitForCheck()
151         * 
152         * @throws Exception in case of a timeout
153         */
154        public void waitForFirstCheck() throws Exception {
155            log.debug("waiting for first check");
156            if (!waitForSignal(checkSignal, 10)) {
157                throw new Exception("timeout");
158            }        
159        }
160    
161        /**
162         * wait for the next filesystem check to happen
163         * 
164         * @throws Exception in case of a timeout
165         */
166        public void waitForCheck() throws Exception {
167            synchronized(checkSignal) {
168                checkSignal.triggered = false;
169            }
170            log.debug("waiting for check");
171            if (!waitForSignal(checkSignal, 10)) {
172                throw new Exception("timeout");
173            }
174        }
175        
176        private boolean waitForSignal(final Signal pSignal, final int pSecondsTimeout) {
177            int i = 0;
178            while(true) {
179                synchronized(pSignal) {
180                    if (!pSignal.triggered) {
181                        try {
182                            pSignal.wait(1000);
183                        } catch (InterruptedException e) {
184                        }
185    
186                        if (++i > pSecondsTimeout) {
187                            log.error("timeout after " + pSecondsTimeout + "s");
188                            return false;
189                        }
190                        
191                    } else {
192                        pSignal.triggered = false;
193                        break;
194                    }
195                }
196            }        
197            return true;
198        }
199    
200    }