From cf4cd34937f7913ff0fbf4cc1e453081fa648311 Mon Sep 17 00:00:00 2001 From: Phillip Kruger Date: Mon, 23 Sep 2024 21:38:25 +1000 Subject: [PATCH] Add HTTP Access Log to Dev UI Signed-off-by: Phillip Kruger --- .../deployment/BuildTimeContentProcessor.java | 16 ++++- .../deployment/DeploymentMethodBuildItem.java | 15 +++- .../devui/deployment/DevUIProcessor.java | 22 +++--- .../deployment/VertxDevUILogBuildItem.java | 22 ++++++ .../http/deployment/VertxHttpProcessor.java | 26 ++++++- .../resources/dev-ui/controller/jsonrpc.js | 5 +- .../dev-ui/controller/log-controller.js | 4 +- .../resources/dev-ui/qwc/qwc-footer-log.js | 3 +- .../main/resources/dev-ui/qwc/qwc-footer.js | 4 +- .../devui/spi/buildtime/BuildTimeAction.java | 19 +++++ .../buildtime/BuildTimeActionBuildItem.java | 11 +++ .../spi/buildtime/FooterLogBuildItem.java | 19 +++++ .../quarkus/devui/runtime/DevUIRecorder.java | 7 +- .../devui/runtime/comms/JsonRpcRouter.java | 29 +++++++- .../vertx/http/runtime/VertxHttpRecorder.java | 69 ++++++++++++++----- 15 files changed, 232 insertions(+), 39 deletions(-) create mode 100644 extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxDevUILogBuildItem.java diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/BuildTimeContentProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/BuildTimeContentProcessor.java index 2864840626569..bdd5a816114b3 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/BuildTimeContentProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/BuildTimeContentProcessor.java @@ -72,6 +72,7 @@ import io.quarkus.devui.spi.page.Page; import io.quarkus.devui.spi.page.PageBuilder; import io.quarkus.maven.dependency.ResolvedDependency; +import io.quarkus.runtime.RuntimeValue; import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.vertx.core.json.jackson.DatabindCodec; @@ -209,21 +210,30 @@ DeploymentMethodBuildItem mapDeploymentMethods( List methodNames = new ArrayList<>(); List subscriptionNames = new ArrayList<>(); + Map recordedValues = new HashMap<>(); for (BuildTimeActionBuildItem actions : buildTimeActions) { String extensionPathName = actions.getExtensionPathName(curateOutcomeBuildItem); for (BuildTimeAction bta : actions.getActions()) { String fullName = extensionPathName + "." + bta.getMethodName(); - DevConsoleManager.register(fullName, bta.getAction()); + if (bta.hasRuntimeValue()) { + recordedValues.put(fullName, bta.getRuntimeValue()); + } else { + DevConsoleManager.register(fullName, bta.getAction()); + } methodNames.add(fullName); } for (BuildTimeAction bts : actions.getSubscriptions()) { String fullName = extensionPathName + "." + bts.getMethodName(); - DevConsoleManager.register(fullName, bts.getAction()); + if (bts.hasRuntimeValue()) { + recordedValues.put(fullName, bts.getRuntimeValue()); + } else { + DevConsoleManager.register(fullName, bts.getAction()); + } subscriptionNames.add(fullName); } } - return new DeploymentMethodBuildItem(methodNames, subscriptionNames); + return new DeploymentMethodBuildItem(methodNames, subscriptionNames, recordedValues); } private Map getBuildTimeDataForPage(AbstractPageBuildItem pageBuildItem) { diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/DeploymentMethodBuildItem.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/DeploymentMethodBuildItem.java index e1822398b18ce..b394b54f16929 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/DeploymentMethodBuildItem.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/DeploymentMethodBuildItem.java @@ -1,8 +1,10 @@ package io.quarkus.devui.deployment; import java.util.List; +import java.util.Map; import io.quarkus.builder.item.SimpleBuildItem; +import io.quarkus.runtime.RuntimeValue; /** * Hold add discovered build time methods that can be executed via json-rpc @@ -11,10 +13,13 @@ public final class DeploymentMethodBuildItem extends SimpleBuildItem { private final List methods; private final List subscriptions; + private final Map recordedValues; - public DeploymentMethodBuildItem(List methods, List subscriptions) { + public DeploymentMethodBuildItem(List methods, List subscriptions, + Map recordedValues) { this.methods = methods; this.subscriptions = subscriptions; + this.recordedValues = recordedValues; } public List getMethods() { @@ -32,4 +37,12 @@ public List getSubscriptions() { public boolean hasSubscriptions() { return this.subscriptions != null && !this.subscriptions.isEmpty(); } + + public Map getRecordedValues() { + return this.recordedValues; + } + + public boolean hasRecordedValues() { + return this.recordedValues != null && !this.recordedValues.isEmpty(); + } } diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/DevUIProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/DevUIProcessor.java index b0e952b333f06..6268b70b6f620 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/DevUIProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/DevUIProcessor.java @@ -404,7 +404,7 @@ void findAllJsonRPCMethods(BuildProducer jsonRPC } @BuildStep(onlyIf = IsDevelopment.class) - @Record(ExecutionTime.STATIC_INIT) + @Record(ExecutionTime.RUNTIME_INIT) void createJsonRpcRouter(DevUIRecorder recorder, BeanContainerBuildItem beanContainer, JsonRPCRuntimeMethodsBuildItem jsonRPCMethodsBuildItem, @@ -417,7 +417,7 @@ void createJsonRpcRouter(DevUIRecorder recorder, DevConsoleManager.setGlobal(DevUIRecorder.DEV_MANAGER_GLOBALS_JSON_MAPPER_FACTORY, JsonMapper.Factory.deploymentLinker().createLinkData(new DevUIDatabindCodec.Factory())); recorder.createJsonRpcRouter(beanContainer.getValue(), extensionMethodsMap, deploymentMethodBuildItem.getMethods(), - deploymentMethodBuildItem.getSubscriptions()); + deploymentMethodBuildItem.getSubscriptions(), deploymentMethodBuildItem.getRecordedValues()); } } @@ -437,13 +437,17 @@ void processFooterLogs(BuildProducer buildTimeActionPr String name = footerLogBuildItem.getName().replaceAll(" ", ""); BuildTimeActionBuildItem devServiceLogActions = new BuildTimeActionBuildItem(FOOTER_LOG_NAMESPACE); - devServiceLogActions.addSubscription(name + "Log", ignored -> { - try { - return footerLogBuildItem.getPublisher(); - } catch (Exception e) { - throw new RuntimeException(e); - } - }); + if (footerLogBuildItem.hasRuntimePublisher()) { + devServiceLogActions.addSubscription(name + "Log", footerLogBuildItem.getRuntimePublisher()); + } else { + devServiceLogActions.addSubscription(name + "Log", ignored -> { + try { + return footerLogBuildItem.getPublisher(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } devServiceLogs.add(devServiceLogActions); // Create the Footer in the Dev UI diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxDevUILogBuildItem.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxDevUILogBuildItem.java new file mode 100644 index 0000000000000..f44050c97426a --- /dev/null +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxDevUILogBuildItem.java @@ -0,0 +1,22 @@ +package io.quarkus.vertx.http.deployment; + +import java.util.concurrent.SubmissionPublisher; + +import io.quarkus.builder.item.SimpleBuildItem; +import io.quarkus.runtime.RuntimeValue; + +/** + * Used to create the publisher for the vertx access log in dev ui + */ +public final class VertxDevUILogBuildItem extends SimpleBuildItem { + + private final RuntimeValue> publisher; + + public VertxDevUILogBuildItem(RuntimeValue> publisher) { + this.publisher = publisher; + } + + public RuntimeValue> getPublisher() { + return this.publisher; + } +} diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java index 706f9d1268b09..595155a575aa5 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java @@ -11,6 +11,7 @@ import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.concurrent.SubmissionPublisher; import java.util.logging.Level; import java.util.stream.Collectors; @@ -28,6 +29,7 @@ import io.quarkus.builder.BuildException; import io.quarkus.deployment.Capabilities; import io.quarkus.deployment.Capability; +import io.quarkus.deployment.IsDevelopment; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.ExecutionTime; @@ -50,6 +52,7 @@ import io.quarkus.deployment.pkg.steps.GraalVM; import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild; import io.quarkus.deployment.pkg.steps.NoopNativeImageBuildRunner; +import io.quarkus.devui.spi.buildtime.FooterLogBuildItem; import io.quarkus.kubernetes.spi.KubernetesPortBuildItem; import io.quarkus.netty.runtime.virtual.VirtualServerChannel; import io.quarkus.runtime.ErrorPageAction; @@ -327,6 +330,17 @@ BodyHandlerBuildItem bodyHandler(VertxHttpRecorder recorder) { return new BodyHandlerBuildItem(recorder.createBodyHandler()); } + @BuildStep(onlyIf = IsDevelopment.class) + @Record(ExecutionTime.RUNTIME_INIT) + void createDevUILog(BuildProducer footerLogProducer, + VertxHttpRecorder recorder, + BuildProducer vertxDevUILogBuildItem) { + + RuntimeValue> publisher = recorder.createAccessLogPublisher(); + footerLogProducer.produce(new FooterLogBuildItem("HTTP", publisher)); + vertxDevUILogBuildItem.produce(new VertxDevUILogBuildItem(publisher)); + } + @BuildStep @Record(ExecutionTime.RUNTIME_INIT) ServiceStartBuildItem finalizeRouter(Optional decorateBuildItem, @@ -348,7 +362,8 @@ ServiceStartBuildItem finalizeRouter(Optional decorate LiveReloadConfig lrc, CoreVertxBuildItem core, // Injected to be sure that Vert.x has been produced before calling this method. ExecutorBuildItem executorBuildItem, - TlsRegistryBuildItem tlsRegistryBuildItem) // Injected to be sure that the TLS registry has been produced before calling this method. + TlsRegistryBuildItem tlsRegistryBuildItem, // Injected to be sure that the TLS registry has been produced before calling this method. + Optional vertxDevUILogBuildItem) throws BuildException { Optional defaultRoute; @@ -409,6 +424,12 @@ ServiceStartBuildItem finalizeRouter(Optional decorate knowClasses = decorateBuildItem.get().getKnowClasses(); } + Optional>> publisher = Optional.empty(); + + if (vertxDevUILogBuildItem.isPresent()) { + publisher = Optional.of(vertxDevUILogBuildItem.get().getPublisher()); + } + recorder.finalizeRouter(beanContainer.getValue(), defaultRoute.map(DefaultRouteBuildItem::getRoute).orElse(null), listOfFilters, listOfManagementInterfaceFilters, @@ -423,7 +444,8 @@ ServiceStartBuildItem finalizeRouter(Optional decorate logBuildTimeConfig, srcMainJava, knowClasses, - combinedActions); + combinedActions, + publisher); return new ServiceStartBuildItem("vertx-http"); } diff --git a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/controller/jsonrpc.js b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/controller/jsonrpc.js index 757518fe3d0a3..f0938ba1fb7e8 100644 --- a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/controller/jsonrpc.js +++ b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/controller/jsonrpc.js @@ -156,6 +156,9 @@ export class JsonRpc { var jsonrpcpayload = JSON.stringify(message); if (jsonRPCSubscriptions.includes(method)) { + if(JsonRpc.observerQueue.has(uid)){ + JsonRpc.observerQueue.get(uid).observer.cancel(); + } // Observer var observer = new Observer(uid); JsonRpc.observerQueue.set(uid, { @@ -163,7 +166,7 @@ export class JsonRpc { log: this._logTraffic }); JsonRpc.sendJsonRPCMessage(jsonrpcpayload, this._logTraffic); - return observer; + return observer; } else if(jsonRPCMethods.includes(method)){ // Promise var _resolve, _reject; diff --git a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/controller/log-controller.js b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/controller/log-controller.js index 2d2366d46f230..a531c7a826e5e 100644 --- a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/controller/log-controller.js +++ b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/controller/log-controller.js @@ -4,14 +4,14 @@ export class LogController { static _controllers = new Map(); static listener; - + host; tab; items = []; constructor(host) { (this.host = host).addController(this); - this.tab = host.tagName.toLowerCase(); + this.tab = host.title; } hostConnected() { diff --git a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-footer-log.js b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-footer-log.js index b51d63d27546e..287bac774d23c 100644 --- a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-footer-log.js +++ b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-footer-log.js @@ -41,7 +41,7 @@ export class QwcFooterLog extends QwcAbstractLogElement { constructor() { super(); - + this.logControl .addToggle("On/off switch", true, (e) => { this._toggleOnOffClicked(e); @@ -130,6 +130,7 @@ export class QwcFooterLog extends QwcAbstractLogElement { hotReload(){ this._clearLog(); if(this._observer != null){ + this._toggleOnOff(false); this._toggleOnOff(true); } } diff --git a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-footer.js b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-footer.js index 8c5ee9cc8c4af..b06b6dcdfc230 100644 --- a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-footer.js +++ b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-footer.js @@ -309,9 +309,9 @@ export class QwcFooter extends observeState(LitElement) { this._selectedTab = index; var selectedComponentName = devuiState.footer[this._selectedTab]; if(selectedComponentName){ - this._controlButtons = LogController.getItemsForTab(devuiState.footer[this._selectedTab].componentName); + this._controlButtons = LogController.getItemsForTab(devuiState.footer[this._selectedTab].title); }else{ - this._controlButtons = LogController.getItemsForTab(devuiState.footer[0].componentName); + this._controlButtons = LogController.getItemsForTab(devuiState.footer[0].title); this._selectedTab = 0; } this.storageControl.set('selected-tab', this._selectedTab); diff --git a/extensions/vertx-http/dev-ui-spi/src/main/java/io/quarkus/devui/spi/buildtime/BuildTimeAction.java b/extensions/vertx-http/dev-ui-spi/src/main/java/io/quarkus/devui/spi/buildtime/BuildTimeAction.java index 5f4e416ba90a6..cd87546cef3fa 100644 --- a/extensions/vertx-http/dev-ui-spi/src/main/java/io/quarkus/devui/spi/buildtime/BuildTimeAction.java +++ b/extensions/vertx-http/dev-ui-spi/src/main/java/io/quarkus/devui/spi/buildtime/BuildTimeAction.java @@ -3,6 +3,8 @@ import java.util.Map; import java.util.function.Function; +import io.quarkus.runtime.RuntimeValue; + /** * Define a action that can be executed against the deployment classpath in runtime * This means a call will still be make with Json-RPC to the backend, but fall through to this action @@ -11,13 +13,22 @@ public class BuildTimeAction { private final String methodName; private final Function, ?> action; + private final RuntimeValue runtimeValue; protected BuildTimeAction(String methodName, Function, T> action) { this.methodName = methodName; this.action = action; + this.runtimeValue = null; + } + protected BuildTimeAction(String methodName, + RuntimeValue runtimeValue) { + + this.methodName = methodName; + this.action = null; + this.runtimeValue = runtimeValue; } public String getMethodName() { @@ -27,4 +38,12 @@ public String getMethodName() { public Function, ?> getAction() { return action; } + + public RuntimeValue getRuntimeValue() { + return runtimeValue; + } + + public boolean hasRuntimeValue() { + return this.runtimeValue != null; + } } diff --git a/extensions/vertx-http/dev-ui-spi/src/main/java/io/quarkus/devui/spi/buildtime/BuildTimeActionBuildItem.java b/extensions/vertx-http/dev-ui-spi/src/main/java/io/quarkus/devui/spi/buildtime/BuildTimeActionBuildItem.java index cc01649e2cf44..6e70ba6d06709 100644 --- a/extensions/vertx-http/dev-ui-spi/src/main/java/io/quarkus/devui/spi/buildtime/BuildTimeActionBuildItem.java +++ b/extensions/vertx-http/dev-ui-spi/src/main/java/io/quarkus/devui/spi/buildtime/BuildTimeActionBuildItem.java @@ -6,6 +6,7 @@ import java.util.function.Function; import io.quarkus.devui.spi.AbstractDevUIBuildItem; +import io.quarkus.runtime.RuntimeValue; /** * Holds any Build time actions for Dev UI the extension has @@ -32,6 +33,11 @@ public void addAction(String methodName, this.addAction(new BuildTimeAction(methodName, action)); } + public void addAction(String methodName, + RuntimeValue runtimeValue) { + this.addAction(new BuildTimeAction(methodName, runtimeValue)); + } + public List getActions() { return actions; } @@ -45,6 +51,11 @@ public void addSubscription(String methodName, this.addSubscription(new BuildTimeAction(methodName, action)); } + public void addSubscription(String methodName, + RuntimeValue runtimeValue) { + this.addSubscription(new BuildTimeAction(methodName, runtimeValue)); + } + public List getSubscriptions() { return subscriptions; } diff --git a/extensions/vertx-http/dev-ui-spi/src/main/java/io/quarkus/devui/spi/buildtime/FooterLogBuildItem.java b/extensions/vertx-http/dev-ui-spi/src/main/java/io/quarkus/devui/spi/buildtime/FooterLogBuildItem.java index 231c175a4425f..93aaecf760439 100644 --- a/extensions/vertx-http/dev-ui-spi/src/main/java/io/quarkus/devui/spi/buildtime/FooterLogBuildItem.java +++ b/extensions/vertx-http/dev-ui-spi/src/main/java/io/quarkus/devui/spi/buildtime/FooterLogBuildItem.java @@ -1,9 +1,11 @@ package io.quarkus.devui.spi.buildtime; import java.util.concurrent.Flow; +import java.util.concurrent.SubmissionPublisher; import java.util.function.Supplier; import io.quarkus.devui.spi.AbstractDevUIBuildItem; +import io.quarkus.runtime.RuntimeValue; /** * Add a log to the footer of dev ui @@ -12,11 +14,24 @@ public final class FooterLogBuildItem extends AbstractDevUIBuildItem { private final String name; private final Supplier> publisherSupplier; + private final RuntimeValue> runtimePublisher; public FooterLogBuildItem(String name, Supplier> publisherSupplier) { super(DEV_UI); this.name = name; this.publisherSupplier = publisherSupplier; + this.runtimePublisher = null; + } + + public FooterLogBuildItem(String name, RuntimeValue> runtimePublisher) { + super(DEV_UI); + this.name = name; + this.runtimePublisher = runtimePublisher; + this.publisherSupplier = null; + } + + public boolean hasRuntimePublisher() { + return runtimePublisher != null; } public String getName() { @@ -26,4 +41,8 @@ public String getName() { public Flow.Publisher getPublisher() { return publisherSupplier.get(); } + + public RuntimeValue> getRuntimePublisher() { + return runtimePublisher; + } } diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/DevUIRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/DevUIRecorder.java index 98780949af807..88f58b6449d13 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/DevUIRecorder.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/DevUIRecorder.java @@ -25,6 +25,7 @@ import io.quarkus.devui.runtime.jsonrpc.JsonRpcMethodName; import io.quarkus.devui.runtime.jsonrpc.json.JsonMapper; import io.quarkus.devui.runtime.jsonrpc.json.JsonTypeAdapter; +import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.ShutdownContext; import io.quarkus.runtime.annotations.Recorder; import io.quarkus.vertx.http.runtime.devmode.FileSystemStaticHandler; @@ -47,10 +48,14 @@ public void shutdownTask(ShutdownContext shutdownContext, String devUIBasePath) public void createJsonRpcRouter(BeanContainer beanContainer, Map> extensionMethodsMap, List deploymentMethods, - List deploymentSubscriptions) { + List deploymentSubscriptions, + Map recordedValues) { JsonRpcRouter jsonRpcRouter = beanContainer.beanInstance(JsonRpcRouter.class); jsonRpcRouter.populateJsonRPCRuntimeMethods(extensionMethodsMap); jsonRpcRouter.setJsonRPCDeploymentActions(deploymentMethods, deploymentSubscriptions); + if (recordedValues != null && !recordedValues.isEmpty()) { + jsonRpcRouter.setRecordedValues(recordedValues); + } jsonRpcRouter.initializeCodec(createJsonMapper()); } diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/comms/JsonRpcRouter.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/comms/JsonRpcRouter.java index 0767be67c9b99..2d6f4d326c3c5 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/comms/JsonRpcRouter.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/comms/JsonRpcRouter.java @@ -24,6 +24,7 @@ import io.quarkus.devui.runtime.jsonrpc.JsonRpcMethodName; import io.quarkus.devui.runtime.jsonrpc.JsonRpcRequest; import io.quarkus.devui.runtime.jsonrpc.json.JsonMapper; +import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.StartupEvent; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; @@ -46,6 +47,8 @@ public class JsonRpcRouter { private final List jsonRpcMethodToDeploymentClassPathJava = new ArrayList<>(); // Map json-rpc subscriptions to java in deployment classpath private final List jsonRpcSubscriptionToDeploymentClassPathJava = new ArrayList<>(); + // Map json-rpc methods responses that is recorded + private final Map recordedValues = new HashMap<>(); private static final List SESSIONS = Collections.synchronizedList(new ArrayList<>()); private JsonRpcCodec codec; @@ -94,6 +97,11 @@ public void setJsonRPCDeploymentActions(List methods, List subsc this.jsonRpcSubscriptionToDeploymentClassPathJava.addAll(subscriptions); } + public void setRecordedValues(Map recordedValues) { + this.recordedValues.clear(); + this.recordedValues.putAll(recordedValues); + } + public void initializeCodec(JsonMapper jsonMapper) { this.codec = new JsonRpcCodec(jsonMapper); } @@ -169,6 +177,13 @@ private void routeToRuntime(JsonRpcRequest jsonRpcRequest, ServerWebSocket s) { private void routeToRuntimeSubscription(JsonRpcRequest jsonRpcRequest, ServerWebSocket s, String jsonRpcMethodName, ReflectionInfo reflectionInfo, Object target) { + + if (this.subscriptions.containsKey(jsonRpcRequest.getId())) { + // Cancel and resubscribe + Cancellable cancellable = this.subscriptions.remove(jsonRpcRequest.getId()); + cancellable.cancel(); + } + Multi multi; try { if (jsonRpcRequest.hasParams()) { @@ -244,7 +259,19 @@ private void routeToRuntimeMethod(JsonRpcRequest jsonRpcRequest, ServerWebSocket private void routeToDeployment(JsonRpcRequest jsonRpcRequest, ServerWebSocket s) { String jsonRpcMethodName = jsonRpcRequest.getMethod(); - Object returnedObject = DevConsoleManager.invoke(jsonRpcMethodName, getArgsAsMap(jsonRpcRequest)); + + if (this.subscriptions.containsKey(jsonRpcRequest.getId())) { + // Cancel and resubscribe + Cancellable cancellable = this.subscriptions.remove(jsonRpcRequest.getId()); + cancellable.cancel(); + } + + Object returnedObject = null; + if (this.recordedValues.containsKey(jsonRpcMethodName)) { + returnedObject = this.recordedValues.get(jsonRpcMethodName).getValue(); + } else { + returnedObject = DevConsoleManager.invoke(jsonRpcMethodName, getArgsAsMap(jsonRpcRequest)); + } if (returnedObject != null) { // Support for Mutiny is diffcult because we are between the runtime and deployment classpath. // Supporting something like CompletableFuture and Flow.Publisher that is in the JDK works fine diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java index 4de23de77b0d1..0ed86ab5f730b 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java @@ -23,6 +23,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.SubmissionPublisher; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; @@ -318,6 +319,10 @@ public RuntimeValue createMutinyRouter(final Run return new RuntimeValue<>(new io.vertx.mutiny.ext.web.Router(router.getValue())); } + public RuntimeValue> createAccessLogPublisher() { + return new RuntimeValue<>(new SubmissionPublisher<>()); + } + public void startServer(Supplier vertx, ShutdownContext shutdown, LaunchMode launchMode, boolean startVirtual, boolean startSocket, Supplier ioThreads, List websocketSubProtocols, @@ -388,7 +393,8 @@ public void finalizeRouter(BeanContainer container, Consumer defaultRoute LogBuildTimeConfig logBuildTimeConfig, String srcMainJava, List knowClasses, - List actions) { + List actions, + Optional>> publisher) { HttpConfiguration httpConfiguration = this.httpConfiguration.getValue(); // install the default route at the end Router httpRouteRouter = httpRouterRuntimeValue.getValue(); @@ -483,22 +489,25 @@ public void handle(RoutingContext routingContext) { } else { receiver = new JBossLoggingAccessLogReceiver(accessLog.category); } - AccessLogHandler handler = new AccessLogHandler(receiver, accessLog.pattern, accessLog.consolidateReroutedRequests, - getClass().getClassLoader(), - accessLog.excludePattern); - if (rootPath.equals("/") || nonRootPath.equals("/")) { - mainRouterRuntimeValue.orElse(httpRouterRuntimeValue).getValue().route() - .order(RouteConstants.ROUTE_ORDER_ACCESS_LOG_HANDLER) - .handler(handler); - } else if (nonRootPath.startsWith(rootPath)) { - httpRouteRouter.route().order(RouteConstants.ROUTE_ORDER_ACCESS_LOG_HANDLER).handler(handler); - } else if (rootPath.startsWith(nonRootPath)) { - frameworkRouter.getValue().route().order(RouteConstants.ROUTE_ORDER_ACCESS_LOG_HANDLER).handler(handler); - } else { - httpRouteRouter.route().order(RouteConstants.ROUTE_ORDER_ACCESS_LOG_HANDLER).handler(handler); - frameworkRouter.getValue().route().order(RouteConstants.ROUTE_ORDER_ACCESS_LOG_HANDLER).handler(handler); - } + setupAccessLogHandler(mainRouterRuntimeValue, httpRouterRuntimeValue, frameworkRouter, receiver, rootPath, + nonRootPath, accessLog.pattern, accessLog.consolidateReroutedRequests, accessLog.excludePattern); + quarkusWrapperNeeded = true; + } + + // Add an access log for Dev UI + if (publisher.isPresent()) { + SubmissionPublisher logPublisher = publisher.get().getValue(); + AccessLogReceiver receiver = new AccessLogReceiver() { + @Override + public void logMessage(String message) { + logPublisher.submit(message); + } + }; + + setupAccessLogHandler(mainRouterRuntimeValue, httpRouterRuntimeValue, frameworkRouter, receiver, rootPath, + nonRootPath, accessLog.pattern, accessLog.consolidateReroutedRequests, + accessLog.excludePattern.or(() -> Optional.of("^" + nonRootPath + ".*"))); quarkusWrapperNeeded = true; } @@ -586,6 +595,34 @@ public void handle(HttpServerRequest event) { } } + private void setupAccessLogHandler(Optional> mainRouterRuntimeValue, + RuntimeValue httpRouterRuntimeValue, + RuntimeValue frameworkRouter, + AccessLogReceiver receiver, + String rootPath, + String nonRootPath, + String pattern, + boolean consolidateReroutedRequests, + Optional excludePattern) { + + Router httpRouteRouter = httpRouterRuntimeValue.getValue(); + AccessLogHandler handler = new AccessLogHandler(receiver, pattern, consolidateReroutedRequests, + getClass().getClassLoader(), + excludePattern); + if (rootPath.equals("/") || nonRootPath.equals("/")) { + mainRouterRuntimeValue.orElse(httpRouterRuntimeValue).getValue().route() + .order(RouteConstants.ROUTE_ORDER_ACCESS_LOG_HANDLER) + .handler(handler); + } else if (nonRootPath.startsWith(rootPath)) { + httpRouteRouter.route().order(RouteConstants.ROUTE_ORDER_ACCESS_LOG_HANDLER).handler(handler); + } else if (rootPath.startsWith(nonRootPath)) { + frameworkRouter.getValue().route().order(RouteConstants.ROUTE_ORDER_ACCESS_LOG_HANDLER).handler(handler); + } else { + httpRouteRouter.route().order(RouteConstants.ROUTE_ORDER_ACCESS_LOG_HANDLER).handler(handler); + frameworkRouter.getValue().route().order(RouteConstants.ROUTE_ORDER_ACCESS_LOG_HANDLER).handler(handler); + } + } + private boolean decorateStacktrace(LaunchMode launchMode, LogBuildTimeConfig logBuildTimeConfig) { return logBuildTimeConfig.decorateStacktraces && launchMode.equals(LaunchMode.DEVELOPMENT); }