blob: 7277820627a599b5544053a224493002c21a4341 [file] [log] [blame]
/*
* 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);
}
}
}