First draft of file system based GCR
authorMathieu Baudier <mbaudier@argeo.org>
Sun, 12 Dec 2021 07:35:14 +0000 (08:35 +0100)
committerMathieu Baudier <mbaudier@argeo.org>
Sun, 12 Dec 2021 07:35:14 +0000 (08:35 +0100)
org.argeo.api/src/org/argeo/api/gcr/AbstractContent.java
org.argeo.api/src/org/argeo/api/gcr/Content.java
org.argeo.api/src/org/argeo/api/gcr/ContentFeatureUnsupportedException.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/gcr/ContentResourceException.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/gcr/fs/FsContent.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/gcr/fs/FsContentSession.java [new file with mode: 0644]

index a54ff1ee2e76734b66f9a8c2910fd545a6cd6ff0..f318b99bef4e31f2f015192fb922cddab578ad04 100644 (file)
@@ -20,7 +20,7 @@ public abstract class AbstractContent extends AbstractMap<String, Object> implem
                                @Override
                                public Object getValue() {
                                        // TODO check type
-                                       return attr(key);
+                                       return get(key, Object.class);
                                }
 
                                @Override
index c5cee9f0e6676495acd7f791a98204bec9f6c8d8..34f11758aa08f2e45d9536875fe6638ddd72098c 100644 (file)
@@ -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<Content>, Map<String, Object> {
 
        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 (file)
index 0000000..0e97a24
--- /dev/null
@@ -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 (file)
index 0000000..fa7195e
--- /dev/null
@@ -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 (file)
index 0000000..2766d0e
--- /dev/null
@@ -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<String> BASIC_KEYS = new HashSet<>(
+                       Arrays.asList("basic:creationTime", "basic:lastModifiedTime", "basic:size", "basic:fileKey"));
+       private static final Set<String> 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<Content> 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> A get(String key, Class<A> 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<String> keys() {
+               Set<String> 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 (file)
index 0000000..68cea4f
--- /dev/null
@@ -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));
+
+       }
+}