From f963f42c57f8f82994acccc579d0c29a62936bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=CC=88rg=20Prante?= Date: Mon, 14 Aug 2023 08:18:28 +0200 Subject: [PATCH] working on json file store --- net/src/main/java/org/xbib/net/Store.java | 21 +++ .../java/org/xbib/net/util/JsonFileStore.java | 134 ++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 net/src/main/java/org/xbib/net/Store.java create mode 100644 net/src/main/java/org/xbib/net/util/JsonFileStore.java diff --git a/net/src/main/java/org/xbib/net/Store.java b/net/src/main/java/org/xbib/net/Store.java new file mode 100644 index 0000000..6eb5eb7 --- /dev/null +++ b/net/src/main/java/org/xbib/net/Store.java @@ -0,0 +1,21 @@ +package org.xbib.net; + +import java.io.IOException; +import java.util.function.Consumer; + +public interface Store { + + String getName(); + + long size() throws IOException; + + S read(String key) throws IOException; + + void readAll(String key, Consumer consumer) throws IOException; + + void write(String key, S s) throws IOException; + + void remove(String key) throws IOException; + + void purge(long expiredAfterSeconds) throws IOException; +} diff --git a/net/src/main/java/org/xbib/net/util/JsonFileStore.java b/net/src/main/java/org/xbib/net/util/JsonFileStore.java new file mode 100644 index 0000000..df243e7 --- /dev/null +++ b/net/src/main/java/org/xbib/net/util/JsonFileStore.java @@ -0,0 +1,134 @@ +package org.xbib.net.util; + +import org.xbib.net.Store; +import org.xbib.net.PercentEncoder; +import org.xbib.net.PercentEncoders; + +import java.io.IOException; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; +import java.time.Duration; +import java.time.Instant; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Consumer; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Stream; + +public class JsonFileStore implements Store> { + + private static final Logger logger = Logger.getLogger(JsonFileStore.class.getName()); + + private final String name; + + private final ReentrantReadWriteLock lock; + + private final Path path; + + public JsonFileStore(String name, Path path) throws IOException { + this.name = name; + this.path = path; + this.lock = new ReentrantReadWriteLock(); + Files.createDirectories(path); + } + + @Override + public String getName() { + return name; + } + + @Override + public long size() throws IOException { + AtomicLong size = new AtomicLong(0L); + try (Stream stream = Files.walk(path)) { + stream.forEach(p -> { + if (Files.isRegularFile(p)) { + size.incrementAndGet(); + } + }); + return size.get(); + } + } + + @Override + public void readAll(String key, Consumer> consumer) throws IOException { + Objects.requireNonNull(consumer); + try (Stream stream = Files.walk(path.resolve(key))) { + stream.forEach(p -> { + try { + consumer.accept(read(p.getFileName().toString())); + } catch (IOException e) { + logger.log(Level.WARNING, "i/o error while consuming: " + e.getMessage(), e); + } + }); + } + } + + @Override + public Map read(String key) throws IOException { + ReentrantReadWriteLock.ReadLock readLock = lock.readLock(); + try { + readLock.lock(); + PercentEncoder percentEncoder = PercentEncoders.getUnreservedEncoder(StandardCharsets.UTF_8); + return JsonUtil.toMap(Files.readString(path.resolve(percentEncoder.encode(key)))); + } finally { + readLock.unlock(); + } + } + + @Override + public void write(String key, Map map) throws IOException { + ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); + try { + writeLock.lock(); + PercentEncoder percentEncoder = PercentEncoders.getUnreservedEncoder(StandardCharsets.UTF_8); + Path p = path.resolve(percentEncoder.encode(key)); + if (!Files.exists(p.getParent())) { + Files.createDirectories(p.getParent()); + } + try (Writer writer = Files.newBufferedWriter(p)) { + writer.write(JsonUtil.toString(map)); + } + } finally { + writeLock.unlock(); + } + } + + @Override + public void remove(String key) throws IOException { + ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); + try { + writeLock.lock(); + PercentEncoder percentEncoder = PercentEncoders.getUnreservedEncoder(StandardCharsets.UTF_8); + Files.deleteIfExists(path.resolve(percentEncoder.encode(key))); + } finally { + writeLock.unlock(); + } + } + + @Override + public void purge(long expiredAfterSeconds) throws IOException { + if (path != null && expiredAfterSeconds > 0L) { + Instant instant = Instant.now(); + try (Stream stream = Files.walk(path)) { + stream.forEach(p -> { + try { + FileTime fileTime = Files.getLastModifiedTime(p); + Duration duration = Duration.between(fileTime.toInstant(), instant); + if (duration.toSeconds() > expiredAfterSeconds) { + Files.delete(p); + } + } catch (IOException e) { + logger.log(Level.WARNING, "i/o error while purge: " + e.getMessage(), e); + } + }); + } + } + } +}