From: Mathieu Baudier Date: Sun, 12 Dec 2021 07:35:14 +0000 (+0100) Subject: First draft of file system based GCR X-Git-Tag: argeo-commons-2.3.5~126 X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;h=5a36795f16b1b2a58188db84d6546b501626bda8;p=lgpl%2Fargeo-commons.git First draft of file system based GCR --- diff --git a/org.argeo.api/src/org/argeo/api/gcr/AbstractContent.java b/org.argeo.api/src/org/argeo/api/gcr/AbstractContent.java index a54ff1ee2..f318b99be 100644 --- a/org.argeo.api/src/org/argeo/api/gcr/AbstractContent.java +++ b/org.argeo.api/src/org/argeo/api/gcr/AbstractContent.java @@ -20,7 +20,7 @@ public abstract class AbstractContent extends AbstractMap implem @Override public Object getValue() { // TODO check type - return attr(key); + return get(key, Object.class); } @Override diff --git a/org.argeo.api/src/org/argeo/api/gcr/Content.java b/org.argeo.api/src/org/argeo/api/gcr/Content.java index c5cee9f0e..34f11758a 100644 --- a/org.argeo.api/src/org/argeo/api/gcr/Content.java +++ b/org.argeo.api/src/org/argeo/api/gcr/Content.java @@ -2,6 +2,9 @@ package org.argeo.api.gcr; import java.util.Map; +/** + * A semi-structured content, with attributes, within a hierarchical structure. + */ public interface Content extends Iterable, Map { String getName(); diff --git a/org.argeo.api/src/org/argeo/api/gcr/ContentFeatureUnsupportedException.java b/org.argeo.api/src/org/argeo/api/gcr/ContentFeatureUnsupportedException.java new file mode 100644 index 000000000..0e97a2427 --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/gcr/ContentFeatureUnsupportedException.java @@ -0,0 +1,22 @@ +package org.argeo.api.gcr; + +/** When a feature is not supported by the underlying repository. */ +public class ContentFeatureUnsupportedException extends UnsupportedOperationException { + private static final long serialVersionUID = 3193936026343114949L; + + public ContentFeatureUnsupportedException() { + } + + public ContentFeatureUnsupportedException(String message) { + super(message); + } + + public ContentFeatureUnsupportedException(Throwable cause) { + super(cause); + } + + public ContentFeatureUnsupportedException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/org.argeo.api/src/org/argeo/api/gcr/ContentResourceException.java b/org.argeo.api/src/org/argeo/api/gcr/ContentResourceException.java new file mode 100644 index 000000000..fa7195e7f --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/gcr/ContentResourceException.java @@ -0,0 +1,25 @@ +package org.argeo.api.gcr; + +/** + * When there is a problem the underlying resources, typically IO, network, DB + * access, etc. + */ +public class ContentResourceException extends IllegalStateException { + private static final long serialVersionUID = -2850145213683756996L; + + public ContentResourceException() { + } + + public ContentResourceException(String s) { + super(s); + } + + public ContentResourceException(Throwable cause) { + super(cause); + } + + public ContentResourceException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/org.argeo.cms/src/org/argeo/cms/gcr/fs/FsContent.java b/org.argeo.cms/src/org/argeo/cms/gcr/fs/FsContent.java new file mode 100644 index 000000000..2766d0e3e --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/gcr/fs/FsContent.java @@ -0,0 +1,107 @@ +package org.argeo.cms.gcr.fs; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; +import java.nio.file.attribute.UserDefinedFileAttributeView; +import java.time.Instant; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.argeo.api.gcr.AbstractContent; +import org.argeo.api.gcr.Content; +import org.argeo.api.gcr.ContentResourceException; +import org.argeo.api.gcr.ContentSession; + +public class FsContent extends AbstractContent implements Content { + private static final Set BASIC_KEYS = new HashSet<>( + Arrays.asList("basic:creationTime", "basic:lastModifiedTime", "basic:size", "basic:fileKey")); + private static final Set POSIX_KEYS; + static { + POSIX_KEYS = new HashSet<>(BASIC_KEYS); + POSIX_KEYS.add("owner:owner"); + POSIX_KEYS.add("posix:group"); + POSIX_KEYS.add("posix:permissions"); + } + + private FsContentSession contentSession; + private final Path path; + + public FsContent(FsContentSession contentSession, Path path) { + super(); + this.contentSession = contentSession; + this.path = path; + } + + private boolean isPosix() { + return path.getFileSystem().supportedFileAttributeViews().contains("posix"); + } + + @Override + public Iterator iterator() { + if (Files.isDirectory(path)) { + try { + return Files.list(path).map((p) -> (Content) new FsContent(contentSession, p)).iterator(); + } catch (IOException e) { + throw new ContentResourceException("Cannot list " + path, e); + } + } else { + return Collections.emptyIterator(); + } + } + + @Override + public String getName() { + return path.getFileName().toString(); + } + + @Override + public A get(String key, Class clss) { + Object value; + try { + value = Files.getAttribute(path, key); + } catch (IOException e) { + throw new ContentResourceException("Cannot retrieve attribute " + key + " for " + path, e); + } + if (value instanceof FileTime) { + if (clss.isAssignableFrom(FileTime.class)) + return (A) value; + Instant instant = ((FileTime) value).toInstant(); + if (Object.class.isAssignableFrom(clss)) {// plain object requested + return (A) instant; + } + // TODO perform trivial file conversion to other formats + } + if (value instanceof byte[]) { + return (A) new String((byte[]) value, StandardCharsets.UTF_8); + } + return (A) value; + } + + @Override + public ContentSession getSession() { + return contentSession; + } + + @Override + protected Iterable keys() { + Set result = new HashSet<>(isPosix() ? POSIX_KEYS : BASIC_KEYS); + UserDefinedFileAttributeView udfav = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class); + if (udfav != null) { + try { + for (String name : udfav.list()) { + result.add("user:" + name); + } + } catch (IOException e) { + throw new ContentResourceException("Cannot liast attributes for " + path, e); + } + } + return result; + } + +} diff --git a/org.argeo.cms/src/org/argeo/cms/gcr/fs/FsContentSession.java b/org.argeo.cms/src/org/argeo/cms/gcr/fs/FsContentSession.java new file mode 100644 index 000000000..68cea4f93 --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/gcr/fs/FsContentSession.java @@ -0,0 +1,32 @@ +package org.argeo.cms.gcr.fs; + +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.argeo.api.gcr.Content; +import org.argeo.api.gcr.ContentSession; +import org.argeo.api.gcr.Contents; + +public class FsContentSession implements ContentSession { + private final Path rootPath; + + public FsContentSession(Path rootPath) { + super(); + this.rootPath = rootPath; + } + + @Override + public Content get() { + return new FsContent(this, rootPath); + } + + public static void main(String[] args) { + Path path = Paths.get("/home/mbaudier/tmp"); + System.out.println(FileSystems.getDefault().supportedFileAttributeViews()); + FsContentSession contentSession = new FsContentSession(path); + Contents.traverse(contentSession.get(), (c, d) -> Contents.print(c, System.out, d, true)); + + } +}