| /******************************************************************************* |
| * Copyright (c) 2007, 2008 aQute and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * aQute - initial implementation and ideas |
| * IBM Corporation - initial adaptation to Equinox provisioning use |
| *******************************************************************************/ |
| package org.eclipse.equinox.internal.provisional.p2.directorywatcher; |
| |
| import java.io.File; |
| import java.util.*; |
| import org.eclipse.osgi.util.NLS; |
| import org.osgi.framework.BundleContext; |
| |
| public class DirectoryWatcher { |
| private static final String DEL_EXT = ".del"; //$NON-NLS-1$ |
| |
| public class WatcherThread extends Thread { |
| |
| private final long pollFrequency; |
| private boolean done = false; |
| |
| public WatcherThread(long pollFrequency) { |
| super("Directory Watcher"); //$NON-NLS-1$ |
| this.pollFrequency = pollFrequency; |
| } |
| |
| public void run() { |
| do { |
| try { |
| poll(); |
| synchronized (this) { |
| wait(pollFrequency); |
| } |
| } catch (InterruptedException e) { |
| // ignore |
| } catch (Throwable e) { |
| log(Messages.error_main_loop, e); |
| done = true; |
| } |
| } while (!done); |
| } |
| |
| public synchronized void done() { |
| done = true; |
| notify(); |
| } |
| } |
| |
| public final static String POLL = "eclipse.p2.directory.watcher.poll"; //$NON-NLS-1$ |
| public final static String DIR = "eclipse.p2.directory.watcher.dir"; //$NON-NLS-1$ |
| private static final long DEFAULT_POLL_FREQUENCY = 2000; |
| |
| public static void log(String string, Throwable e) { |
| System.err.println(string + ": " + e); //$NON-NLS-1$ |
| } |
| |
| final File[] directories; |
| |
| long poll = 2000; |
| private Set listeners = new HashSet(); |
| private HashSet scannedFiles = new HashSet(); |
| private HashSet removals; |
| private Set pendingDeletions; |
| private WatcherThread watcher; |
| |
| public DirectoryWatcher(Dictionary properties, BundleContext context) { |
| String dir = (String) properties.get(DIR); |
| if (dir == null) |
| dir = "./load"; //$NON-NLS-1$ |
| |
| File targetDirectory = new File(dir); |
| targetDirectory.mkdirs(); |
| directories = new File[] {targetDirectory}; |
| } |
| |
| public DirectoryWatcher(File directory) { |
| if (directory == null) |
| throw new IllegalArgumentException(Messages.null_folder); |
| |
| this.directories = new File[] {directory}; |
| } |
| |
| public DirectoryWatcher(File[] directories) { |
| if (directories == null) |
| throw new IllegalArgumentException(Messages.null_folder); |
| this.directories = directories; |
| } |
| |
| public synchronized void addListener(DirectoryChangeListener listener) { |
| listeners.add(listener); |
| } |
| |
| public synchronized void removeListener(DirectoryChangeListener listener) { |
| listeners.remove(listener); |
| } |
| |
| public void start() { |
| start(DEFAULT_POLL_FREQUENCY); |
| } |
| |
| public synchronized void poll() { |
| startPoll(); |
| scanDirectories(); |
| stopPoll(); |
| } |
| |
| public synchronized void start(final long pollFrequency) { |
| if (watcher != null) |
| throw new IllegalStateException(Messages.thread_started); |
| |
| watcher = new WatcherThread(pollFrequency); |
| watcher.start(); |
| } |
| |
| public synchronized void stop() { |
| if (watcher == null) |
| throw new IllegalStateException(Messages.thread_not_started); |
| |
| watcher.done(); |
| watcher = null; |
| } |
| |
| public File[] getDirectories() { |
| return directories; |
| } |
| |
| private void startPoll() { |
| removals = scannedFiles; |
| scannedFiles = new HashSet(); |
| pendingDeletions = new HashSet(); |
| for (Iterator i = listeners.iterator(); i.hasNext();) |
| ((DirectoryChangeListener) i.next()).startPoll(); |
| } |
| |
| private void scanDirectories() { |
| for (int index = 0; index < directories.length; index++) { |
| File directory = directories[index]; |
| File list[] = directory.listFiles(); |
| if (list == null) |
| continue; |
| for (int i = 0; i < list.length; i++) { |
| File file = list[i]; |
| // if this is a deletion marker then add to the list of pending deletions. |
| if (list[i].getPath().endsWith(DEL_EXT)) { |
| File target = new File(file.getPath().substring(0, file.getPath().length() - 4)); |
| removals.add(target); |
| pendingDeletions.add(target); |
| } else { |
| // else remember that we saw the file and remove it from this list of files to be |
| // removed at the end. Then notify all the listeners as needed. |
| scannedFiles.add(file); |
| removals.remove(file); |
| for (Iterator iterator = listeners.iterator(); iterator.hasNext();) { |
| DirectoryChangeListener listener = (DirectoryChangeListener) iterator.next(); |
| if (isInterested(listener, file)) |
| processFile(file, listener); |
| } |
| } |
| } |
| } |
| } |
| |
| private void stopPoll() { |
| notifyRemovals(); |
| removals = scannedFiles; |
| for (Iterator i = listeners.iterator(); i.hasNext();) |
| ((DirectoryChangeListener) i.next()).stopPoll(); |
| processPendingDeletions(); |
| } |
| |
| private boolean isInterested(DirectoryChangeListener listener, File file) { |
| return listener.isInterested(file); |
| } |
| |
| /** |
| * Notify the listeners of the files that have been deleted or marked for deletion. |
| */ |
| private void notifyRemovals() { |
| Set removed = removals; |
| for (Iterator i = listeners.iterator(); i.hasNext();) { |
| DirectoryChangeListener listener = (DirectoryChangeListener) i.next(); |
| for (Iterator j = removed.iterator(); j.hasNext();) { |
| File file = (File) j.next(); |
| if (isInterested(listener, file)) |
| listener.removed(file); |
| } |
| } |
| } |
| |
| private void processFile(File file, DirectoryChangeListener listener) { |
| try { |
| Long oldTimestamp = listener.getSeenFile(file); |
| if (oldTimestamp == null) { |
| // The file is new |
| listener.added(file); |
| } else { |
| // The file is not new but may have changed |
| long lastModified = file.lastModified(); |
| if (oldTimestamp.longValue() != lastModified) |
| listener.changed(file); |
| } |
| } catch (Exception e) { |
| log(NLS.bind(Messages.error_processing, listener), e); |
| } |
| } |
| |
| /** |
| * Try to remove the files that have been marked for deletion. |
| */ |
| private void processPendingDeletions() { |
| for (Iterator iterator = pendingDeletions.iterator(); iterator.hasNext();) { |
| File file = (File) iterator.next(); |
| if (!file.exists() || file.delete()) |
| iterator.remove(); |
| new File(file.getPath() + DEL_EXT).delete(); |
| } |
| } |
| |
| } |