| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.commons.jci.monitor; |
| |
| import java.io.File; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| /** |
| * Implementation of a FilesystemAlterationObserver |
| * |
| * @author tcurdt |
| */ |
| public class FilesystemAlterationObserverImpl implements FilesystemAlterationObserver { |
| |
| private final Log log = LogFactory.getLog(FilesystemAlterationObserverImpl.class); |
| |
| private interface MonitorFile { |
| |
| long lastModified(); |
| MonitorFile[] listFiles(); |
| boolean isDirectory(); |
| boolean exists(); |
| String getName(); |
| |
| } |
| |
| private final static class MonitorFileImpl implements MonitorFile { |
| |
| private final File file; |
| |
| public MonitorFileImpl( final File pFile ) { |
| file = pFile; |
| } |
| |
| public boolean exists() { |
| return file.exists(); |
| } |
| |
| public MonitorFile[] listFiles() { |
| final File[] children = file.listFiles(); |
| if (children == null) { // not a directory or IOError (e.g. protection issue) |
| return new MonitorFile[0]; |
| } |
| |
| final MonitorFile[] providers = new MonitorFile[children.length]; |
| for (int i = 0; i < providers.length; i++) { |
| providers[i] = new MonitorFileImpl(children[i]); |
| } |
| return providers; |
| } |
| |
| public String getName() { |
| return file.getName(); |
| } |
| |
| public boolean isDirectory() { |
| return file.isDirectory(); |
| } |
| |
| public long lastModified() { |
| return file.lastModified(); |
| } |
| |
| @Override |
| public String toString() { |
| return file.toString(); |
| } |
| |
| } |
| |
| private final class Entry { |
| |
| private final static int TYPE_UNKNOWN = 0; |
| private final static int TYPE_FILE = 1; |
| private final static int TYPE_DIRECTORY = 2; |
| |
| private final MonitorFile file; |
| private long lastModified = -1; |
| private int lastType = TYPE_UNKNOWN; |
| private Map<String, Entry> children = new HashMap<String, Entry>(); |
| |
| public Entry(final MonitorFile pFile) { |
| file = pFile; |
| } |
| |
| public String getName() { |
| return file.getName(); |
| } |
| |
| |
| @Override |
| public String toString() { |
| return file.toString(); |
| } |
| |
| |
| private void compareChildren() { |
| if (!file.isDirectory()) { |
| return; |
| } |
| |
| final MonitorFile[] files = file.listFiles(); |
| final Set<Entry> deleted = new HashSet<Entry>(children.values()); |
| for (MonitorFile f : files) { |
| final String name = f.getName(); |
| final Entry entry = children.get(name); |
| if (entry != null) { |
| // already recognized as child |
| deleted.remove(entry); |
| |
| if(entry.needsToBeDeleted()) { |
| // we have to delete this one |
| children.remove(name); |
| } |
| } else { |
| // a new child |
| final Entry newChild = new Entry(f); |
| children.put(name, newChild); |
| newChild.needsToBeDeleted(); |
| } |
| } |
| |
| // the ones not found on disk anymore |
| |
| for (Entry entry : deleted) { |
| entry.deleteChildrenAndNotify(); |
| children.remove(entry.getName()); |
| } |
| } |
| |
| |
| private void deleteChildrenAndNotify() { |
| for (Entry entry : children.values()) { |
| entry.deleteChildrenAndNotify(); |
| } |
| children.clear(); |
| |
| if(lastType == TYPE_DIRECTORY) { |
| notifyOnDirectoryDelete(this); |
| } else if (lastType == TYPE_FILE) { |
| notifyOnFileDelete(this); |
| } |
| } |
| |
| public boolean needsToBeDeleted() { |
| |
| if (!file.exists()) { |
| // deleted or has never existed yet |
| |
| // log.debug(file + " does not exist or has been deleted"); |
| |
| deleteChildrenAndNotify(); |
| |
| // mark to be deleted by parent |
| return true; |
| } else { |
| // exists |
| final long currentModified = file.lastModified(); |
| |
| if (currentModified != lastModified) { |
| // last modified has changed |
| lastModified = currentModified; |
| |
| // log.debug(file + " has new last modified"); |
| |
| // types only changes when also the last modified changes |
| final int newType = (file.isDirectory()?TYPE_DIRECTORY:TYPE_FILE); |
| |
| if (lastType != newType) { |
| // the type has changed |
| |
| // log.debug(file + " has a new type"); |
| |
| deleteChildrenAndNotify(); |
| |
| lastType = newType; |
| |
| // and then an add as the new type |
| |
| if (newType == TYPE_DIRECTORY) { |
| notifyOnDirectoryCreate(this); |
| compareChildren(); |
| } else { |
| notifyOnFileCreate(this); |
| } |
| |
| return false; |
| } |
| |
| if (newType == TYPE_DIRECTORY) { |
| notifyOnDirectoryChange(this); |
| compareChildren(); |
| } else { |
| notifyOnFileChange(this); |
| } |
| |
| return false; |
| |
| } else { |
| |
| // so exists and has not changed |
| |
| // log.debug(file + " does exist and has not changed"); |
| |
| compareChildren(); |
| |
| return false; |
| } |
| } |
| } |
| |
| public MonitorFile getFile() { |
| return file; |
| } |
| |
| public void markNotChanged() { |
| lastModified = file.lastModified(); |
| } |
| |
| } |
| |
| private final File rootDirectory; |
| private final Entry rootEntry; |
| |
| private FilesystemAlterationListener[] listeners = new FilesystemAlterationListener[0]; |
| private Set<FilesystemAlterationListener> listenersSet = new HashSet<FilesystemAlterationListener>(); |
| |
| public FilesystemAlterationObserverImpl( final File pRootDirectory ) { |
| rootDirectory = pRootDirectory; |
| rootEntry = new Entry(new MonitorFileImpl(pRootDirectory)); |
| } |
| |
| |
| |
| private void notifyOnStart() { |
| log.debug("onStart " + rootEntry); |
| for (FilesystemAlterationListener listener : listeners) { |
| listener.onStart(this); |
| } |
| } |
| private void notifyOnStop() { |
| log.debug("onStop " + rootEntry); |
| for (FilesystemAlterationListener listener : listeners) { |
| listener.onStop(this); |
| } |
| } |
| |
| private void notifyOnFileCreate( final Entry pEntry ) { |
| log.debug("onFileCreate " + pEntry); |
| for (FilesystemAlterationListener listener : listeners) { |
| listener.onFileCreate(((MonitorFileImpl)pEntry.getFile()).file ); |
| } |
| } |
| private void notifyOnFileChange( final Entry pEntry ) { |
| log.debug("onFileChange " + pEntry); |
| for (FilesystemAlterationListener listener : listeners) { |
| listener.onFileChange(((MonitorFileImpl)pEntry.getFile()).file ); |
| } |
| } |
| private void notifyOnFileDelete( final Entry pEntry ) { |
| log.debug("onFileDelete " + pEntry); |
| for (FilesystemAlterationListener listener : listeners) { |
| listener.onFileDelete(((MonitorFileImpl)pEntry.getFile()).file ); |
| } |
| } |
| |
| private void notifyOnDirectoryCreate( final Entry pEntry ) { |
| log.debug("onDirectoryCreate " + pEntry); |
| for (FilesystemAlterationListener listener : listeners) { |
| listener.onDirectoryCreate(((MonitorFileImpl)pEntry.getFile()).file ); |
| } |
| } |
| private void notifyOnDirectoryChange( final Entry pEntry ) { |
| log.debug("onDirectoryChange " + pEntry); |
| for (FilesystemAlterationListener listener : listeners) { |
| listener.onDirectoryChange(((MonitorFileImpl)pEntry.getFile()).file ); |
| } |
| } |
| private void notifyOnDirectoryDelete( final Entry pEntry ) { |
| log.debug("onDirectoryDelete " + pEntry); |
| for (FilesystemAlterationListener listener : listeners) { |
| listener.onDirectoryDelete(((MonitorFileImpl)pEntry.getFile()).file ); |
| } |
| } |
| |
| |
| private void checkEntries() { |
| if(rootEntry.needsToBeDeleted()) { |
| // root not existing |
| rootEntry.lastType = Entry.TYPE_UNKNOWN; |
| } |
| } |
| |
| |
| public void checkAndNotify() { |
| synchronized(listenersSet) { |
| if (listeners.length == 0) { |
| return; |
| } |
| |
| notifyOnStart(); |
| |
| checkEntries(); |
| |
| notifyOnStop(); |
| } |
| } |
| |
| |
| public File getRootDirectory() { |
| return rootDirectory; |
| } |
| |
| public void addListener( final FilesystemAlterationListener pListener ) { |
| synchronized(listenersSet) { |
| if (listenersSet.add(pListener)) { |
| listeners = createArrayFromSet(); |
| } |
| } |
| } |
| |
| public void removeListener( final FilesystemAlterationListener pListener ) { |
| synchronized(listenersSet) { |
| if (listenersSet.remove(pListener)) { |
| listeners = createArrayFromSet(); |
| } |
| } |
| } |
| |
| private FilesystemAlterationListener[] createArrayFromSet() { |
| final FilesystemAlterationListener[] newListeners = new FilesystemAlterationListener[listenersSet.size()]; |
| listenersSet.toArray(newListeners); |
| return newListeners; |
| } |
| |
| public FilesystemAlterationListener[] getListeners() { |
| synchronized(listenersSet) { |
| final FilesystemAlterationListener[] res = new FilesystemAlterationListener[listeners.length]; |
| System.arraycopy(listeners, 0, res, 0, res.length); |
| return res; |
| } |
| } |
| |
| /* |
| |
| public static void main( String[] args ) { |
| final FilesystemAlterationObserverImpl observer = new FilesystemAlterationObserverImpl(new File(args[0])); |
| while(true) { |
| observer.checkEntries(); |
| try { |
| Thread.sleep(1000); |
| } catch (InterruptedException e) { |
| } |
| } |
| } |
| |
| */ |
| } |