Skip to content

Commit

Permalink
Implement our own static content handler to fix quarkiverse#3
Browse files Browse the repository at this point in the history
  • Loading branch information
ppalaga committed Apr 11, 2024
1 parent 13d2be2 commit 5fce2d2
Show file tree
Hide file tree
Showing 21 changed files with 615 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public CardPageBuildItem pages(NonApplicationRootPathBuildItem nonApplicationRoo
CardPageBuildItem cardPageBuildItem = new CardPageBuildItem();

cardPageBuildItem.addPage(Page.externalPageBuilder("Antora site")
.url("/antora/index.html")
.url("/index.html")
.icon("font-awesome-solid:file-lines"));

return cardPageBuildItem;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,9 @@
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.GeneratedResourceBuildItem;
import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem;
import io.quarkus.deployment.pkg.builditem.BuildSystemTargetBuildItem;
import io.quarkus.deployment.util.FileUtil;
import io.quarkus.vertx.http.deployment.spi.AdditionalStaticResourceBuildItem;

public class AntoraProcessor {
private static final String FEATURE = "antora";
Expand All @@ -59,7 +57,6 @@ void watchResources(BuildProducer<HotDeploymentWatchedFileBuildItem> watchedFile
try (Stream<Path> files = Files.walk(modulesDir)) {
files
.map(Path::toString)
.peek(p -> System.out.println("watching " + p))
.map(HotDeploymentWatchedFileBuildItem::new)
.forEach(watchedFiles::produce);
} catch (IOException e) {
Expand All @@ -70,8 +67,7 @@ void watchResources(BuildProducer<HotDeploymentWatchedFileBuildItem> watchedFile
@BuildStep
void buildAntoraSite(
BuildSystemTargetBuildItem buildSystemTarget,
BuildProducer<AdditionalStaticResourceBuildItem> staticResources,
BuildProducer<GeneratedResourceBuildItem> generatedResource) {
BuildProducer<GeneratedWebResourceBuildItem> staticResourceProducer) {

final Path targetDir = buildSystemTarget.getOutputDirectory();
if (!Files.isDirectory(targetDir)) {
Expand Down Expand Up @@ -104,7 +100,7 @@ void buildAntoraSite(
AntoraFrameConsumer antoraFrameConsumer = new AntoraFrameConsumer();

final Path gitRepoRoot = gitRepoRoot(baseDir);
final PlaybookInfo pbInfo = augmentAntoraPlaybook(gitRepoRoot, baseDir, targetDir, staticResources);
final PlaybookInfo pbInfo = augmentAntoraPlaybook(gitRepoRoot, baseDir, targetDir);
final Path absAntoraPlaybookPath = pbInfo.playbookPath;
final Path antoraPlaybookPath = gitRepoRoot.relativize(absAntoraPlaybookPath);

Expand Down Expand Up @@ -160,13 +156,12 @@ void buildAntoraSite(
if (!Files.exists(indexHtmlCopy)) {
/* Override it only if it does not exist */
final String newContent = oldContent
.replaceAll("([^=/\">]*/dev/index.html)", "/antora/$1")
//.replaceAll("([^=/\">]*/dev/index.html)", "/antora/$1")
/* Do not cache the redirect page */
.replace("<meta http-equiv=\"refresh\"",
"<meta http-equiv=\"Cache-Control\" content=\"no-store\">\n<meta http-equiv=\"refresh\"");
generatedResource.produce(new GeneratedResourceBuildItem("META-INF/resources/index.html",
staticResourceProducer.produce(new GeneratedWebResourceBuildItem("/index.html",
newContent.getBytes(StandardCharsets.UTF_8)));
staticResources.produce(new AdditionalStaticResourceBuildItem("/index.html", false));
}
bytes = oldContent
/* Do not cache the redirect page */
Expand All @@ -180,10 +175,8 @@ void buildAntoraSite(
throw new RuntimeException("Could not read " + absP, e);
}
}
log.infof("Producing META-INF/resources/antora/%s", relPath);
generatedResource.produce(new GeneratedResourceBuildItem("META-INF/resources/antora/" + relPath,
bytes));
staticResources.produce(new AdditionalStaticResourceBuildItem("/antora/" + relPath, false));
log.infof("Producing META-INF/antora/%s", relPath);
staticResourceProducer.produce(new GeneratedWebResourceBuildItem("/" + relPath, bytes));
}
});
} catch (IOException e) {
Expand All @@ -207,8 +200,7 @@ private Path gitRepoRoot(Path startDir) {
private static PlaybookInfo augmentAntoraPlaybook(
Path gitRepoRoot,
Path baseDir,
Path targetDir,
BuildProducer<AdditionalStaticResourceBuildItem> staticResources) {
Path targetDir) {

final Path augmentedAntoraPlaybookYml = targetDir.resolve("antora-playbook.yml");

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.quarkiverse.antora.deployment;

import static io.quarkiverse.antora.WebBundlerResourceHandler.META_INF_ANTORA;

import io.quarkus.builder.item.MultiBuildItem;
import io.quarkus.runtime.util.HashUtil;

public final class GeneratedWebResourceBuildItem extends MultiBuildItem {

private final String publicPath;
private final byte[] content;
private final String contentHash;

public GeneratedWebResourceBuildItem(String publicPath, byte[] content) {
this.publicPath = publicPath;
this.content = content;
this.contentHash = HashUtil.sha512(content);
}

public String resourceName() {
return META_INF_ANTORA + publicPath;
}

public String publicPath() {
return publicPath;
}

public byte[] content() {
return content;
}

public String contentHash() {
return contentHash;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package io.quarkiverse.antora.deployment;

import static io.quarkiverse.antora.WebBundlerResourceHandler.DEFAULT_ROUTE_ORDER;
import static io.quarkiverse.antora.WebBundlerResourceHandler.META_INF_ANTORA;
import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.jboss.logging.Logger;

import io.quarkiverse.antora.WebBundlerResourceRecorder;
import io.quarkus.bootstrap.workspace.ArtifactSources;
import io.quarkus.bootstrap.workspace.SourceDir;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.GeneratedResourceBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.LiveReloadBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem;
import io.quarkus.deployment.util.FileUtil;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.vertx.http.deployment.RouteBuildItem;

public class GeneratedWebResourcesProcessor {
private static final Logger LOGGER = Logger.getLogger(GeneratedWebResourcesProcessor.class);

@BuildStep
public void processStaticFiles(
List<GeneratedWebResourceBuildItem> staticResources,
BuildProducer<GeneratedResourceBuildItem> prodResourcesProducer,
BuildProducer<NativeImageResourceBuildItem> nativeImageResourcesProducer,
CurateOutcomeBuildItem curateOutcome,
OutputTargetBuildItem outputTarget,
LiveReloadBuildItem liveReload,
LaunchModeBuildItem launchModeBuildItem) {
if (staticResources.isEmpty()) {
return;
}
if (launchModeBuildItem.getLaunchMode().isDevOrTest()) {
// in dev and test we need to write the files to the build directory
final Path buildDir = getBuildDirectory(outputTarget, curateOutcome);
// Clean up the directory
try {
FileUtil.deleteDirectory(buildDir.resolve(META_INF_ANTORA));
} catch (IOException e) {
throw new RuntimeException(e);
}

// Write the files
for (GeneratedWebResourceBuildItem r : staticResources) {
createGeneratedResourceOnDisk(r, buildDir);
}
}

for (GeneratedWebResourceBuildItem staticResource : staticResources) {
// generated resource for prod
prodResourcesProducer.produce(new GeneratedResourceBuildItem(staticResource.resourceName(),
staticResource.content(), false));
// for native
nativeImageResourcesProducer.produce(new NativeImageResourceBuildItem(staticResource.resourceName()));
}
}

private static void createGeneratedResourceOnDisk(GeneratedWebResourceBuildItem r, Path buildDir) {
final Path targetPath = buildDir.resolve(r.resourceName());
try {
Files.deleteIfExists(targetPath);
Files.createDirectories(targetPath.getParent());
Files.write(targetPath, r.content());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

@BuildStep
@Record(RUNTIME_INIT)
public void runtimeInit(
LaunchModeBuildItem launchMode,
List<GeneratedWebResourceBuildItem> staticResources,
WebBundlerResourceRecorder recorder,
CurateOutcomeBuildItem curateOutcome,
OutputTargetBuildItem outputTarget,
BuildProducer<RouteBuildItem> routes) throws IOException {
if (!staticResources.isEmpty()) {
String metaInfWeb = launchMode.getLaunchMode().isDevOrTest()
? getBuildDirectory(outputTarget, curateOutcome).resolve(META_INF_ANTORA).toAbsolutePath().toString()
: null;

routes.produce(RouteBuildItem.builder().orderedRoute("/*", DEFAULT_ROUTE_ORDER)
.handler(recorder.createHandler(metaInfWeb,
staticResources.stream().map(GeneratedWebResourceBuildItem::publicPath)
.collect(Collectors.toSet()),
launchMode.getLaunchMode() == LaunchMode.DEVELOPMENT))
.build());
}
}

public static Path getBuildDirectory(OutputTargetBuildItem outputTarget, CurateOutcomeBuildItem curateOutcomeBuildItem) {
if (Files.exists(outputTarget.getOutputDirectory().resolve("classes/META-INF/resources"))) {
return outputTarget.getOutputDirectory().resolve("classes");
}
if (Files.exists(outputTarget.getOutputDirectory().resolve("resources/main/META-INF/resources"))) {
return outputTarget.getOutputDirectory().resolve("resources/main");
}
ArtifactSources src = curateOutcomeBuildItem.getApplicationModel().getAppArtifact().getSources();
if (src != null) { // shouldn't be null in dev mode
Collection<SourceDir> dirs = src.getResourceDirs();
if (dirs.isEmpty()) {
// in the module has no resources dir?
dirs = src.getSourceDirs();
}
if (!dirs.isEmpty()) {
final Set<Path> outputDirs = dirs.stream().map(SourceDir::getOutputDir).collect(Collectors.toSet());
if (outputDirs.size() > 1) {
LOGGER.warnf("Multiple resources directories found, using the first one in the list: %s",
outputDirs);
}
// pick the first resources output dir
return outputDirs.iterator().next();

}
}
throw new RuntimeException("Unable to determine the build directory");
}
}
5 changes: 5 additions & 0 deletions dev-mode-test/antora.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: quarkus-antora
title: Antora
version: dev
nav:
- modules/ROOT/nav.adoc
1 change: 1 addition & 0 deletions dev-mode-test/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* xref:index.adoc[Getting started]
6 changes: 6 additions & 0 deletions dev-mode-test/modules/ROOT/pages/index.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
= Lorem ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Loading

0 comments on commit 5fce2d2

Please sign in to comment.