summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web
diff options
context:
space:
mode:
Diffstat (limited to 'opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web')
-rw-r--r--opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/runner/OpenDCRunnerBuildItem.java42
-rw-r--r--opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/runner/OpenDCRunnerConfig.java38
-rw-r--r--opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/runner/OpenDCRunnerProcessor.java120
-rw-r--r--opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/ui/NextRouteManifestBuildItem.java78
-rw-r--r--opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/ui/OpenDCUiProcessor.java57
-rw-r--r--opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/ui/QuinoaNextRoutingProcessor.java154
6 files changed, 489 insertions, 0 deletions
diff --git a/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/runner/OpenDCRunnerBuildItem.java b/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/runner/OpenDCRunnerBuildItem.java
new file mode 100644
index 00000000..544365c8
--- /dev/null
+++ b/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/runner/OpenDCRunnerBuildItem.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.quarkus.deployment.runner;
+
+import io.quarkus.builder.item.SimpleBuildItem;
+import io.quarkus.runtime.RuntimeValue;
+import org.opendc.web.runner.OpenDCRunner;
+
+/**
+ * A {@link SimpleBuildItem} that produces an {@link OpenDCRunner} instance.
+ */
+public final class OpenDCRunnerBuildItem extends SimpleBuildItem {
+ private final RuntimeValue<OpenDCRunner> runner;
+
+ public OpenDCRunnerBuildItem(RuntimeValue<OpenDCRunner> runner) {
+ this.runner = runner;
+ }
+
+ public RuntimeValue<OpenDCRunner> getRunner() {
+ return runner;
+ }
+}
diff --git a/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/runner/OpenDCRunnerConfig.java b/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/runner/OpenDCRunnerConfig.java
new file mode 100644
index 00000000..1bc201b2
--- /dev/null
+++ b/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/runner/OpenDCRunnerConfig.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.quarkus.deployment.runner;
+
+import io.quarkus.runtime.annotations.ConfigItem;
+import io.quarkus.runtime.annotations.ConfigRoot;
+
+/**
+ * Build-time configuration for the OpenDC web runner extension.
+ */
+@ConfigRoot(name = "opendc-runner")
+public class OpenDCRunnerConfig {
+ /**
+ * A flag to include the OpenDC web runner extension into the build.
+ */
+ @ConfigItem(defaultValue = "true")
+ boolean include;
+}
diff --git a/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/runner/OpenDCRunnerProcessor.java b/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/runner/OpenDCRunnerProcessor.java
new file mode 100644
index 00000000..a796c01d
--- /dev/null
+++ b/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/runner/OpenDCRunnerProcessor.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.quarkus.deployment.runner;
+
+import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT;
+
+import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
+import io.quarkus.deployment.annotations.BuildProducer;
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.annotations.Record;
+import io.quarkus.deployment.builditem.*;
+import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem;
+import io.quarkus.deployment.util.ServiceUtil;
+import io.quarkus.runtime.RuntimeValue;
+import java.io.IOException;
+import java.util.Set;
+import java.util.function.BooleanSupplier;
+import org.opendc.trace.spi.TraceFormat;
+import org.opendc.web.quarkus.runtime.runner.OpenDCRunnerRecorder;
+import org.opendc.web.quarkus.runtime.runner.OpenDCRunnerRuntimeConfig;
+import org.opendc.web.runner.JobManager;
+import org.opendc.web.runner.OpenDCRunner;
+
+/**
+ * Build processor for the OpenDC web runner Quarkus extension.
+ */
+public class OpenDCRunnerProcessor {
+
+ private static final String FEATURE = "opendc-runner";
+
+ /**
+ * Provide the {@link FeatureBuildItem} for this Quarkus extension.
+ */
+ @BuildStep(onlyIf = IsIncluded.class)
+ public FeatureBuildItem feature() {
+ return new FeatureBuildItem(FEATURE);
+ }
+
+ /**
+ * Build step to register the trace formats used by OpenDC.
+ */
+ @BuildStep
+ void registerTraceFormats(BuildProducer<ServiceProviderBuildItem> services) throws IOException {
+ String service = "META-INF/services/" + TraceFormat.class.getName();
+
+ Set<String> implementations =
+ ServiceUtil.classNamesNamedIn(Thread.currentThread().getContextClassLoader(), service);
+
+ services.produce(
+ new ServiceProviderBuildItem(TraceFormat.class.getName(), implementations.toArray(new String[0])));
+ }
+
+ /**
+ * Mark {@link JobManager} as unremoveable, since we look up this service dynamically in {@link OpenDCRunnerRecorder}.
+ */
+ @BuildStep
+ UnremovableBeanBuildItem unremovableBeans() {
+ return UnremovableBeanBuildItem.beanTypes(JobManager.class);
+ }
+
+ /**
+ * Build step to create the runner service.
+ */
+ @BuildStep(onlyIf = IsIncluded.class)
+ @Record(RUNTIME_INIT)
+ ServiceStartBuildItem createRunnerService(
+ OpenDCRunnerRecorder recorder,
+ OpenDCRunnerRuntimeConfig config,
+ BuildProducer<OpenDCRunnerBuildItem> runnerBuildItem) {
+ RuntimeValue<OpenDCRunner> runner = recorder.createRunner(config);
+ runnerBuildItem.produce(new OpenDCRunnerBuildItem(runner));
+ return new ServiceStartBuildItem("OpenDCRunnerService");
+ }
+
+ /**
+ * Build step to start the runner service.
+ */
+ @BuildStep(onlyIf = IsIncluded.class)
+ @Record(RUNTIME_INIT)
+ void startRunnerService(
+ ApplicationStartBuildItem start,
+ OpenDCRunnerBuildItem runnerBuildItem,
+ OpenDCRunnerRecorder recorder,
+ OpenDCRunnerRuntimeConfig config,
+ ShutdownContextBuildItem shutdownContextBuildItem) {
+ recorder.startRunner(runnerBuildItem.getRunner(), config, shutdownContextBuildItem);
+ }
+
+ /**
+ * A {@link BooleanSupplier} to determine if the OpenDC web runner extension should be included.
+ */
+ private static class IsIncluded implements BooleanSupplier {
+ OpenDCRunnerConfig config;
+
+ @Override
+ public boolean getAsBoolean() {
+ return config.include;
+ }
+ }
+}
diff --git a/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/ui/NextRouteManifestBuildItem.java b/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/ui/NextRouteManifestBuildItem.java
new file mode 100644
index 00000000..4640b04b
--- /dev/null
+++ b/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/ui/NextRouteManifestBuildItem.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.quarkus.deployment.ui;
+
+import io.quarkus.builder.item.SimpleBuildItem;
+import java.util.List;
+
+/**
+ * Build item containing the route manifest of the Next.js application.
+ */
+public final class NextRouteManifestBuildItem extends SimpleBuildItem {
+
+ private final boolean custom404;
+ private final List<Page> pages;
+ private final List<Redirect> redirects;
+
+ /**
+ * Construct a {@link NextRouteManifestBuildItem} object.
+ *
+ * @param routes The routes defined by Next.js.
+ * @param redirects The redirects that have been defined by Next.js.
+ * @param custom404 A flag to indicate that custom 404 pages are enabled.
+ */
+ public NextRouteManifestBuildItem(List<Page> routes, List<Redirect> redirects, boolean custom404) {
+ this.custom404 = custom404;
+ this.pages = routes;
+ this.redirects = redirects;
+ }
+
+ public List<Page> getPages() {
+ return pages;
+ }
+
+ public List<Redirect> getRedirects() {
+ return redirects;
+ }
+
+ public boolean hasCustom404() {
+ return this.custom404;
+ }
+
+ /**
+ * A redirect defined by the Next.js routes manifest.
+ *
+ * @param path The path that should result in a redirect.
+ * @param destination The destination of the redirect.
+ * @param statusCode The status code of the redirect.
+ */
+ public record Redirect(String path, String destination, int statusCode) {}
+
+ /**
+ * A page defined by the Next.js routes manifest.
+ *
+ * @param path The path that to the page.
+ * @param name The name of the page.
+ */
+ public record Page(String path, String name) {}
+}
diff --git a/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/ui/OpenDCUiProcessor.java b/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/ui/OpenDCUiProcessor.java
new file mode 100644
index 00000000..8b2c0244
--- /dev/null
+++ b/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/ui/OpenDCUiProcessor.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.quarkus.deployment.ui;
+
+import io.quarkus.deployment.annotations.BuildProducer;
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.annotations.ExecutionTime;
+import io.quarkus.deployment.annotations.Record;
+import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem;
+import io.quarkus.vertx.http.deployment.RouteBuildItem;
+import org.opendc.web.quarkus.runtime.ui.OpenDCUiConfig;
+import org.opendc.web.quarkus.runtime.ui.OpenDCUiRecorder;
+
+/**
+ * Quarkus build processor for the OpenDC web UI.
+ */
+public class OpenDCUiProcessor {
+ /**
+ * Register a route handler for serving the config of the OpenDC web UI.
+ */
+ @BuildStep
+ @Record(ExecutionTime.RUNTIME_INIT)
+ public RouteBuildItem registerConfigHandler(
+ OpenDCUiRecorder openDCUiRecorder,
+ BuildProducer<RouteBuildItem> routes,
+ HttpRootPathBuildItem httpRootPathBuildItem,
+ OpenDCUiConfig openDCUiConfig) {
+
+ String basePath = httpRootPathBuildItem.getRootPath();
+
+ return httpRootPathBuildItem
+ .routeBuilder()
+ .route(basePath + "/__ENV.js")
+ .handler(openDCUiRecorder.configHandler(openDCUiConfig))
+ .build();
+ }
+}
diff --git a/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/ui/QuinoaNextRoutingProcessor.java b/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/ui/QuinoaNextRoutingProcessor.java
new file mode 100644
index 00000000..ddaa8809
--- /dev/null
+++ b/opendc-web/opendc-web-quarkus-deployment/src/main/java/org/opendc/web/quarkus/deployment/ui/QuinoaNextRoutingProcessor.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2023 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.web.quarkus.deployment.ui;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.quarkiverse.quinoa.deployment.items.BuiltResourcesBuildItem;
+import io.quarkiverse.quinoa.deployment.items.ConfiguredQuinoaBuildItem;
+import io.quarkiverse.quinoa.deployment.items.TargetDirBuildItem;
+import io.quarkus.deployment.IsNormal;
+import io.quarkus.deployment.annotations.BuildProducer;
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.annotations.ExecutionTime;
+import io.quarkus.deployment.annotations.Record;
+import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem;
+import io.quarkus.vertx.http.deployment.RouteBuildItem;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.regex.Pattern;
+import org.opendc.web.quarkus.runtime.ui.QuinoaNextRoutingRecorder;
+
+/**
+ * Quarkus build processor for enabling dynamic routes and redirects in Quinoa for Next.js applications.
+ */
+public class QuinoaNextRoutingProcessor {
+ private static final Pattern PATH_PARAM_PATTERN = Pattern.compile("\\[(\\w+)]");
+ private final ObjectMapper objectMapper = new ObjectMapper();
+
+ /**
+ * Build the dynamic routes of the application based on the route manifest generated by Next.js.
+ *
+ * @param configuredQuinoa Quinoa configuration pointing to the UI directory.
+ * @param targetDirBuildItem Dependency on the build directory to ensure the Next.js is built before this build step
+ * is run.
+ * @return Routing manifest generated by the Next.js application.
+ */
+ @BuildStep
+ public NextRouteManifestBuildItem buildRoutes(
+ ConfiguredQuinoaBuildItem configuredQuinoa, TargetDirBuildItem targetDirBuildItem) throws IOException {
+ if (configuredQuinoa == null) {
+ return null;
+ }
+
+ Path routeManifestPath = configuredQuinoa.uiDir().resolve(".next/routes-manifest.json");
+ if (Files.notExists(routeManifestPath)) {
+ throw new FileNotFoundException("Cannot find " + routeManifestPath + " for creating route map");
+ }
+
+ JsonNode routeManifest = objectMapper.readTree(routeManifestPath.toFile());
+
+ var pages = new ArrayList<NextRouteManifestBuildItem.Page>();
+ for (Iterator<JsonNode> it = routeManifest.get("staticRoutes").elements(); it.hasNext(); ) {
+ JsonNode route = it.next();
+
+ String page = route.get("page").asText();
+
+ // Static routes do not have any path parameters
+ pages.add(new NextRouteManifestBuildItem.Page(page, page));
+ }
+
+ for (Iterator<JsonNode> it = routeManifest.get("dynamicRoutes").elements(); it.hasNext(); ) {
+ JsonNode route = it.next();
+
+ String page = route.get("page").asText();
+ String path = PATH_PARAM_PATTERN.matcher(page).replaceAll(r -> ":" + r.group(1));
+
+ pages.add(new NextRouteManifestBuildItem.Page(path, page));
+ }
+
+ var redirects = new ArrayList<NextRouteManifestBuildItem.Redirect>();
+ for (Iterator<JsonNode> it = routeManifest.get("redirects").elements(); it.hasNext(); ) {
+ JsonNode redirect = it.next();
+ if (redirect.has("internal")) {
+ continue;
+ }
+
+ int statusCode = redirect.get("statusCode").asInt();
+ String path = redirect.get("source").asText();
+ String destination = redirect.get("destination").asText();
+
+ if (path.isEmpty()) {
+ path = "/";
+ }
+
+ redirects.add(new NextRouteManifestBuildItem.Redirect(path, destination, statusCode));
+ }
+
+ var custom404 = routeManifest.get("pages404").asBoolean();
+ return new NextRouteManifestBuildItem(pages, redirects, custom404);
+ }
+
+ /**
+ * Register the dynamic routes and redirects of the Next.js application.
+ */
+ @Record(ExecutionTime.RUNTIME_INIT)
+ @BuildStep(onlyIf = IsNormal.class)
+ public void registerNextRoutes(
+ QuinoaNextRoutingRecorder recorder,
+ BuildProducer<RouteBuildItem> routes,
+ HttpRootPathBuildItem httpRootPathBuildItem,
+ BuiltResourcesBuildItem uiResources,
+ NextRouteManifestBuildItem routeManifestBuildItem) {
+
+ if (uiResources.getNames().isEmpty()) {
+ return;
+ }
+
+ String basePath = httpRootPathBuildItem.getRootPath();
+
+ /* Construct redirects */
+ for (var redirect : routeManifestBuildItem.getRedirects()) {
+ String destination = basePath.equals("/") ? redirect.destination() : basePath + redirect.destination();
+
+ routes.produce(httpRootPathBuildItem
+ .routeBuilder()
+ .route(basePath + redirect.path())
+ .handler(recorder.redirectHandler(destination, redirect.statusCode()))
+ .build());
+ }
+
+ /* Construct dynamic routes */
+ for (var page : routeManifestBuildItem.getPages()) {
+ routes.produce(httpRootPathBuildItem
+ .routeBuilder()
+ .route(basePath + page.path())
+ .handler(recorder.pageHandler(basePath, page.name()))
+ .build());
+ }
+ }
+}