Skip to content

Commit

Permalink
#217 simple persistence
Browse files Browse the repository at this point in the history
  • Loading branch information
Thorsten Marx committed Jun 28, 2024
1 parent 524bcc1 commit 1120858
Show file tree
Hide file tree
Showing 16 changed files with 866 additions and 203 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.github.thmarx.cms.api.utils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;

/**
*
* @author thmar
*/
public class FileUtils {

public static void deleteFolder(Path pathToBeDeleted) throws IOException {
Files.walk(pathToBeDeleted)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
}
}
1 change: 1 addition & 0 deletions cms-filesystem/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src/test/resources/data/
14 changes: 14 additions & 0 deletions cms-filesystem/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@
<packaging>jar</packaging>

<dependencies>

<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analysis-common</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2-mvstore</artifactId>
</dependency>

<dependency>
<groupId>com.github.thmarx.cms</groupId>
<artifactId>cms-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
import com.github.thmarx.cms.filesystem.metadata.memory.MemoryMetaData;
import com.github.thmarx.cms.api.ModuleFileSystem;
import com.github.thmarx.cms.api.Constants;
import com.github.thmarx.cms.api.db.ContentNode;
Expand All @@ -32,6 +33,7 @@
import com.github.thmarx.cms.api.eventbus.events.ReIndexContentMetaDataEvent;
import com.github.thmarx.cms.api.eventbus.events.TemplateChangedEvent;
import com.github.thmarx.cms.api.utils.PathUtil;
import com.github.thmarx.cms.filesystem.metadata.persistent.PersistentMetaData;
import com.github.thmarx.cms.filesystem.query.Query;
import java.io.IOException;
import java.nio.charset.Charset;
Expand Down Expand Up @@ -70,7 +72,7 @@ public class FileSystem implements ModuleFileSystem, DBFileSystem {
private Path contentBase;

@Getter
private final MetaData metaData = new MetaData();
private MetaData metaData;

@Override
public Path base () {
Expand Down Expand Up @@ -101,7 +103,7 @@ public boolean isVisible(final String uri) {
return false;
}
var n = node.get();
return MetaData.isVisible(n);
return MemoryMetaData.isVisible(n);
}

@Override
Expand Down Expand Up @@ -147,7 +149,7 @@ public List<String> loadLines(final Path file, final Charset charset) throws IOE

public List<ContentNode> listDirectories(final Path base, final String start) {
var startPath = base.resolve(start);
String folder = PathUtil.toRelativePath(startPath, contentBase).toString();
String folder = PathUtil.toRelativePath(startPath, contentBase);

List<ContentNode> nodes = new ArrayList<>();

Expand Down Expand Up @@ -191,7 +193,7 @@ public List<ContentNode> listDirectories(final Path base, final String start) {
public List<ContentNode> listContent(final Path base, final String start) {
var startPath = base.resolve(start);

String folder = PathUtil.toRelativePath(startPath, contentBase).toString();
String folder = PathUtil.toRelativePath(startPath, contentBase);

if ("".equals(folder)) {
return metaData.listChildren("");
Expand All @@ -202,7 +204,7 @@ public List<ContentNode> listContent(final Path base, final String start) {
}

public List<ContentNode> listSections(final Path contentFile) {
String folder = PathUtil.toRelativePath(contentFile, contentBase).toString();
String folder = PathUtil.toRelativePath(contentFile, contentBase);
String filename = contentFile.getFileName().toString();
filename = filename.substring(0, filename.length() - 3);

Expand Down Expand Up @@ -267,7 +269,18 @@ private void addOrUpdateMetaData(Path file) {
}

public void init() throws IOException {
init(MetaData.Type.MEMORY);
}

public void init(MetaData.Type metaDataType) throws IOException {
log.debug("init filesystem");

if (MetaData.Type.PERSISTENT.equals(metaDataType)) {
this.metaData = new PersistentMetaData(this.hostBaseDirectory);
this.metaData.open();
} else {
this.metaData = new MemoryMetaData();
}

this.contentBase = resolve("content/");
var templateBase = resolve("templates/");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,214 +1,39 @@
package com.github.thmarx.cms.filesystem;

/*-
* #%L
* cms-server
* %%
* Copyright (C) 2023 Marx-Software
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/

import com.github.thmarx.cms.api.Constants;
import com.github.thmarx.cms.api.db.ContentNode;
import com.github.thmarx.cms.filesystem.index.IndexProviding;
import com.github.thmarx.cms.filesystem.index.SecondaryIndex;
import com.google.common.base.Strings;
import java.io.IOException;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
*
* @author t.marx
*/
public class MetaData implements IndexProviding {

private ConcurrentMap<String, ContentNode> nodes = new ConcurrentHashMap<>();

private ConcurrentMap<String, ContentNode> tree = new ConcurrentHashMap<>();
public interface MetaData extends IndexProviding {

private ConcurrentMap<String, SecondaryIndex<?>> secondaryIndexes = new ConcurrentHashMap<>();

@Override
public SecondaryIndex<?> getOrCreateIndex (final String field, Function<ContentNode, Object> indexFunction) {

if (!secondaryIndexes.containsKey(field)) {
var index = SecondaryIndex.<Object>builder()
.indexFunction(indexFunction)
.build();
index.addAll(nodes.values());
secondaryIndexes.put(field, index);
}

return secondaryIndexes.get(field);
}

void clear() {
nodes.clear();
tree.clear();
secondaryIndexes.clear();
public enum Type {
MEMORY, PERSISTENT
}

ConcurrentMap<String, ContentNode> nodes() {
return new ConcurrentHashMap<>(nodes);
}
void open () throws IOException;
void close () throws IOException;

ConcurrentMap<String, ContentNode> tree() {
return new ConcurrentHashMap<>(tree);
}

public void createDirectory(final String uri) {
if (Strings.isNullOrEmpty(uri)) {
return;
}
var parts = uri.split(Constants.SPLIT_PATH_PATTERN);
ContentNode n = new ContentNode(uri, parts[parts.length - 1], Map.of(), true);

Optional<ContentNode> parentFolder;
if (parts.length == 1) {
parentFolder = getFolder(uri);
} else {
var parentPath = Arrays.copyOfRange(parts, 0, parts.length - 1);
var parentUri = String.join("/", parentPath);
parentFolder = getFolder(parentUri);
}

if (parentFolder.isPresent()) {
parentFolder.get().children().put(n.name(), n);
} else {
tree.put(n.name(), n);
}
}
void addFile(final String uri, final Map<String, Object> data, final LocalDate lastModified);

public List<ContentNode> listChildren(String uri) {
if ("".equals(uri)) {
return tree.values().stream()
.filter(node -> !node.isHidden())
.map(this::mapToIndex)
.filter(node -> node != null)
.filter(MetaData::isVisible)
.collect(Collectors.toList());
Optional<ContentNode> byUri(final String uri);

} else {
Optional<ContentNode> findFolder = findFolder(uri);
if (findFolder.isPresent()) {
return findFolder.get().children().values()
.stream()
.filter(node -> !node.isHidden())
.map(this::mapToIndex)
.filter(node -> node != null)
.filter(MetaData::isVisible)
.collect(Collectors.toList());
}
}
return Collections.emptyList();
}
void createDirectory(final String uri);

protected ContentNode mapToIndex(ContentNode node) {
if (node.isDirectory()) {
var tempNode = node.children().entrySet().stream().filter((entry)
-> entry.getKey().equals("index.md")
).findFirst();
if (tempNode.isPresent()) {
return tempNode.get().getValue();
}
return null;
} else {
return node;
}
}
Optional<ContentNode> findFolder(String uri);

public static boolean isVisible (ContentNode node) {
return node != null
// check if some parent is hidden
&& !node.uri().startsWith(".") && !node.uri().contains("/.")
&& node.isPublished()
&& !node.isHidden()
&& !node.isSection();
}
List<ContentNode> listChildren(String uri);

public Optional<ContentNode> findFolder(String uri) {
return getFolder(uri);
}

private Optional<ContentNode> getFolder(String uri) {
var parts = uri.split(Constants.SPLIT_PATH_PATTERN);

final AtomicReference<ContentNode> folder = new AtomicReference<>(null);
Stream.of(parts).forEach(part -> {
if (part.endsWith(".md")) {
return;
}
if (folder.get() == null) {
folder.set(tree.get(part));
} else {
folder.set(folder.get().children().get(part));
}
});
return Optional.ofNullable(folder.get());
}

public void addFile(final String uri, final Map<String, Object> data, final LocalDate lastModified) {

var parts = uri.split(Constants.SPLIT_PATH_PATTERN);
final ContentNode node = new ContentNode(uri, parts[parts.length - 1], data, lastModified);

nodes.put(uri, node);

var folder = getFolder(uri);
if (folder.isPresent()) {
folder.get().children().put(node.name(), node);
} else {
tree.put(node.name(), node);
}

secondaryIndexes.values().forEach(index -> index.add(node));
}

public Optional<ContentNode> byUri(final String uri) {
if (!nodes.containsKey(uri)) {
return Optional.empty();
}
return Optional.of(nodes.get(uri));
}

void remove(String uri) {
var node = nodes.remove(uri);

var folder = getFolder(uri);
var parts = uri.split(Constants.SPLIT_PATH_PATTERN);
var name = parts[parts.length - 1];
if (folder.isPresent()) {
folder.get().children().remove(name);
} else {
tree.remove(name);
}

secondaryIndexes.values().forEach(index -> index.remove(node));
}

void clear ();

Map<String, ContentNode> nodes();

Map<String, ContentNode> tree();
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,9 @@
import com.github.thmarx.cms.api.db.ContentNode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import lombok.Builder;

Expand Down
Loading

0 comments on commit 1120858

Please sign in to comment.