From 950645dcefcb32aec01ee535dfeaa316e9204b14 Mon Sep 17 00:00:00 2001 From: Minecon724 Date: Thu, 20 Feb 2025 15:29:47 +0100 Subject: [PATCH] feat: Compression and refactoring Signed-off-by: Minecon724 --- pom.xml | 14 +++- src/main/java/eu/m724/blog/BlogBuilder.java | 70 +++++++++++++------ src/main/java/eu/m724/blog/Main.java | 3 + .../eu/m724/blog/compress/FileCompressor.java | 36 ++++++++++ .../m724/blog/template/TemplateRenderer.java | 4 +- 5 files changed, 102 insertions(+), 25 deletions(-) create mode 100644 src/main/java/eu/m724/blog/compress/FileCompressor.java diff --git a/pom.xml b/pom.xml index 0d00270..4ff01c3 100644 --- a/pom.xml +++ b/pom.xml @@ -41,11 +41,23 @@ 2.9 - commons-io + commons-io commons-io 2.18.0 + + org.apache.commons + commons-compress + 1.27.1 + + + com.github.luben + zstd-jni + 1.5.6-10 + + + org.slf4j slf4j-api diff --git a/src/main/java/eu/m724/blog/BlogBuilder.java b/src/main/java/eu/m724/blog/BlogBuilder.java index 74510d8..143a4e3 100644 --- a/src/main/java/eu/m724/blog/BlogBuilder.java +++ b/src/main/java/eu/m724/blog/BlogBuilder.java @@ -1,9 +1,12 @@ package eu.m724.blog; +import eu.m724.blog.compress.FileCompressor; import eu.m724.blog.data.Feed; import eu.m724.blog.data.Post; import eu.m724.blog.data.Site; import eu.m724.blog.template.TemplateRenderer; +import org.apache.commons.compress.compressors.CompressorException; +import org.apache.commons.compress.compressors.CompressorStreamFactory; import org.apache.commons.io.file.PathUtils; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.lib.RepositoryBuilder; @@ -14,6 +17,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Comparator; +import java.util.List; +import java.util.Set; import java.util.stream.Collectors; /** @@ -115,17 +120,35 @@ public class BlogBuilder { * @throws IOException if an I/O error occurs */ public void build() throws IOException { + System.out.println("Loading site..."); if (site == null) this.site = Site.fromConfig(workingDirectory.resolve("site-config.yml")); if (template == null) this.template = new TemplateRenderer(templateDirectory); - copyIfExists(workingDirectory.resolve("assets"), outputDirectory.resolve("assets")); - copyIfExists(templateDirectory.resolve("static"), outputDirectory.resolve("static")); + System.out.println("Copying assets..."); + copyTree(workingDirectory.resolve("assets"), outputDirectory.resolve("assets")); + copyTree(templateDirectory.resolve("static"), outputDirectory.resolve("static")); + System.out.println("Rendering posts..."); + var posts = renderPosts(); + + System.out.println("Rendering meta..."); + posts.sort(Comparator.comparing(Post::createdAt).reversed()); + Files.writeString(outputDirectory.resolve("index.html"), template.renderIndex(site, posts)); + + Files.writeString(outputDirectory.resolve("posts.rss"), Feed.generateRss(site, posts)); + + + System.out.println("Compressing..."); + compressOutput(); + } + + private List renderPosts() throws IOException { Files.createDirectory(outputDirectory.resolve("post")); var postDirectory = workingDirectory.resolve("posts"); + var posts = new ArrayList(); try (var stream = Files.walk(postDirectory)) { @@ -158,20 +181,35 @@ public class BlogBuilder { } } - posts.sort(Comparator.comparing(Post::createdAt).reversed()); - Files.writeString(outputDirectory.resolve("index.html"), template.renderIndex(site, posts)); + return posts; + } - Files.writeString(outputDirectory.resolve("posts.rss"), Feed.generateRss(site, posts)); + private void compressOutput() throws IOException { + var compressors = new FileCompressor[] { + new FileCompressor(CompressorStreamFactory.GZIP), + new FileCompressor(CompressorStreamFactory.ZSTANDARD) + }; + + Set tree; + try (var walk = Files.walk(outputDirectory)) { + tree = walk.filter(Files::isRegularFile).collect(Collectors.toSet()); + } + + for (var compressor : compressors) { + for (var path : tree) { + try { + compressor.compress(path); + } catch (CompressorException e) { + System.err.printf("Exception compressing %s to %s: %s%n", path, compressor.getAlgorithm(), e.getMessage()); + } + } + } } /* Internal functions */ - private boolean copyTree(Path srcDir, Path destDir) throws IOException { - if (!Files.isDirectory(srcDir)) { - return false; - } - + private void copyTree(Path srcDir, Path destDir) throws IOException { try (var walk = Files.walk(srcDir)) { for (var src : walk.collect(Collectors.toSet())) { var rel = srcDir.relativize(src); @@ -188,17 +226,5 @@ public class BlogBuilder { } } } - - return true; - } - - private void copyIfExists(Path srcDir, Path destDir) throws IOException { - System.out.print(srcDir); - - if (copyTree(srcDir, destDir)) { - System.out.println(" copied"); - } else { - System.out.println(" doesn't exist, not copying"); - } } } diff --git a/src/main/java/eu/m724/blog/Main.java b/src/main/java/eu/m724/blog/Main.java index a5b1ed1..6ae747d 100644 --- a/src/main/java/eu/m724/blog/Main.java +++ b/src/main/java/eu/m724/blog/Main.java @@ -36,7 +36,10 @@ public class Main { .renderDrafts(renderDrafts); builder.mkdirs(force); + + System.out.println("---- START BUILD ----"); builder.build(); + System.out.println("----- END BUILD -----"); var end = System.nanoTime(); System.out.printf("Exported to %s (%.2f ms)\n", outputDirectory, (end - start) / 1000000.0); diff --git a/src/main/java/eu/m724/blog/compress/FileCompressor.java b/src/main/java/eu/m724/blog/compress/FileCompressor.java new file mode 100644 index 0000000..3b1ec3c --- /dev/null +++ b/src/main/java/eu/m724/blog/compress/FileCompressor.java @@ -0,0 +1,36 @@ +package eu.m724.blog.compress; + +import org.apache.commons.compress.compressors.CompressorException; +import org.apache.commons.compress.compressors.CompressorStreamFactory; + +import java.io.IOException; +import java.nio.file.*; + +public class FileCompressor { + private final String algorithm; + + public FileCompressor(String algorithm) { + this.algorithm = algorithm; + } + + public void compress(Path source) throws IOException, CompressorException { + var destination = source.resolveSibling(source.getFileName() + "." + algorithm); + compress(source, destination); + } + + public void compress(Path source, Path destination) throws IOException, CompressorException { + if (Files.exists(destination)) + throw new FileAlreadyExistsException(destination.toString()); + + try ( + var outputStream = new CompressorStreamFactory() + .createCompressorOutputStream(algorithm, Files.newOutputStream(destination)) + ) { + Files.copy(source, outputStream); + } + } + + public String getAlgorithm() { + return algorithm; + } +} diff --git a/src/main/java/eu/m724/blog/template/TemplateRenderer.java b/src/main/java/eu/m724/blog/template/TemplateRenderer.java index ce52b8c..cf9fe15 100644 --- a/src/main/java/eu/m724/blog/template/TemplateRenderer.java +++ b/src/main/java/eu/m724/blog/template/TemplateRenderer.java @@ -9,7 +9,7 @@ import io.pebbletemplates.pebble.template.PebbleTemplate; import java.io.IOException; import java.io.StringWriter; import java.nio.file.Path; -import java.util.ArrayList; +import java.util.List; import java.util.Map; /** @@ -46,7 +46,7 @@ public class TemplateRenderer { * @return the rendered index HTML page as a string * @throws IOException if an error occurs during the template evaluation */ - public String renderIndex(Site site, ArrayList posts) throws IOException { + public String renderIndex(Site site, List posts) throws IOException { Map context = Map.of( "site", site, "articles", posts