This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch 3975-telemetry
in repository https://gitbox.apache.org/repos/asf/causeway.git


The following commit(s) were added to refs/heads/3975-telemetry by this push:
     new 0e8c46d2e0c CAUSEWAY-3975: interaction creation fixes
0e8c46d2e0c is described below

commit 0e8c46d2e0cfc11acf975003463c14d5ef2da096
Author: andi-huber <[email protected]>
AuthorDate: Sat Mar 21 08:17:09 2026 +0100

    CAUSEWAY-3975: interaction creation fixes
---
 .../wicket/viewer/integration/RequestCycle2.java   |  57 +++++++++++
 .../viewer/integration/RootRequestMapper.java      |  66 +------------
 .../viewer/integration/SessionAuthenticator.java   |   3 +-
 .../viewer/integration/TelemetryStartHandler.java  |  51 ++++++++++
 .../viewer/integration/TelemetryStopHandler.java   |  47 +++++++++
 .../integration/WebRequestCycleForCauseway.java    | 107 +++++----------------
 .../wicketapp/CausewayWicketApplication.java       |  31 +++---
 7 files changed, 201 insertions(+), 161 deletions(-)

diff --git 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/RequestCycle2.java
 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/RequestCycle2.java
new file mode 100644
index 00000000000..7efdc44585c
--- /dev/null
+++ 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/RequestCycle2.java
@@ -0,0 +1,57 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.causeway.viewer.wicket.viewer.integration;
+
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.cycle.RequestCycleContext;
+
+import io.micrometer.observation.Observation;
+import io.micrometer.observation.Observation.Scope;
+
+public class RequestCycle2 extends RequestCycle {
+
+    final long startTimeNanos;
+    Observation observation;
+    Scope scope;
+
+    public RequestCycle2(final RequestCycleContext context) {
+        super(context);
+        this.startTimeNanos = System.nanoTime();
+    }
+
+    long millisSinceStart() {
+        return (System.nanoTime() - startTimeNanos)/1000_000;
+    }
+
+    void observationStartAndOpenScope() {
+        if(observation==null) return;
+        observation.start();
+        this.scope = observation.openScope();
+    }
+
+    void observationCloseScopeAndStop() {
+        if(observation==null) return;
+        if(scope!=null) {
+            this.scope.close();
+            this.scope = null;
+        }
+        observation.stop();
+    }
+
+}
diff --git 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/RootRequestMapper.java
 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/RootRequestMapper.java
index 9f3c1204fe5..7e6b7cfd9c7 100644
--- 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/RootRequestMapper.java
+++ 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/RootRequestMapper.java
@@ -22,72 +22,18 @@
 import org.apache.wicket.SystemMapper;
 import org.apache.wicket.core.request.handler.ListenerRequestHandler;
 import org.apache.wicket.core.request.mapper.PageInstanceMapper;
-import org.apache.wicket.request.IRequestCycle;
 import org.apache.wicket.request.IRequestHandler;
 import org.apache.wicket.request.IRequestMapper;
 import org.apache.wicket.request.Request;
-import org.apache.wicket.request.component.IRequestablePage;
 
-import org.apache.causeway.applib.services.iactnlayer.InteractionContext;
-import org.apache.causeway.applib.services.iactnlayer.InteractionService;
-import org.apache.causeway.commons.internal.base._Lazy;
-import org.apache.causeway.core.metamodel.context.MetaModelContext;
 import org.apache.causeway.viewer.wicket.ui.pages.PageAbstract;
 
-import lombok.extern.slf4j.Slf4j;
-
 public final class RootRequestMapper extends SystemMapper implements 
IRequestMapper {
 
-    public static ThreadLocal<InteractionContext> X = new ThreadLocal<>();
-
-    @Slf4j
-    record RequestHandlerWrapper(
-            InteractionService interactionService,
-            InteractionContext interactionContext,
-            _Lazy<IRequestHandler> delegate) implements IRequestHandler {
-
-        @Override
-        public void respond(final IRequestCycle requestCycle) {
-            if(interactionContext==null) {
-                delegate.get().respond(requestCycle);
-                return;
-            }
-            
interactionService.testSupport().openInteraction(interactionContext);
-            X.remove();
-            delegate.get().respond(requestCycle);
-
-
-//            interactionService.run(interactionContext, ()->{
-//                delegate.get().respond(requestCycle);
-//                X.remove();
-//            });
-        }
-        @Override
-        public void detach(final IRequestCycle requestCycle) {
-            if(delegate.isMemoized()) {
-                delegate.get().detach(requestCycle);
-            }
-            interactionService.closeInteractionLayers();
-        }
-    }
-
     public RootRequestMapper(final Application application) {
         super(application);
     }
 
-    @Override
-    public IRequestHandler mapRequest(final Request request) {
-        var mmc = MetaModelContext.instanceElseFail();
-//        var ic = new SessionAuthenticator(mmc.getInteractionService(), 
mmc.lookupServiceElseFail(UserService.class))
-//            .determineInteractionContext()
-//            .orElse(null);
-
-        return new RequestHandlerWrapper(
-                        mmc.getInteractionService(),
-                        X.get(),
-                        _Lazy.threadSafe(()->super.mapRequest(request)));
-    }
-
     // intercept AJAX requests and reload view-models so any detached entities 
are re-fetched
     @Override
     protected IRequestMapper newPageInstanceMapper() {
@@ -95,16 +41,10 @@ protected IRequestMapper newPageInstanceMapper() {
             @Override
             public IRequestHandler mapRequest(final Request request) {
                 var handler = super.mapRequest(request);
-
-                if (handler instanceof ListenerRequestHandler) {
-
-                    final IRequestablePage iRequestablePage = 
((ListenerRequestHandler) handler).getPage();
-
-                    if (iRequestablePage instanceof PageAbstract pageAbstract) 
{
-                        pageAbstract.onNewRequestCycle();
-                    }
+                if (handler instanceof ListenerRequestHandler 
listenerRequestHandler
+                        && listenerRequestHandler.getPage() instanceof 
PageAbstract pageAbstract) {
+                    pageAbstract.onNewRequestCycle();
                 }
-
                 return handler;
             }
         };
diff --git 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/SessionAuthenticator.java
 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/SessionAuthenticator.java
index 85f726aeec7..4d1e223f174 100644
--- 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/SessionAuthenticator.java
+++ 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/SessionAuthenticator.java
@@ -47,10 +47,11 @@ public Optional<InteractionContext> 
determineInteractionContext() {
 
         var interactionContext = session.getInteractionContext();
         if (interactionContext == null) {
-            log.warn("onBeginRequest out - session was not opened (because no 
authentication)");
+            log.warn("session was not opened (because not authenticated)");
             return Optional.empty();
         }
 
+        // impersonation support
         return Optional.of(
                 userService
                     .lookupImpersonatedUser()
diff --git 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/TelemetryStartHandler.java
 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/TelemetryStartHandler.java
new file mode 100644
index 00000000000..b5eb4badd62
--- /dev/null
+++ 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/TelemetryStartHandler.java
@@ -0,0 +1,51 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.causeway.viewer.wicket.viewer.integration;
+
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.cycle.IRequestCycleListener;
+import org.apache.wicket.request.cycle.RequestCycle;
+
+import 
org.apache.causeway.commons.internal.observation.CausewayObservationInternal.ObservationProvider;
+
+/**
+ * @since 4.0
+ */
+public record TelemetryStartHandler(
+        ObservationProvider observationProvider)
+implements IRequestCycleListener {
+
+    @Override
+    public synchronized void onBeginRequest(final RequestCycle requestCycle) {
+        if (requestCycle instanceof RequestCycle2 requestCycle2) {
+            requestCycle2.observation = observationProvider.get("Apache Wicket 
Request Cycle");
+            requestCycle2.observationStartAndOpenScope();
+        }
+    }
+
+    @Override
+    public IRequestHandler onException(final RequestCycle requestCycle, final 
Exception ex) {
+        if (requestCycle instanceof RequestCycle2 requestCycle2
+                && requestCycle2.observation!=null) {
+            requestCycle2.observation.error(ex);
+        }
+        return null;
+    }
+
+}
diff --git 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/TelemetryStopHandler.java
 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/TelemetryStopHandler.java
new file mode 100644
index 00000000000..d33a11f1ff0
--- /dev/null
+++ 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/TelemetryStopHandler.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.causeway.viewer.wicket.viewer.integration;
+
+import org.apache.wicket.request.cycle.IRequestCycleListener;
+import org.apache.wicket.request.cycle.RequestCycle;
+
+import org.apache.causeway.applib.services.metrics.MetricsService;
+
+/**
+ * @since 4.0
+ */
+public record TelemetryStopHandler(
+        MetricsService metricsService)
+implements IRequestCycleListener {
+
+    @Override
+    public void onEndRequest(final RequestCycle requestCycle) {
+        if (requestCycle instanceof RequestCycle2 requestCycle2
+                && requestCycle2.observation!=null) {
+
+            if(requestCycle2.millisSinceStart() > 50) { // avoid clutter
+                
requestCycle2.observation.highCardinalityKeyValue("numberEntitiesLoaded", 
""+metricsService.numberEntitiesLoaded());
+                
requestCycle2.observation.highCardinalityKeyValue("numberEntitiesDirtied", 
""+metricsService.numberEntitiesDirtied());
+            }
+
+            requestCycle2.observation.stop();
+        }
+    }
+
+}
diff --git 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/WebRequestCycleForCauseway.java
 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/WebRequestCycleForCauseway.java
index b1fb6b7a8fe..b2f1650080e 100644
--- 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/WebRequestCycleForCauseway.java
+++ 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/WebRequestCycleForCauseway.java
@@ -54,16 +54,15 @@
 import org.apache.causeway.applib.services.exceprecog.Recognition;
 import org.apache.causeway.applib.services.i18n.TranslationContext;
 import org.apache.causeway.applib.services.iactn.Interaction;
-import org.apache.causeway.applib.services.metrics.MetricsService;
+import org.apache.causeway.applib.services.iactnlayer.InteractionService;
 import org.apache.causeway.applib.services.user.UserService;
 import org.apache.causeway.commons.collections.Can;
 import org.apache.causeway.commons.internal.base._Strings;
-import org.apache.causeway.commons.internal.base._Timing;
-import org.apache.causeway.commons.internal.base._Timing.StopWatch;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.core.metamodel.context.HasMetaModelContext;
 import org.apache.causeway.core.metamodel.context.MetaModelContext;
 import org.apache.causeway.core.metamodel.spec.feature.ObjectMember;
+import org.apache.causeway.core.metamodel.specloader.SpecificationLoader;
 import 
org.apache.causeway.core.metamodel.specloader.validator.MetaModelInvalidException;
 import org.apache.causeway.viewer.commons.model.error.ExceptionModel;
 import org.apache.causeway.viewer.wicket.model.models.PageType;
@@ -73,7 +72,6 @@
 import org.apache.causeway.viewer.wicket.ui.pages.mmverror.MmvErrorPage;
 import org.apache.causeway.viewer.wicket.ui.panels.PromptFormAbstract;
 
-import lombok.Setter;
 import lombok.extern.slf4j.Slf4j;
 
 /**
@@ -84,7 +82,12 @@
  * @since 2.0
  */
 @Slf4j
-public class WebRequestCycleForCauseway
+public record WebRequestCycleForCauseway(
+        InteractionService interactionService,
+        PageClassRegistry pageClassRegistry,
+        ExceptionRecognizerService exceptionRecognizerService,
+        SpecificationLoader specificationLoader,
+        SessionAuthenticator sessionAuthenticator)
 implements
     HasMetaModelContext,
     IRequestCycleListener {
@@ -121,17 +124,14 @@ static boolean isExpiryMessageTimeframeExpired() {
     private static final MetaDataKey<SessionLifecyclePhase> 
SESSION_LIFECYCLE_PHASE_KEY =
             new MetaDataKey<>() { private static final long serialVersionUID = 
1L; };
 
-    @Setter
-    private PageClassRegistry pageClassRegistry;
-
-    private static ThreadLocal<StopWatch> timings = 
ThreadLocal.withInitial(_Timing::now);
+    public WebRequestCycleForCauseway(final MetaModelContext mmc, final 
PageClassRegistry pageClassRegistry) {
+        this(mmc.getInteractionService(), pageClassRegistry, 
mmc.lookupServiceElseFail(ExceptionRecognizerService.class),
+                mmc.getSpecificationLoader(),
+                new SessionAuthenticator(mmc.getInteractionService(), 
mmc.lookupServiceElseFail(UserService.class)));
+    }
 
     @Override
-    public synchronized void onBeginRequest(final RequestCycle requestCycle) {
-
-        if(log.isTraceEnabled()) {
-            log.trace("onBeginRequest in");
-        }
+    public void onBeginRequest(final RequestCycle requestCycle) {
 
         if (!Session.exists()) {
             // Track if session was created from an expired one to notify user 
of the refresh.
@@ -145,19 +145,8 @@ public synchronized void onBeginRequest(final RequestCycle 
requestCycle) {
             return;
         }
 
-        var mmc = MetaModelContext.instanceElseFail();
-        var ic = new SessionAuthenticator(mmc.getInteractionService(), 
mmc.lookupServiceElseFail(UserService.class))
-              .determineInteractionContext()
-              .orElse(null);
-        RootRequestMapper.X.set(ic);
-
-        if(log.isTraceEnabled()) {
-            log.trace("onBeginRequest out - session about to open");
-        }
-
-        if(log.isDebugEnabled()) {
-            timings.set(_Timing.now());
-        }
+        sessionAuthenticator.determineInteractionContext()
+            .ifPresent(interactionService.testSupport()::openInteraction);
     }
 
     @Override
@@ -182,7 +171,7 @@ public void onRequestHandlerResolved(final RequestCycle 
requestCycle, final IReq
         } else if(handler instanceof RenderPageRequestHandler requestHandler) {
 
             // using side-effect free access to MM validation result
-            var validationResult = 
getMetaModelContext().getSpecificationLoader().getValidationResult()
+            var validationResult = specificationLoader().getValidationResult()
             .orElseThrow(()->_Exceptions.illegalState("Application is not 
fully initialized yet."));
 
             if(validationResult.hasFailures()) {
@@ -217,9 +206,6 @@ public void onRequestHandlerResolved(final RequestCycle 
requestCycle, final IReq
             }
         }
 
-        if(log.isTraceEnabled()) {
-            log.trace("onRequestHandlerResolved out");
-        }
     }
 
     /**
@@ -238,41 +224,14 @@ public void onRequestHandlerExecuted(final RequestCycle 
requestCycle, final IReq
      */
     @Override
     public synchronized void onEndRequest(final RequestCycle requestCycle) {
-
-        if(log.isDebugEnabled()) {
-            var metricsServiceIfAny = 
getMetaModelContext().lookupService(MetricsService.class);
-            long took = timings.get().getMillis();
-            if(took > 50) {  // avoid too much clutter
-                if(metricsServiceIfAny.isPresent()) {
-                    var metricsService = metricsServiceIfAny.get();
-                    int numberEntitiesLoaded = 
metricsService.numberEntitiesLoaded();
-                    int numberEntitiesDirtied = 
metricsService.numberEntitiesDirtied();
-                    if(numberEntitiesLoaded > 0 || numberEntitiesDirtied > 0) {
-                        log.debug("onEndRequest  took: {}ms  
numberEntitiesLoaded: {}, numberEntitiesDirtied: {}", took, 
numberEntitiesLoaded, numberEntitiesDirtied);
-                    }
-                } else {
-                    log.debug("onEndRequest  took: {}ms", took);
-                }
-            }
-        }
-
-    }
-
-    @Override
-    public void onDetach(final RequestCycle requestCycle) {
-        // detach the current @RequestScope, if any
-        IRequestCycleListener.super.onDetach(requestCycle);
+        interactionService.closeInteractionLayers();
     }
 
     @Override
     public IRequestHandler onException(final RequestCycle cycle, final 
Exception ex) {
 
-        if(log.isDebugEnabled()) {
-            log.debug("onException {}  took: {}ms", 
ex.getClass().getSimpleName(), timings.get().getMillis());
-        }
-
         // using side-effect free access to MM validation result
-        var validationResult = 
getMetaModelContext().getSpecificationLoader().getValidationResult()
+        var validationResult = specificationLoader().getValidationResult()
                 .orElse(null);
         if(validationResult!=null
                 && validationResult.hasFailures()) {
@@ -295,8 +254,7 @@ public IRequestHandler onException(final RequestCycle 
cycle, final Exception ex)
             }
 
             // handle recognized exceptions gracefully also
-            var exceptionRecognizerService = getExceptionRecognizerService();
-            var recognizedIfAny = exceptionRecognizerService.recognize(ex);
+            var recognizedIfAny = exceptionRecognizerService().recognize(ex);
             if(recognizedIfAny.isPresent()) {
                 
addWarning(recognizedIfAny.get().toMessage(getMetaModelContext().getTranslationService()));
                 return respondGracefully(cycle);
@@ -397,16 +355,13 @@ protected IRequestablePage errorPageFor(final Exception 
ex) {
         }
 
         // using side-effect free access to MM validation result
-        var validationResult = 
mmc.getSpecificationLoader().getValidationResult()
+        var validationResult = specificationLoader().getValidationResult()
                 .orElse(null);
         if(validationResult!=null
                 && validationResult.hasFailures())
             return new MmvErrorPage(validationResult.getMessages("[%d] %s"));
 
-        var exceptionRecognizerService = mmc.getServiceRegistry()
-            .lookupServiceElseFail(ExceptionRecognizerService.class);
-
-        final Optional<Recognition> recognition = exceptionRecognizerService
+        final Optional<Recognition> recognition = exceptionRecognizerService()
                 .recognizeFromSelected(
                         Can.<ExceptionRecognizer>of(
                                         pageExpiredExceptionRecognizer,
@@ -461,9 +416,9 @@ private IRequestablePage newSignInPage(final ExceptionModel 
exceptionModel) {
      * Matters should improve once CAUSEWAY-299 gets implemented...
      */
     protected boolean isSignedIn() {
-        if(!isInInteraction())
+        if(!interactionService.isInInteraction())
             return false;
-        return getWicketAuthenticatedWebSession().isSignedIn();
+        return AuthenticatedWebSession.get().isSignedIn();
     }
 
     private boolean userHasSessionWithRememberMe(final RequestCycle 
requestCycle) {
@@ -482,18 +437,4 @@ private boolean userHasSessionWithRememberMe(final 
RequestCycle requestCycle) {
         return false;
     }
 
-    // -- DEPENDENCIES
-
-    private ExceptionRecognizerService getExceptionRecognizerService() {
-        return 
getMetaModelContext().getServiceRegistry().lookupServiceElseFail(ExceptionRecognizerService.class);
-    }
-
-    private boolean isInInteraction() {
-        return getMetaModelContext().getInteractionService().isInInteraction();
-    }
-
-    private AuthenticatedWebSession getWicketAuthenticatedWebSession() {
-        return AuthenticatedWebSession.get();
-    }
-
 }
diff --git 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/wicketapp/CausewayWicketApplication.java
 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/wicketapp/CausewayWicketApplication.java
index 41bb10f8294..61d9db93014 100644
--- 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/wicketapp/CausewayWicketApplication.java
+++ 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/wicketapp/CausewayWicketApplication.java
@@ -21,6 +21,7 @@
 import java.time.Duration;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.UUID;
 import java.util.function.Function;
 
@@ -41,17 +42,19 @@
 import org.apache.wicket.markup.head.ResourceAggregator;
 import 
org.apache.wicket.markup.head.filter.JavaScriptFilteredIntoFooterHeaderResponse;
 import org.apache.wicket.markup.html.WebPage;
-import org.apache.wicket.request.cycle.IRequestCycleListener;
 import org.apache.wicket.request.cycle.PageRequestHandlerTracker;
 import org.apache.wicket.request.resource.CssResourceReference;
 import org.apache.wicket.settings.RequestCycleSettings;
 import org.apache.wicket.spring.injection.annot.SpringComponentInjector;
 
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Component;
 
 import org.apache.causeway.applib.services.inject.ServiceInjector;
+import org.apache.causeway.applib.services.metrics.MetricsService;
 import org.apache.causeway.commons.internal.concurrent._ConcurrentContext;
 import org.apache.causeway.commons.internal.concurrent._ConcurrentTaskList;
+import 
org.apache.causeway.commons.internal.observation.CausewayObservationInternal;
 import org.apache.causeway.core.config.CausewayConfiguration;
 import org.apache.causeway.core.config.environment.CausewaySystemEnvironment;
 import org.apache.causeway.core.metamodel.context.MetaModelContext;
@@ -68,7 +71,10 @@
 import 
org.apache.causeway.viewer.wicket.viewer.integration.AuthenticatedWebSessionForCauseway;
 import 
org.apache.causeway.viewer.wicket.viewer.integration.CausewayResourceSettings;
 import 
org.apache.causeway.viewer.wicket.viewer.integration.ConverterForObjectAdapter;
+import org.apache.causeway.viewer.wicket.viewer.integration.RequestCycle2;
 import org.apache.causeway.viewer.wicket.viewer.integration.RootRequestMapper;
+import 
org.apache.causeway.viewer.wicket.viewer.integration.TelemetryStartHandler;
+import 
org.apache.causeway.viewer.wicket.viewer.integration.TelemetryStopHandler;
 import 
org.apache.causeway.viewer.wicket.viewer.integration.WebRequestCycleForCauseway;
 
 import lombok.Getter;
@@ -112,6 +118,10 @@ public static CausewayWicketApplication get() {
     @Inject private List<WicketApplicationInitializer> applicationInitializers;
     @Inject private CausewaySystemEnvironment systemEnvironment;
     @Inject private CausewayConfiguration configuration;
+    @Inject private MetricsService metricService;
+
+    @Qualifier("causeway-wicketviewer")
+    @Inject private CausewayObservationInternal observationInternal;
 
     @Getter(onMethod = @__(@Override))
     @Inject private ComponentFactoryRegistry componentFactoryRegistry;
@@ -138,9 +148,6 @@ protected void internalInit() {
         // settings before any other.
         setResourceSettings(new CausewayResourceSettings(this));
         super.internalInit();
-        //setRequestCycleProvider(RequestCycle2::new);
-
-        setRootRequestMapper(new RootRequestMapper(this));
     }
 
     private AjaxRequestTarget decorate(final AjaxRequestTarget 
ajaxRequestTarget) {
@@ -202,10 +209,15 @@ protected void init() {
                 .submit(_ConcurrentContext.sequential())
                 .await();
 
+            setRequestCycleProvider(RequestCycle2::new);
+            setRootRequestMapper(new RootRequestMapper(this));
             
getRequestCycleSettings().setRenderStrategy(RequestCycleSettings.RenderStrategy.REDIRECT_TO_RENDER);
             getResourceSettings().setParentFolderPlaceholder("$up$");
 
-            
getRequestCycleListeners().add(createWebRequestCycleListenerForCauseway());
+            getRequestCycleListeners().add(new 
TelemetryStartHandler(Objects.requireNonNull(observationInternal)
+                    .provider(TelemetryStartHandler.class)));
+            getRequestCycleListeners().add(new 
WebRequestCycleForCauseway(metaModelContext, getPageClassRegistry()));
+            getRequestCycleListeners().add(new 
TelemetryStopHandler(metricService));
             getRequestCycleListeners().add(new PageRequestHandlerTracker());
 
             //XXX CAUSEWAY-2530, don't recreate expired pages
@@ -275,15 +287,6 @@ protected String defaultEncryptionKey() {
 
     // //////////////////////////////////////
 
-    /**
-     * Factored out for easy (informal) pluggability.
-     */
-    protected IRequestCycleListener createWebRequestCycleListenerForCauseway() 
{
-        var webRequestCycleForCauseway = new WebRequestCycleForCauseway();
-        
webRequestCycleForCauseway.setPageClassRegistry(getPageClassRegistry());
-        return webRequestCycleForCauseway;
-    }
-
     protected static final Function<ComponentFactory, 
Iterable<CssResourceReference>> getCssResourceReferences =
             (final ComponentFactory input) -> {
                 final CssResourceReference cssResourceReference = 
input.getCssResourceReference();

Reply via email to