diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a940ba..400a430 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ Proteus Changelog. ## Unreleased ### No issue +**Removed swagger module and updated dependencies.** + + +[999223901c55f4c](https://github.com/noboomu/proteus/commit/999223901c55f4c) Joshua Bauer *2020-11-20 21:38:54* + **Async service timeouts.** diff --git a/proteus-core/src/main/java/io/sinistral/proteus/ProteusApplication.java b/proteus-core/src/main/java/io/sinistral/proteus/ProteusApplication.java index d328b65..02a3574 100644 --- a/proteus-core/src/main/java/io/sinistral/proteus/ProteusApplication.java +++ b/proteus-core/src/main/java/io/sinistral/proteus/ProteusApplication.java @@ -1,5 +1,6 @@ package io.sinistral.proteus; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.util.concurrent.MoreExecutors; @@ -244,12 +245,17 @@ public void failure(Service service) undertow.start(); + Duration timeout = config.getDuration("application.services.timeout"); + try { - serviceManager.startAsync().awaitHealthy(config.getDuration("application.services.timeout")); - } catch( Exception e ) + serviceManager.startAsync().awaitHealthy(timeout); + } catch( TimeoutException e ) + { + log.error("Failed start to services within {} minutes",timeout,e); + } catch (Exception e) { - log.error("Failed start to services within 2 minutes",e); + log.error("Failed to start services",e); } // serviceManager.startAsync(); @@ -477,7 +483,15 @@ public ProteusApplication addDefaultRoutes(RoutingHandler router) router.add(Methods.GET, statusPath, (final HttpServerExchange exchange) -> { exchange.getResponseHeaders().add(Headers.CONTENT_TYPE, MediaType.TEXT_PLAIN); - exchange.getResponseSender().send("OK"); + + if(this.serviceManager.servicesByState().values().stream().allMatch(Service::isRunning)) + { + exchange.setStatusCode(200).getResponseSender().send("OK"); + } + else + { + exchange.setStatusCode(500).getResponseSender().send("NOT_HEALTHY"); + } }); this.registeredEndpoints.add(EndpointInfo.builder().withConsumes("*/*").withProduces("text/plain").withPathTemplate(statusPath).withControllerName("Internal").withMethod(Methods.GET).build()); @@ -687,4 +701,6 @@ public void printStatus() log.info(sb.toString()); } + + } diff --git a/proteus-core/src/main/java/io/sinistral/proteus/protocol/HealthSummary.java b/proteus-core/src/main/java/io/sinistral/proteus/protocol/HealthSummary.java new file mode 100644 index 0000000..ecc831a --- /dev/null +++ b/proteus-core/src/main/java/io/sinistral/proteus/protocol/HealthSummary.java @@ -0,0 +1,7 @@ +package io.sinistral.proteus.protocol; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class HealthSummary { +} diff --git a/proteus-core/src/main/java/io/sinistral/proteus/server/endpoints/EndpointInfo.java b/proteus-core/src/main/java/io/sinistral/proteus/server/endpoints/EndpointInfo.java index 94eae04..621d53c 100644 --- a/proteus-core/src/main/java/io/sinistral/proteus/server/endpoints/EndpointInfo.java +++ b/proteus-core/src/main/java/io/sinistral/proteus/server/endpoints/EndpointInfo.java @@ -3,12 +3,14 @@ */ package io.sinistral.proteus.server.endpoints; +import com.fasterxml.jackson.annotation.JsonInclude; import io.undertow.util.HttpString; /** * @author jbauer * */ +@JsonInclude(JsonInclude.Include.NON_NULL) public class EndpointInfo implements Comparable { private String consumes = "*/*"; diff --git a/proteus-core/src/main/java/io/sinistral/proteus/services/DefaultService.java b/proteus-core/src/main/java/io/sinistral/proteus/services/DefaultService.java index ffe1b3d..b40be09 100644 --- a/proteus-core/src/main/java/io/sinistral/proteus/services/DefaultService.java +++ b/proteus-core/src/main/java/io/sinistral/proteus/services/DefaultService.java @@ -60,6 +60,8 @@ protected void startUp() throws Exception + + } diff --git a/proteus-openapi/src/main/java/io/sinistral/proteus/openapi/services/OpenAPIService.java b/proteus-openapi/src/main/java/io/sinistral/proteus/openapi/services/OpenAPIService.java index bb06132..8830861 100644 --- a/proteus-openapi/src/main/java/io/sinistral/proteus/openapi/services/OpenAPIService.java +++ b/proteus-openapi/src/main/java/io/sinistral/proteus/openapi/services/OpenAPIService.java @@ -42,6 +42,7 @@ import javax.ws.rs.HttpMethod; import javax.ws.rs.core.MediaType; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.ByteBuffer; @@ -58,382 +59,399 @@ /** * A service for generating and serving an OpenAPI v3 spec and ui. + * * @author jbauer */ @Singleton -public class OpenAPIService extends DefaultService implements Supplier -{ - private static Logger log = LoggerFactory.getLogger(OpenAPIService.class.getCanonicalName()); - - protected final String resourcePathPrefix = "openapi"; - - protected ObjectMapper mapper; - protected ObjectWriter writer ; - protected ObjectMapper yamlMapper; - protected Path resourcePath = null; - protected ClassLoader serviceClassLoader = null; - protected OpenAPI openApi = null; - protected String spec = null; - protected String indexHTML = null; - protected String redocHTML = null; - - protected final String resourcePrefix = "io/sinistral/proteus/openapi"; - - @Inject - @Named("openapi.basePath") - protected String basePath; - - @Inject - @Named("openapi.specFilename") - protected String specFilename; - - @Inject - @Named("openapi") - protected Config openAPIConfig; - - @Inject - @Named("application.name") - protected String applicationName; - - @Inject - @Named("openapi.port") - protected Integer port; - - @Inject - @Named("openapi.redocPath") - protected String redocPath; - - @Inject - @Named("application.path") - protected String applicationPath; - - @Inject - protected RoutingHandler router; - - @Inject - @Named("registeredEndpoints") - protected Set registeredEndpoints; - - @Inject - @Named("registeredControllers") - protected Set> registeredControllers; - - @Inject(optional = true) - @Named("jackson.jsonView.queryParameterName") - protected String jsonViewQueryParameterName; - - @Inject - @Named("registeredHandlerWrappers") - protected Map registeredHandlerWrappers; - - - - public OpenAPIService() - { - mapper = Json.mapper(); - - mapper.registerModule(new Jdk8Module()); - - yamlMapper = Yaml.mapper(); - writer = Yaml.pretty(); - } - - protected void generateHTML() - { - try - { - - try (InputStream templateInputStream = this.getClass().getClassLoader().getResourceAsStream(resourcePrefix + "/index.html")) - { - - byte[] templateBytes = IOUtils.toByteArray(templateInputStream); - String templateString = new String(templateBytes, Charset.defaultCharset()); - - templateString = templateString.replaceAll("\\{\\{ basePath \\}\\}", basePath); - templateString = templateString.replaceAll("\\{\\{ title \\}\\}", applicationName + " Swagger UI"); - this.indexHTML = templateString; - } - - try (InputStream templateInputStream = getClass().getClassLoader().getResourceAsStream(resourcePrefix + "/redoc.html")) - { - byte[] templateBytes = IOUtils.toByteArray(templateInputStream); - - this.redocHTML = new String(templateBytes, Charset.defaultCharset()); - } - - URL url = this.getClass().getClassLoader().getResource(resourcePrefix); +public class OpenAPIService extends DefaultService implements Supplier { - if (url.toExternalForm().contains("!")) - { - log.debug("Copying OpenAPI resources..."); + private static Logger log = LoggerFactory.getLogger(OpenAPIService.class.getCanonicalName()); - String jarPathString = url.toExternalForm().substring(0, url.toExternalForm().indexOf("!")).replaceAll("file:", "").replaceAll("jar:", ""); - File srcFile = new File(jarPathString); + protected final String resourcePathPrefix = "openapi"; - try (JarFile jarFile = new JarFile(srcFile, false)) - { + protected ObjectMapper mapper; + protected ObjectWriter writer; + protected ObjectMapper yamlMapper; + protected Path resourcePath = null; + protected ClassLoader serviceClassLoader = null; + protected OpenAPI openApi = null; + protected String spec = null; + protected String indexHTML = null; + protected String redocHTML = null; - String appName = config.getString("application.name").replaceAll(" ", "_"); - Path tmpDirParent = Files.createTempDirectory(appName); - Path tmpDir = tmpDirParent.resolve("openapi/"); + protected final String resourcePrefix = "io/sinistral/proteus/openapi"; - if (tmpDir.toFile().exists()) - { - log.debug("Deleting existing OpenAPI directory at " + tmpDir); + @Inject + @Named("openapi.basePath") + protected String basePath; - try - { - FileUtils.deleteDirectory(tmpDir.toFile()); - } catch (IllegalArgumentException e) - { + @Inject + @Named("openapi.specFilename") + protected String specFilename; - log.debug("Tmp directory is not a directory..."); - tmpDir.toFile().delete(); - } - } + @Inject + @Named("openapi") + protected Config openAPIConfig; - Files.createDirectory(tmpDir); + @Inject + @Named("application.name") + protected String applicationName; - this.resourcePath = tmpDir; + @Inject + @Named("openapi.port") + protected Integer port; - jarFile.stream().filter(ze -> ze.getName().endsWith("js") || ze.getName().endsWith("css") || ze.getName().endsWith("map") || ze.getName().endsWith("html")) - .forEach(ze -> - { - try - { + @Inject + @Named("openapi.redocPath") + protected String redocPath; - final InputStream entryInputStream = jarFile.getInputStream(ze); - String filename = ze.getName().substring(resourcePrefix.length() + 1); - Path entryFilePath = tmpDir.resolve(filename); + @Inject + @Named("application.path") + protected String applicationPath; - Files.createDirectories(entryFilePath.getParent()); - Files.copy(entryInputStream, entryFilePath, StandardCopyOption.REPLACE_EXISTING); + @Inject + protected RoutingHandler router; - } catch (Exception e) - { - log.error(e.getMessage() + " for entry " + ze.getName()); - } - }); - } - } - else - { - this.resourcePath = Paths.get(this.getClass().getClassLoader().getResource(this.resourcePrefix).toURI()); + @Inject + @Named("registeredEndpoints") + protected Set registeredEndpoints; - this.serviceClassLoader = this.getClass().getClassLoader(); - } + @Inject + @Named("registeredControllers") + protected Set> registeredControllers; - } catch (Exception e) - { - log.error(e.getMessage(), e); - } - } + @Inject(optional = true) + @Named("jackson.jsonView.queryParameterName") + protected String jsonViewQueryParameterName; - @SuppressWarnings("rawtypes") - protected void generateSpec() throws Exception - { - Set> classes = this.registeredControllers; + @Inject + @Named("registeredHandlerWrappers") + protected Map registeredHandlerWrappers; - OpenAPIExtensions.setExtensions(Collections.singletonList(new ServerParameterExtension())); + public OpenAPIService() + { - OpenAPI openApi = new OpenAPI(); - Info info = mapper.convertValue(openAPIConfig.getValue("info").unwrapped(), Info.class); + mapper = Json.mapper(); - openApi.setInfo(info); + mapper.registerModule(new Jdk8Module()); - Map securitySchemes = mapper.convertValue( openAPIConfig.getValue("securitySchemes").unwrapped(),new TypeReference>(){}); + yamlMapper = Yaml.mapper(); + writer = Yaml.pretty(); + } - if (openApi.getComponents() == null) - { - openApi.setComponents(new Components()); - } + protected void generateHTML() + { - openApi.getComponents().setSecuritySchemes(securitySchemes); + try + { - List servers = mapper.convertValue(openAPIConfig.getValue("servers").unwrapped(), new TypeReference>(){}); + try (InputStream templateInputStream = this.getClass().getClassLoader().getResourceAsStream(resourcePrefix + "/index.html")) + { - openApi.setServers(servers); + byte[] templateBytes = IOUtils.toByteArray(templateInputStream); + String templateString = new String(templateBytes, Charset.defaultCharset()); - SwaggerConfiguration config = new SwaggerConfiguration().resourceClasses(classes.stream().map(Class::getName).collect(Collectors.toSet())).openAPI(openApi); + templateString = templateString.replaceAll("\\{\\{ basePath \\}\\}", basePath); + templateString = templateString.replaceAll("\\{\\{ title \\}\\}", applicationName + " Swagger UI"); + this.indexHTML = templateString; + } - if(jsonViewQueryParameterName != null) { + try (InputStream templateInputStream = getClass().getClassLoader().getResourceAsStream(resourcePrefix + "/redoc.html")) + { + byte[] templateBytes = IOUtils.toByteArray(templateInputStream); - if(config.getUserDefinedOptions() == null) - { - config.setUserDefinedOptions(new HashMap<>()); - } + this.redocHTML = new String(templateBytes, Charset.defaultCharset()); + } - config.getUserDefinedOptions().put("jsonViewQueryParameterName", jsonViewQueryParameterName); - } + URL url = this.getClass().getClassLoader().getResource(resourcePrefix); - Set modelConverterClasses = new HashSet<>(); + if (url.toExternalForm().contains("!")) + { + log.debug("Copying OpenAPI resources..."); - modelConverterClasses.add(ServerModelResolver.class.getName()); + String jarPathString = url.toExternalForm().substring(0, url.toExternalForm().indexOf("!")).replaceAll("file:", "").replaceAll("jar:", ""); + File srcFile = new File(jarPathString); - List additionalConverterClasses = openAPIConfig.getStringList("converterClasses"); + try (JarFile jarFile = new JarFile(srcFile, false)) + { - modelConverterClasses.addAll(additionalConverterClasses); + String appName = config.getString("application.name").replaceAll(" ", "_"); + Path tmpDirParent = Files.createTempDirectory(appName); + Path tmpDir = tmpDirParent.resolve("openapi/"); - config.setModelConverterClassess(modelConverterClasses); + if (tmpDir.toFile().exists()) + { + log.debug("Deleting existing OpenAPI directory at " + tmpDir); - OpenApiContext ctx = new GenericOpenApiContext().openApiConfiguration(config) - .openApiReader(new Reader(config)) - .openApiScanner(new JaxrsApplicationAndAnnotationScanner().openApiConfiguration(config)) - .init(); + try + { + FileUtils.deleteDirectory(tmpDir.toFile()); + } catch (IllegalArgumentException e) + { - openApi = ctx.read(); - this.openApi = openApi; - this.spec = writer.writeValueAsString(openApi); - } + log.debug("Tmp directory is not a directory..."); + tmpDir.toFile().delete(); + } + } - @Override - protected void startUp() throws Exception - { - super.startUp(); + Files.createDirectory(tmpDir); - generateHTML(); + this.resourcePath = tmpDir; - CompletableFuture.runAsync(() -> - { - try - { - generateSpec(); + jarFile.stream().filter(ze -> ze.getName().endsWith("js") || ze.getName().endsWith("css") || ze.getName().endsWith("map") || ze.getName().endsWith("html")) + .forEach(ze -> + { + try + { - log.debug("\nOpenAPI Spec:\n" + writer.writeValueAsString(this.openApi)); + final InputStream entryInputStream = jarFile.getInputStream(ze); + String filename = ze.getName().substring(resourcePrefix.length() + 1); + Path entryFilePath = tmpDir.resolve(filename); - } catch (Exception e) - { - log.error("Error generating OpenAPI spec", e); - } + Files.createDirectories(entryFilePath.getParent()); + Files.copy(entryInputStream, entryFilePath, StandardCopyOption.REPLACE_EXISTING); - },this.executor()); + } catch (Exception e) + { + log.error(e.getMessage() + " for entry " + ze.getName()); + } + }); - router.addAll(this.get()); - } + Runtime.getRuntime().addShutdownHook(new Thread(() -> { - public RoutingHandler get() - { - RoutingHandler router = new RoutingHandler(); + try + { + FileUtils.deleteDirectory(tmpDirParent.toFile()); + } catch (IOException ex) + { + log.error("Failed to delete temp openapi directory",ex); + } + })); + } + } + else + { + this.resourcePath = Paths.get(this.getClass().getClassLoader().getResource(this.resourcePrefix).toURI()); - /* - * YAML path - */ - String pathTemplate = this.applicationPath + "/" + this.specFilename; + this.serviceClassLoader = this.getClass().getClassLoader(); + } - FileResourceManager resourceManager = new FileResourceManager(this.resourcePath.toFile(), 1024); + } catch (Exception e) + { + log.error(e.getMessage(), e); + } + } - router.add( HttpMethod.GET, pathTemplate, (HttpServerExchange exchange) -> - { - exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, io.sinistral.proteus.protocol.MediaType.TEXT_YAML.contentType()); + @SuppressWarnings("rawtypes") + protected void generateSpec() throws Exception + { - exchange.getResponseSender().send(spec); - }); + Set> classes = this.registeredControllers; - this.registeredEndpoints.add(EndpointInfo.builder() - .withConsumes("*/*") - .withPathTemplate(pathTemplate) - .withControllerName(this.getClass().getSimpleName()) - .withMethod(Methods.GET) - .withProduces(io.sinistral.proteus.protocol.MediaType.TEXT_YAML.contentType()) - .build()); - + OpenAPIExtensions.setExtensions(Collections.singletonList(new ServerParameterExtension())); - router.add( HttpMethod.GET,basePath, (HttpServerExchange exchange) -> - { - exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, MediaType.TEXT_HTML); - exchange.getResponseSender().send(indexHTML); - }); + OpenAPI openApi = new OpenAPI(); + Info info = mapper.convertValue(openAPIConfig.getValue("info").unwrapped(), Info.class); + + openApi.setInfo(info); + + Map securitySchemes = mapper.convertValue(openAPIConfig.getValue("securitySchemes").unwrapped(), new TypeReference>() {}); + + if (openApi.getComponents() == null) + { + openApi.setComponents(new Components()); + } + + openApi.getComponents().setSecuritySchemes(securitySchemes); + + List servers = mapper.convertValue(openAPIConfig.getValue("servers").unwrapped(), new TypeReference>() {}); + + openApi.setServers(servers); + + SwaggerConfiguration config = new SwaggerConfiguration().resourceClasses(classes.stream().map(Class::getName).collect(Collectors.toSet())).openAPI(openApi); + + if (jsonViewQueryParameterName != null) + { + + if (config.getUserDefinedOptions() == null) + { + config.setUserDefinedOptions(new HashMap<>()); + } + + config.getUserDefinedOptions().put("jsonViewQueryParameterName", jsonViewQueryParameterName); + } + + Set modelConverterClasses = new HashSet<>(); + + modelConverterClasses.add(ServerModelResolver.class.getName()); + + List additionalConverterClasses = openAPIConfig.getStringList("converterClasses"); + + modelConverterClasses.addAll(additionalConverterClasses); + + config.setModelConverterClassess(modelConverterClasses); + + OpenApiContext ctx = new GenericOpenApiContext().openApiConfiguration(config) + .openApiReader(new Reader(config)) + .openApiScanner(new JaxrsApplicationAndAnnotationScanner().openApiConfiguration(config)) + .init(); + + openApi = ctx.read(); + this.openApi = openApi; + this.spec = writer.writeValueAsString(openApi); + + } + + @Override + protected void startUp() throws Exception + { + + super.startUp(); + + generateHTML(); + + CompletableFuture.runAsync(() -> + { + try + { + generateSpec(); + + log.debug("\nOpenAPI Spec:\n" + writer.writeValueAsString(this.openApi)); + + } catch (Exception e) + { + log.error("Error generating OpenAPI spec", e); + } + + }, this.executor()); + + router.addAll(this.get()); + } + + public RoutingHandler get() + { + + RoutingHandler router = new RoutingHandler(); + + /* + * YAML path + */ + String pathTemplate = this.applicationPath + "/" + this.specFilename; + + FileResourceManager resourceManager = new FileResourceManager(this.resourcePath.toFile(), 1024); + + router.add(HttpMethod.GET, pathTemplate, (HttpServerExchange exchange) -> + { + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, io.sinistral.proteus.protocol.MediaType.TEXT_YAML.contentType()); + + exchange.getResponseSender().send(spec); + }); + + this.registeredEndpoints.add(EndpointInfo.builder() + .withConsumes("*/*") + .withPathTemplate(pathTemplate) + .withControllerName(this.getClass().getSimpleName()) + .withMethod(Methods.GET) + .withProduces(io.sinistral.proteus.protocol.MediaType.TEXT_YAML.contentType()) + .build()); + + router.add(HttpMethod.GET, basePath, (HttpServerExchange exchange) -> + { + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, MediaType.TEXT_HTML); + exchange.getResponseSender().send(indexHTML); + }); + + this.registeredEndpoints.add(EndpointInfo.builder() + .withConsumes(MediaType.WILDCARD) + .withProduces(MediaType.TEXT_HTML) + .withPathTemplate(pathTemplate) + .withControllerName(this.getClass().getSimpleName()) + .withMethod(Methods.GET) + .build()); + + final String specPath = pathTemplate; + + router.add(HttpMethod.GET, this.basePath + "/" + this.redocPath, (HttpServerExchange exchange) -> + { + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, MediaType.TEXT_HTML); + + final String fullPath = String.format("%s://%s%s", exchange.getRequestScheme(), exchange.getHostAndPort(), specPath); + + final String html = redocHTML.replaceAll("\\{\\{ specPath \\}\\}", fullPath); + + exchange.getResponseSender().send(html); + }); + + this.registeredEndpoints.add(EndpointInfo.builder() + .withConsumes(MediaType.WILDCARD) + .withProduces(MediaType.TEXT_HTML) + .withPathTemplate(this.basePath + "/" + this.redocPath) + .withControllerName(this.getClass().getSimpleName()) + .withMethod(Methods.GET) + .build()); + + try + { + + pathTemplate = this.basePath + "/*"; + + router.add(HttpMethod.GET, + pathTemplate, + new ResourceHandler(resourceManager) { + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception + { + + String canonicalPath = CanonicalPathUtils.canonicalize((exchange.getRelativePath())); + + canonicalPath = canonicalPath.split(basePath)[1]; + + exchange.setRelativePath(canonicalPath); + + if (serviceClassLoader == null) + { + super.handleRequest(exchange); + } + else + { + canonicalPath = resourcePrefix + canonicalPath; + + try (final InputStream resourceInputStream = serviceClassLoader.getResourceAsStream(canonicalPath)) + { + + if (resourceInputStream == null) + { + ResponseCodeHandler.HANDLE_404.handleRequest(exchange); + + return; + } + + byte[] resourceBytes = IOUtils.toByteArray(resourceInputStream); + + io.sinistral.proteus.protocol.MediaType mediaType = io.sinistral.proteus.protocol.MediaType.getByFileName(canonicalPath); + + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, mediaType.toString()); + exchange.getResponseSender().send(ByteBuffer.wrap(resourceBytes)); + } + } + } + }); + + this.registeredEndpoints.add(EndpointInfo.builder() + .withConsumes(MediaType.WILDCARD) + .withProduces(MediaType.WILDCARD) + .withPathTemplate(pathTemplate) + .withControllerName(this.getClass().getSimpleName()) + .withMethod(Methods.GET) + .build()); + + } catch (Exception e) + { + log.error(e.getMessage(), e); + } + + return router; + } - this.registeredEndpoints.add(EndpointInfo.builder() - .withConsumes(MediaType.WILDCARD) - .withProduces(MediaType.TEXT_HTML) - .withPathTemplate(pathTemplate) - .withControllerName(this.getClass().getSimpleName()) - .withMethod(Methods.GET) - .build()); - - final String specPath = pathTemplate; - - router.add( HttpMethod.GET,this.basePath + "/" + this.redocPath, (HttpServerExchange exchange) -> - { - exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, MediaType.TEXT_HTML); - - final String fullPath = String.format("%s://%s%s",exchange.getRequestScheme(), exchange.getHostAndPort(), specPath); - - final String html = redocHTML.replaceAll("\\{\\{ specPath \\}\\}", fullPath); - - exchange.getResponseSender().send(html); - }); - - this.registeredEndpoints.add(EndpointInfo.builder() - .withConsumes(MediaType.WILDCARD) - .withProduces(MediaType.TEXT_HTML) - .withPathTemplate(this.basePath + "/" + this.redocPath) - .withControllerName(this.getClass().getSimpleName()) - .withMethod(Methods.GET) - .build()); - - try - { - - pathTemplate = this.basePath + "/*"; - - router.add( HttpMethod.GET, - pathTemplate, - new ResourceHandler(resourceManager) - { - @Override - public void handleRequest(HttpServerExchange exchange) throws Exception - { - String canonicalPath = CanonicalPathUtils.canonicalize((exchange.getRelativePath())); - - canonicalPath = canonicalPath.split(basePath)[1]; - - exchange.setRelativePath(canonicalPath); - - if (serviceClassLoader == null) - { - super.handleRequest(exchange); - } - else - { - canonicalPath = resourcePrefix + canonicalPath; - - try (final InputStream resourceInputStream = serviceClassLoader.getResourceAsStream(canonicalPath)) - { - - if (resourceInputStream == null) - { - ResponseCodeHandler.HANDLE_404.handleRequest(exchange); - - return; - } - - byte[] resourceBytes = IOUtils.toByteArray(resourceInputStream); - - io.sinistral.proteus.protocol.MediaType mediaType = io.sinistral.proteus.protocol.MediaType.getByFileName(canonicalPath); - - exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, mediaType.toString()); - exchange.getResponseSender().send(ByteBuffer.wrap(resourceBytes)); - } - } - } - }); - - this.registeredEndpoints.add(EndpointInfo.builder() - .withConsumes(MediaType.WILDCARD) - .withProduces(MediaType.WILDCARD) - .withPathTemplate(pathTemplate) - .withControllerName(this.getClass().getSimpleName()) - .withMethod(Methods.GET) - .build()); - - } catch (Exception e) - { - log.error(e.getMessage(), e); - } - - return router; - } }