mirror of
https://github.com/apache/maven-mvnd.git
synced 2026-01-13 07:04:14 +08:00
Test also WatchServiceCacheFactory
This commit is contained in:
@@ -31,6 +31,9 @@ public class DefaultCacheFactory implements CacheFactory {
|
||||
private final CacheFactory delegate;
|
||||
|
||||
public DefaultCacheFactory() {
|
||||
/* java.nio.file.attribute.BasicFileAttributes.fileKey() is always null on Windows
|
||||
* and we do not hold relying solely on java.nio.file.attribute.BasicFileAttributes#lastModifiedTime()
|
||||
* for sufficient. So we rather rely on WatchService on Windows */
|
||||
this.delegate = Os.current() == Os.WINDOWS ? new WatchServiceCacheFactory() : new TimestampCacheFactory();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import org.codehaus.plexus.logging.AbstractLogEnabled;
|
||||
import org.mvndaemon.mvnd.cache.Cache;
|
||||
import org.mvndaemon.mvnd.cache.CacheFactory;
|
||||
import org.mvndaemon.mvnd.cache.CacheRecord;
|
||||
@@ -37,7 +36,7 @@ import org.mvndaemon.mvnd.cache.CacheRecord;
|
||||
* A factory for {@link Cache} objects invalidating its entries based on {@link BasicFileAttributes#lastModifiedTime()}
|
||||
* and {@link java.nio.file.attribute.BasicFileAttributes#fileKey()}.
|
||||
*/
|
||||
public class TimestampCacheFactory extends AbstractLogEnabled implements CacheFactory {
|
||||
public class TimestampCacheFactory implements CacheFactory {
|
||||
|
||||
public TimestampCacheFactory() {
|
||||
}
|
||||
|
||||
@@ -30,15 +30,19 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Function;
|
||||
import org.codehaus.plexus.logging.AbstractLogEnabled;
|
||||
import java.util.stream.Stream;
|
||||
import org.mvndaemon.mvnd.cache.Cache;
|
||||
import org.mvndaemon.mvnd.cache.CacheFactory;
|
||||
import org.mvndaemon.mvnd.cache.CacheRecord;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A factory for {@link Cache} objects invalidating its entries based on events received from {@link WatchService}.
|
||||
*/
|
||||
public class WatchServiceCacheFactory extends AbstractLogEnabled implements CacheFactory {
|
||||
public class WatchServiceCacheFactory implements CacheFactory {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(WatchServiceCacheFactory.class);
|
||||
|
||||
private final WatchService watchService;
|
||||
|
||||
@@ -84,9 +88,7 @@ public class WatchServiceCacheFactory extends AbstractLogEnabled implements Cach
|
||||
|
||||
private Registration register(Path key, Registration value) {
|
||||
if (value == null) {
|
||||
if (getLogger().isDebugEnabled()) {
|
||||
getLogger().debug("Starting to watch path " + key);
|
||||
}
|
||||
LOG.debug("Starting to watch path {}", key);
|
||||
try {
|
||||
WatchEvent.Modifier[] mods;
|
||||
try {
|
||||
@@ -105,9 +107,7 @@ public class WatchServiceCacheFactory extends AbstractLogEnabled implements Cach
|
||||
}
|
||||
} else {
|
||||
int cnt = value.count.incrementAndGet();
|
||||
if (getLogger().isDebugEnabled()) {
|
||||
getLogger().debug("Already " + cnt + " watchers for path " + key);
|
||||
}
|
||||
LOG.debug("Already {} watchers for path {}", cnt, key);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -121,19 +121,13 @@ public class WatchServiceCacheFactory extends AbstractLogEnabled implements Cach
|
||||
for (WatchEvent<?> event : watchKey.pollEvents()) {
|
||||
final Path dir = entry.getKey();
|
||||
WatchEvent.Kind<?> kind = event.kind();
|
||||
if (getLogger().isDebugEnabled()) {
|
||||
getLogger().debug("Got watcher event " + kind.name());
|
||||
}
|
||||
if (kind == StandardWatchEventKinds.ENTRY_DELETE || kind == StandardWatchEventKinds.ENTRY_MODIFY) {
|
||||
final Path path = dir.resolve((Path) event.context());
|
||||
LOG.debug("Got watcher event {} for file {}", kind.name(), path);
|
||||
final List<CacheRecord> records = recordsByPath.remove(path);
|
||||
if (getLogger().isDebugEnabled()) {
|
||||
getLogger().debug("Records for path " + path + ": " + records);
|
||||
}
|
||||
LOG.debug("Records for path {}: {}", path, records);
|
||||
if (records != null) {
|
||||
if (getLogger().isDebugEnabled()) {
|
||||
getLogger().debug("Invalidating recorder of path " + path);
|
||||
}
|
||||
LOG.debug("Invalidating records of path {}", path);
|
||||
remove(records);
|
||||
}
|
||||
} else if (kind == StandardWatchEventKinds.OVERFLOW) {
|
||||
@@ -143,9 +137,7 @@ public class WatchServiceCacheFactory extends AbstractLogEnabled implements Cach
|
||||
Map.Entry<Path, List<CacheRecord>> en = it.next();
|
||||
if (en.getKey().getParent().equals(dir)) {
|
||||
it.remove();
|
||||
if (getLogger().isDebugEnabled()) {
|
||||
getLogger().debug("Invalidating recorder of path " + en.getKey());
|
||||
}
|
||||
LOG.debug("Invalidating records of path {}", en.getKey());
|
||||
remove(en.getValue());
|
||||
}
|
||||
}
|
||||
@@ -170,22 +162,16 @@ public class WatchServiceCacheFactory extends AbstractLogEnabled implements Cach
|
||||
|
||||
private Registration unregister(Path key, Registration value) {
|
||||
if (value == null) {
|
||||
if (getLogger().isDebugEnabled()) {
|
||||
getLogger().debug("Already unwatchers for path " + key);
|
||||
}
|
||||
LOG.debug("Already stopped watching path {}", key);
|
||||
return null;
|
||||
} else {
|
||||
final int cnt = value.count.decrementAndGet();
|
||||
if (cnt <= 0) {
|
||||
if (getLogger().isDebugEnabled()) {
|
||||
getLogger().debug("Unwatching path " + key);
|
||||
}
|
||||
LOG.debug("Unwatching path {}", key);
|
||||
value.watchKey.cancel();
|
||||
return null;
|
||||
} else {
|
||||
if (getLogger().isDebugEnabled()) {
|
||||
getLogger().debug("Still " + cnt + " watchers for path " + key);
|
||||
}
|
||||
LOG.debug("Still " + cnt + " watchers for path {}", key);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -210,7 +196,7 @@ public class WatchServiceCacheFactory extends AbstractLogEnabled implements Cach
|
||||
|
||||
@Override
|
||||
public boolean contains(K key) {
|
||||
return map.containsKey(key);
|
||||
return get(key) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -221,7 +207,7 @@ public class WatchServiceCacheFactory extends AbstractLogEnabled implements Cach
|
||||
|
||||
@Override
|
||||
public void put(K key, V value) {
|
||||
add(value);
|
||||
add(new WrappedCacheRecord<>(map, key, value));
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
@@ -246,9 +232,33 @@ public class WatchServiceCacheFactory extends AbstractLogEnabled implements Cach
|
||||
validateRecords();
|
||||
return map.computeIfAbsent(key, k -> {
|
||||
V v = mappingFunction.apply(k);
|
||||
add(v);
|
||||
add(new WrappedCacheRecord<>(map, k, v));
|
||||
return v;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static class WrappedCacheRecord<K, V extends CacheRecord> implements CacheRecord {
|
||||
private final Map<K, V> map;
|
||||
private final K key;
|
||||
private final V delegate;
|
||||
|
||||
public WrappedCacheRecord(Map<K, V> map, K key, V delegate) {
|
||||
this.map = map;
|
||||
this.key = key;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Path> getDependencyPaths() {
|
||||
return delegate.getDependencyPaths();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
delegate.invalidate();
|
||||
map.remove(key);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mvndaemon.mvnd.cache.factory.impl;
|
||||
package org.mvndaemon.mvnd.cache.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -27,14 +27,23 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mvndaemon.mvnd.cache.Cache;
|
||||
import org.mvndaemon.mvnd.cache.CacheRecord;
|
||||
import org.mvndaemon.mvnd.cache.impl.DefaultCacheFactory;
|
||||
import org.mvndaemon.mvnd.common.Os;
|
||||
|
||||
public class TimestampCacheFactoryTest {
|
||||
public class CacheFactoryTest {
|
||||
|
||||
@Test
|
||||
void putGet(@TempDir Path tempDir) throws IOException, InterruptedException {
|
||||
void timestampCache(@TempDir Path tempDir) throws IOException, InterruptedException {
|
||||
assertPutGet(tempDir, new TimestampCacheFactory().newCache(), 200);
|
||||
}
|
||||
|
||||
@Test
|
||||
void watchServiceCache(@TempDir Path tempDir) throws IOException, InterruptedException {
|
||||
int asyncOpDelayMs = Os.current() == Os.MAC ? 2500 : 200;
|
||||
assertPutGet(tempDir, new WatchServiceCacheFactory().newCache(), asyncOpDelayMs);
|
||||
}
|
||||
|
||||
public void assertPutGet(Path tempDir, final Cache<String, CacheRecord> cache, int asyncOpDelayMs)
|
||||
throws IOException, InterruptedException {
|
||||
final Path file1 = tempDir.resolve("file1");
|
||||
Files.write(file1, "content1".getBytes(StandardCharsets.UTF_8));
|
||||
final SimpleCacheRecord record1 = new SimpleCacheRecord(file1);
|
||||
@@ -43,8 +52,6 @@ public class TimestampCacheFactoryTest {
|
||||
Files.write(file2, "content2".getBytes(StandardCharsets.UTF_8));
|
||||
final SimpleCacheRecord record2 = new SimpleCacheRecord(file2);
|
||||
|
||||
final Cache<String, CacheRecord> cache = new DefaultCacheFactory().newCache();
|
||||
|
||||
final String k1 = "k1";
|
||||
cache.put(k1, record1);
|
||||
final String k2 = "k2";
|
||||
@@ -58,9 +65,8 @@ public class TimestampCacheFactoryTest {
|
||||
Assertions.assertFalse(record2.invalidated);
|
||||
|
||||
Files.write(file1, "content1.1".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
if (Os.current() == Os.WINDOWS) {
|
||||
Thread.sleep(3000);
|
||||
if (asyncOpDelayMs > 0) {
|
||||
Thread.sleep(asyncOpDelayMs);
|
||||
}
|
||||
|
||||
Assertions.assertFalse(cache.contains(k1));
|
||||
@@ -71,8 +77,8 @@ public class TimestampCacheFactoryTest {
|
||||
Assertions.assertFalse(record2.invalidated);
|
||||
|
||||
Files.delete(file2);
|
||||
if (Os.current() == Os.WINDOWS) {
|
||||
Thread.sleep(3000);
|
||||
if (asyncOpDelayMs > 0) {
|
||||
Thread.sleep(asyncOpDelayMs);
|
||||
}
|
||||
Assertions.assertFalse(cache.contains(k2));
|
||||
Assertions.assertNull(cache.get(k2));
|
||||
@@ -98,5 +104,10 @@ public class TimestampCacheFactoryTest {
|
||||
this.invalidated = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SimpleCacheRecord [paths=" + paths + ", invalidated=" + invalidated + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user