| /* |
| * 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.felix.fileinstall.internal; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.nio.file.FileSystems; |
| import java.nio.file.Path; |
| import java.nio.file.PathMatcher; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Set; |
| |
| import org.osgi.framework.BundleContext; |
| |
| public class WatcherScanner extends Scanner { |
| |
| BundleContext bundleContext; |
| PathMatcher fileMatcher; |
| Watcher watcher; |
| |
| Set<File> changed = new HashSet<File>(); |
| |
| /** |
| * Create a scanner for the specified directory and file filter |
| * |
| * @param directory the directory to scan |
| * @param filterString a filter for file names |
| * @param subdirMode to use when scanning |
| */ |
| public WatcherScanner(BundleContext bundleContext, File directory, String filterString, String subdirMode) throws IOException { |
| super(directory, filterString, subdirMode); |
| this.bundleContext = bundleContext; |
| if (filterString != null) { |
| this.fileMatcher = FileSystems.getDefault().getPathMatcher("regex:" + filterString); |
| } else { |
| this.fileMatcher = null; |
| } |
| this.watcher = new ScannerWatcher(); |
| this.watcher.setFileMatcher(fileMatcher); |
| this.watcher.setRootDirectory(this.directory); |
| this.watcher.init(); |
| this.watcher.rescan(); |
| } |
| |
| public Set<File> scan(boolean reportImmediately) { |
| watcher.processEvents(); |
| synchronized (changed) { |
| if (changed.isEmpty()) { |
| return new HashSet<File>(); |
| } |
| Set<File> files = new HashSet<File>(); |
| Set<File> removed = new HashSet<File>(); |
| if (reportImmediately) { |
| removed.addAll(storedChecksums.keySet()); |
| } |
| for (Iterator<File> iterator = changed.iterator(); iterator.hasNext(); ) { |
| File file = iterator.next(); |
| long lastChecksum = lastChecksums.get(file) != null ? (Long) lastChecksums.get(file) : 0; |
| long storedChecksum = storedChecksums.get(file) != null ? (Long) storedChecksums.get(file) : 0; |
| long newChecksum = checksum(file); |
| lastChecksums.put(file, newChecksum); |
| if (file.exists()) { |
| // Only handle file when it does not change anymore and it has changed since last reported |
| if ((newChecksum == lastChecksum || reportImmediately)) { |
| if (newChecksum != storedChecksum) { |
| storedChecksums.put(file, newChecksum); |
| files.add(file); |
| } else { |
| iterator.remove(); |
| } |
| if (reportImmediately) { |
| removed.remove(file); |
| } |
| } |
| } else { |
| if (!reportImmediately) { |
| removed.add(file); |
| } |
| } |
| } |
| for (File file : removed) { |
| // Make sure we'll handle a file that has been deleted |
| files.add(file); |
| // Remove no longer used checksums |
| lastChecksums.remove(file); |
| storedChecksums.remove(file); |
| changed.remove(file); |
| } |
| |
| return files; |
| } |
| } |
| |
| public void close() throws IOException { |
| watcher.close(); |
| } |
| |
| class ScannerWatcher extends Watcher { |
| |
| @Override |
| protected void process(Path path) { |
| File file = path.toFile(); |
| if (!file.getParentFile().equals(directory)) { |
| // File is in a sub directory. |
| if (skipSubdir) { |
| return; |
| } |
| if (jarSubdir) { |
| // Walk up until the first level sub-directory. |
| do { |
| file = file.getParentFile(); |
| if (file == null) { |
| // The file was not actually inside the watched directory. |
| // Should not happen. |
| return; |
| } |
| } while (!file.getParentFile().equals(directory)); |
| } |
| // Otherwise we recurse by adding the file as-is. |
| } |
| synchronized (changed) { |
| changed.add(file); |
| } |
| } |
| |
| @Override |
| protected void onRemove(Path path) { |
| process(path); |
| } |
| |
| @Override |
| protected void debug(String message, Object... args) { |
| log(Util.Logger.LOG_DEBUG, message, args); |
| } |
| |
| @Override |
| protected void warn(String message, Object... args) { |
| log(Util.Logger.LOG_WARNING, message, args); |
| } |
| |
| protected void log(int level, String message, Object... args) { |
| String msg = String.format(message, args); |
| Util.log(bundleContext, level, msg, null); |
| } |
| |
| } |
| |
| } |