001/* 002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bstefanescu 018 * 019 * $Id$ 020 */ 021 022package org.nuxeo.runtime.deploy; 023 024import java.io.File; 025import java.io.IOException; 026import java.util.Hashtable; 027import java.util.Timer; 028import java.util.TimerTask; 029import java.util.Map; 030 031import org.nuxeo.common.collections.ListenerList; 032 033/** 034 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 035 */ 036public class FileChangeNotifier { 037 038 private final ListenerList listeners = new ListenerList(); 039 040 private final Timer timer = new Timer("FileChangeNotifier"); 041 042 private final Map<String, FileEntry> files = new Hashtable<String, FileEntry>(); 043 044 public void start(int startAfter, int interval) { 045 timer.scheduleAtFixedRate(new WatchTask(), startAfter, interval); 046 } 047 048 public void start() { 049 start(10000, 2000); 050 } 051 052 public void stop() { 053 timer.cancel(); 054 timer.purge(); 055 } 056 057 public String watch(File file) throws IOException { 058 FileEntry entry = new FileEntry(file); 059 files.put(entry.id, entry); 060 return entry.id; 061 } 062 063 public String watch(String id, File file) throws IOException { 064 files.put(id, new FileEntry(id, file)); 065 return id; 066 } 067 068 public void unwatch(File file) throws IOException { 069 files.remove(new FileEntry(file).id); 070 } 071 072 public void unwatch(String id, File file) throws IOException { 073 files.remove(new FileEntry(id, file).id); 074 } 075 076 public void addListener(FileChangeListener listener) { 077 listeners.add(listener); 078 } 079 080 public void removeListener(FileChangeListener listener) { 081 listeners.remove(listener); 082 } 083 084 protected void fireNotification(FileEntry entry) { 085 long tm = System.currentTimeMillis(); 086 for (Object listener : listeners.getListeners()) { 087 ((FileChangeListener) listener).fileChanged(entry, tm); 088 } 089 } 090 091 class WatchTask extends TimerTask { 092 @Override 093 public void run() { 094 // make a copy to avoid concurrent modifs if a listener is 095 // unwatching a file 096 FileEntry[] entries = files.values().toArray(new FileEntry[files.size()]); 097 for (FileEntry entry : entries) { 098 long lastModified = entry.file.lastModified(); 099 if (entry.lastModified < lastModified) { 100 fireNotification(entry); 101 entry.lastModified = lastModified; 102 } 103 } 104 } 105 } 106 107 public class FileEntry { 108 public final String id; 109 110 public final File file; 111 112 public long lastModified; 113 114 FileEntry(String id, File file) throws IOException { 115 this.file = file.getCanonicalFile(); 116 lastModified = file.lastModified(); 117 this.id = id == null ? file.getAbsolutePath() : id; 118 } 119 120 FileEntry(File file) throws IOException { 121 this(null, file); 122 } 123 124 @Override 125 public boolean equals(Object obj) { 126 if (obj == null) { 127 return false; 128 } 129 if (obj.getClass() == FileEntry.class) { 130 return id.equals(((FileEntry) obj).id); 131 } 132 return false; 133 } 134 135 @Override 136 public int hashCode() { 137 return id != null ? id.hashCode() : 0; 138 } 139 140 @Override 141 public String toString() { 142 return id; 143 } 144 } 145 146}