io.quarkus.vertx.http.runtime.devmode.ResourceNotFoundData Maven / Gradle / Ivy
The newest version!
package io.quarkus.vertx.http.runtime.devmode;
import static io.quarkus.runtime.TemplateHtmlBuilder.adjustRoot;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UncheckedIOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jakarta.enterprise.context.ApplicationScoped;
import org.jboss.logging.Logger;
import io.quarkus.runtime.TemplateHtmlBuilder;
import io.quarkus.runtime.util.ClassPathUtils;
import io.smallrye.common.annotation.NonBlocking;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
@ApplicationScoped
public class ResourceNotFoundData {
private static final Logger LOG = Logger.getLogger(ResourceNotFoundData.class);
private static volatile List runtimeRoutes = null;
private static volatile List servletMappings = new ArrayList<>();
private static final String META_INF_RESOURCES = "META-INF/resources";
private String baseUrl;
private String httpRoot;
private List endpointRoutes;
private Set staticRoots;
private List additionalEndpoints;
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
public void setHttpRoot(String httpRoot) {
this.httpRoot = httpRoot;
}
public void setEndpointRoutes(List endpointRoutes) {
this.endpointRoutes = endpointRoutes;
}
public void setStaticRoots(Set staticRoots) {
this.staticRoots = staticRoots;
}
public void setAdditionalEndpoints(List additionalEndpoints) {
this.additionalEndpoints = additionalEndpoints;
}
public static void addServlet(String mapping) {
servletMappings.add(mapping);
}
public static void setRuntimeRoutes(List routeDescriptions) {
runtimeRoutes = routeDescriptions;
}
public String getHTMLContent() {
List combinedRoutes = getCombinedRoutes();
TemplateHtmlBuilder builder = new TemplateHtmlBuilder(this.baseUrl,
HEADING, "", "Resources overview", List.of());
builder.resourcesStart(RESOURCE_ENDPOINTS);
for (RouteDescription resource : combinedRoutes) {
builder.resourcePath(adjustRoot(this.httpRoot, resource.getBasePath()));
for (RouteMethodDescription method : resource.getCalls()) {
builder.method(method.getHttpMethod(),
adjustRoot(this.httpRoot, method.getFullPath()));
if (method.getJavaMethod() != null) {
builder.listItem(method.getJavaMethod());
}
if (method.getConsumes() != null) {
builder.consumes(method.getConsumes());
}
if (method.getProduces() != null) {
builder.produces(method.getProduces());
}
builder.methodEnd();
}
builder.resourceEnd();
}
if (combinedRoutes.isEmpty()) {
builder.noResourcesFound();
}
builder.resourcesEnd();
if (!servletMappings.isEmpty()) {
builder.resourcesStart(SERVLET_MAPPINGS);
for (String servletMapping : servletMappings) {
builder.servletMapping(adjustRoot(this.httpRoot, servletMapping));
}
builder.resourcesEnd();
}
// Static Resources
if (!this.staticRoots.isEmpty()) {
List resources = findRealResources();
if (!resources.isEmpty()) {
builder.resourcesStart(STATIC_RESOURCES);
for (String staticResource : resources) {
builder.staticResourcePath(adjustRoot(this.httpRoot, staticResource));
}
builder.resourcesEnd();
}
}
// Additional Endpoints
if (!this.additionalEndpoints.isEmpty()) {
List endpoints = getSortedAdditionalRouteDescriptions();
builder.resourcesStart(ADDITIONAL_ENDPOINTS);
for (AdditionalRouteDescription additionalEndpoint : endpoints) {
builder.staticResourcePath(additionalEndpoint.getUri(), additionalEndpoint.getDescription());
}
builder.resourcesEnd();
}
return builder.toString();
}
@NonBlocking
public JsonObject getJsonContent() {
List combinedRoutes = getCombinedRoutes();
JsonObject infoMap = new JsonObject();
// REST Endpoints
if (!combinedRoutes.isEmpty()) {
JsonArray r = new JsonArray();
for (RouteDescription resource : combinedRoutes) {
String path = adjustRoot(this.httpRoot, resource.getBasePath());
for (RouteMethodDescription method : resource.getCalls()) {
String description = method.getHttpMethod();
if (method.getConsumes() != null) {
description = description + " (consumes: " + method.getConsumes() + ")";
}
if (method.getProduces() != null) {
description = description + " (produces:" + method.getProduces() + ")";
}
if (method.getJavaMethod() != null) {
description = description + " (java:" + method.getJavaMethod() + ")";
}
r.add(JsonObject.of(URI, adjustRoot(this.httpRoot, method.getFullPath()),
DESCRIPTION, description));
}
}
infoMap.put(RESOURCE_ENDPOINTS, r);
}
// Servlets
if (!servletMappings.isEmpty()) {
JsonArray sm = new JsonArray();
for (String servletMapping : servletMappings) {
sm.add(JsonObject.of(URI, adjustRoot(this.httpRoot, servletMapping), DESCRIPTION,
EMPTY));
}
infoMap.put(SERVLET_MAPPINGS, sm);
}
// Static Resources
if (!this.staticRoots.isEmpty()) {
List resources = findRealResources();
if (!resources.isEmpty()) {
JsonArray sr = new JsonArray();
for (String staticResource : resources) {
sr.add(JsonObject.of(URI, adjustRoot(this.httpRoot, staticResource), DESCRIPTION,
EMPTY));
}
infoMap.put(STATIC_RESOURCES, sr);
}
}
// Additional Endpoints
if (!this.additionalEndpoints.isEmpty()) {
JsonArray ae = new JsonArray();
List endpoints = getSortedAdditionalRouteDescriptions();
for (AdditionalRouteDescription additionalEndpoint : endpoints) {
ae.add(JsonObject.of(URI, additionalEndpoint.getUri(), DESCRIPTION, additionalEndpoint.getDescription()));
}
infoMap.put(ADDITIONAL_ENDPOINTS, ae);
}
return infoMap;
}
@NonBlocking
public String getTextContent() {
List combinedRoutes = getCombinedRoutes();
try (StringWriter sw = new StringWriter()) {
sw.write(NL + HEADING + NL);
sw.write("------------------------" + NL);
sw.write(NL);
// REST Endpoints
if (!combinedRoutes.isEmpty()) {
sw.write(RESOURCE_ENDPOINTS + NL);
for (RouteDescription resource : combinedRoutes) {
for (RouteMethodDescription method : resource.getCalls()) {
String description = method.getHttpMethod();
if (method.getConsumes() != null) {
description = description + " (consumes: " + method.getConsumes() + ")";
}
if (method.getProduces() != null) {
description = description + " (produces:" + method.getProduces() + ")";
}
if (method.getJavaMethod() != null) {
description = description + " (java:" + method.getJavaMethod() + ")";
}
sw.write(TAB + "- " + adjustRoot(this.httpRoot, method.getFullPath()) + NL);
sw.write(TAB + TAB + "- " + description + NL);
}
}
sw.write(NL);
}
// Servlets
if (!servletMappings.isEmpty()) {
sw.write(SERVLET_MAPPINGS + NL);
for (String servletMapping : servletMappings) {
sw.write(TAB + "- " + adjustRoot(this.httpRoot, servletMapping) + NL);
}
sw.write(NL);
}
// Static Resources
if (!this.staticRoots.isEmpty()) {
List resources = findRealResources();
if (!resources.isEmpty()) {
sw.write(STATIC_RESOURCES + NL);
for (String staticResource : resources) {
sw.write(TAB + "- " + adjustRoot(this.httpRoot, staticResource) + NL);
}
sw.write(NL);
}
}
// Additional Endpoints
if (!this.additionalEndpoints.isEmpty()) {
sw.write(ADDITIONAL_ENDPOINTS + NL);
List endpoints = getSortedAdditionalRouteDescriptions();
for (AdditionalRouteDescription additionalEndpoint : endpoints) {
sw.write(TAB + "- " + additionalEndpoint.getUri() + NL);
sw.write(TAB + TAB + "- " + additionalEndpoint.getDescription() + NL);
}
sw.write(NL);
}
return sw.toString();
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
private List getCombinedRoutes() {
// Endpoints
List combinedRoutes = new ArrayList<>();
if (this.runtimeRoutes != null) {
combinedRoutes.addAll(this.runtimeRoutes);
}
if (endpointRoutes != null) {
combinedRoutes.addAll(this.endpointRoutes);
}
return combinedRoutes;
}
private List findRealResources() {
//we need to check for web resources in order to get welcome files to work
//this kinda sucks
Set knownFiles = new HashSet<>();
for (String staticResourceRoot : this.staticRoots) {
if (staticResourceRoot != null) {
Path resource = Paths.get(staticResourceRoot);
if (Files.exists(resource)) {
try (Stream fileTreeElements = Files.walk(resource)) {
fileTreeElements.forEach(new Consumer() {
@Override
public void accept(java.nio.file.Path path) {
// Skip META-INF/resources entry
if (resource.equals(path)) {
return;
}
java.nio.file.Path rel = resource.relativize(path);
if (!Files.isDirectory(path)) {
knownFiles.add("/" + rel.toString());
}
}
});
} catch (IOException e) {
LOG.error("Failed to read static resources", e);
}
}
}
}
try {
ClassPathUtils.consumeAsPaths(META_INF_RESOURCES, p -> {
collectKnownPaths(p, knownFiles);
});
} catch (IOException e) {
LOG.error("Failed to read static resources", e);
}
//limit to 1000 to not have to many files to display
return knownFiles.stream().filter(this::isHtmlFileName).limit(1000).distinct().sorted(Comparator.naturalOrder())
.collect(Collectors.toList());
}
private void collectKnownPaths(java.nio.file.Path resource, Set knownPaths) {
try {
Files.walkFileTree(resource, new SimpleFileVisitor() {
@Override
public FileVisitResult visitFile(java.nio.file.Path p, BasicFileAttributes attrs)
throws IOException {
String file = resource.relativize(p).toString();
// Windows has a backslash
file = file.replace('\\', '/');
if (!file.startsWith("_static/") && !file.startsWith("webjars/")) {
knownPaths.add("/" + file);
}
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private boolean isHtmlFileName(String fileName) {
return fileName.endsWith(".html") || fileName.endsWith(".htm") || fileName.endsWith(".xhtml");
}
private List getSortedAdditionalRouteDescriptions() {
return this.additionalEndpoints.stream().sorted(
Comparator.comparingInt((AdditionalRouteDescription desc) -> desc.getUri().split("/").length)
.thenComparing(AdditionalRouteDescription::getUri))
.toList();
}
private static final String HEADING = "404 - Resource Not Found";
private static final String RESOURCE_ENDPOINTS = "Resource Endpoints";
private static final String SERVLET_MAPPINGS = "Servlet mappings";
private static final String STATIC_RESOURCES = "Static resources";
private static final String ADDITIONAL_ENDPOINTS = "Additional endpoints";
private static final String URI = "uri";
private static final String DESCRIPTION = "description";
private static final String EMPTY = "";
private static final String NL = "\n";
private static final String TAB = "\t";
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy