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

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


The following commit(s) were added to refs/heads/v4 by this push:
     new 28f95999ba6 CAUSEWAY-2297: removes ModelAbstract (wkt)
28f95999ba6 is described below

commit 28f95999ba60fcb2215e4fc91c5445acbd240644
Author: Andi Huber <[email protected]>
AuthorDate: Tue Jul 29 07:40:00 2025 +0200

    CAUSEWAY-2297: removes ModelAbstract (wkt)
---
 .../wicket/model/models/BookmarkedPagesModel.java  |  38 +++--
 .../viewer/wicket/model/models/ModelAbstract.java  |  42 ------
 .../wicket/model/models/WicketComponentUtils.java  |  61 --------
 .../wicket/model/timetaken/TimeTakenModel.java     |   6 +-
 .../wicket/ui/components/about/AboutPanel.java     |   2 +-
 .../ui/components/about/JarManifestModel.java      |  37 ++---
 .../ui/components/about/JarManifestPanel.java      |   5 +-
 .../viewer/wicket/ui/errors/ExceptionModel.java    | 157 +++++++++++----------
 .../wicket/ui/errors/ExceptionStackTracePanel.java |  18 +--
 .../viewer/wicket/ui/pages/PageAbstract.java       |   2 +-
 .../viewer/wicket/ui/pages/error/ErrorPage.java    |  32 -----
 11 files changed, 133 insertions(+), 267 deletions(-)

diff --git 
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/BookmarkedPagesModel.java
 
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/BookmarkedPagesModel.java
index 68300d91f40..29547f83508 100644
--- 
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/BookmarkedPagesModel.java
+++ 
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/BookmarkedPagesModel.java
@@ -18,19 +18,28 @@
  */
 package org.apache.causeway.viewer.wicket.model.models;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
 
-import org.apache.causeway.commons.internal.collections._Lists;
+import org.apache.wicket.model.IModel;
 
-public class BookmarkedPagesModel extends 
ModelAbstract<List<BookmarkTreeNode>> {
+import org.apache.causeway.core.metamodel.context.MetaModelContext;
 
-    private static final long serialVersionUID = 1L;
+public record BookmarkedPagesModel(
+    List<BookmarkTreeNode> rootNodes,
+    /**
+     * Meant to be transient, hence cleared on detach.
+     */
+    List<BookmarkTreeNode> depthFirstGraph
+    ) implements IModel<List<BookmarkTreeNode>> {
 
-    private final List<BookmarkTreeNode> rootNodes = _Lists.newArrayList();
+    public BookmarkedPagesModel() {
+        this(new ArrayList<>(), new ArrayList<>());
+    }
 
-    public void bookmarkPage(final BookmarkableModel bookmarkableModel) {
+    public void add(final BookmarkableModel bookmarkableModel) {
 
         var bookmark = bookmarkableModel.toBookmark().orElse(null);
         if(bookmark == null) return; // ignore
@@ -51,10 +60,13 @@ public void bookmarkPage(final BookmarkableModel 
bookmarkableModel) {
     }
 
     @Override
-    protected List<BookmarkTreeNode> load() {
-        List<BookmarkTreeNode> depthFirstGraph = _Lists.newArrayList();
+    public List<BookmarkTreeNode> getObject() {
+        if(rootNodes.isEmpty()) return List.of();
+        if(!depthFirstGraph.isEmpty()) return depthFirstGraph;
+
+        depthFirstGraph.clear();
 
-        List<BookmarkTreeNode> sortedNodes = _Lists.newArrayList(rootNodes);
+        var sortedNodes = new ArrayList<>(rootNodes);
         Collections.sort(sortedNodes);
 
         for (BookmarkTreeNode rootNode : sortedNodes) {
@@ -63,8 +75,14 @@ protected List<BookmarkTreeNode> load() {
         return depthFirstGraph;
     }
 
+    @Override
+    public void detach() {
+        depthFirstGraph.clear();
+    }
+
     public void clear() {
         rootNodes.clear();
+        depthFirstGraph.clear();
     }
 
     public boolean isEmpty() {
@@ -73,11 +91,13 @@ public boolean isEmpty() {
 
     public void remove(final BookmarkTreeNode rootNode) {
         rootNodes.remove(rootNode);
+        depthFirstGraph.clear();
     }
 
     public void remove(final UiObjectWkt objectModel) {
         var bookmark = objectModel.getOwnerBookmark();
         rootNodes.removeIf(node->node.getBookmark().equals(bookmark));
+        depthFirstGraph.clear();
     }
 
     // -- HELPER
@@ -92,7 +112,7 @@ private Optional<BookmarkTreeNode> matchRootNode(final 
BookmarkableModel bookmar
     }
 
     private int getMaxSize() {
-        return getWicketViewerSettings().getBookmarkedPages().getMaxSize();
+        return 
MetaModelContext.instanceElseFail().getWicketViewerSettings().getBookmarkedPages().getMaxSize();
     }
 
     private static void trim(final List<?> list, final int requiredSize) {
diff --git 
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ModelAbstract.java
 
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ModelAbstract.java
deleted file mode 100644
index 01554d9d47f..00000000000
--- 
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/ModelAbstract.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- *  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.model.models;
-
-import org.apache.wicket.model.LoadableDetachableModel;
-
-import org.apache.causeway.core.metamodel.context.HasMetaModelContext;
-import org.apache.causeway.core.metamodel.context.MetaModelContext;
-
-/**
- * Adapter for {@link LoadableDetachableModel}s,
- * providing access to the {@link MetaModelContext}.
- */
-public abstract class ModelAbstract<T>
-extends LoadableDetachableModel<T>
-implements HasMetaModelContext {
-
-    private static final long serialVersionUID = 1L;
-
-    protected ModelAbstract() {}
-
-    protected ModelAbstract(final T t) {
-        super.setObject(t);
-    }
-
-}
diff --git 
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/WicketComponentUtils.java
 
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/WicketComponentUtils.java
deleted file mode 100644
index 3c096d0933c..00000000000
--- 
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/models/WicketComponentUtils.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- *  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.model.models;
-
-import org.apache.wicket.Component;
-import org.apache.wicket.Page;
-import org.apache.wicket.markup.renderStrategy.DeepChildFirstVisitor;
-import org.apache.wicket.util.visit.IVisit;
-
-import org.apache.causeway.commons.internal.base._Casts;
-
-public class WicketComponentUtils {
-
-    public WicketComponentUtils(){}
-
-    /**
-     * Locates a component implementing the required class on the same page as 
the supplied component.
-     */
-    public static <T> T getFrom(Component component, final Class<T> cls) {
-        return getFrom(component.getPage(), cls);
-    }
-
-    /**
-     * Locates a component implementing the required class on the supplied 
page.
-     */
-    public static <T> T getFrom(Page page, final Class<T> cls) {
-        final Object[] pComponent = new Object[1];
-        page.visitChildren(new DeepChildFirstVisitor() {
-            @Override
-            public void component(Component component, IVisit<Void> visit) {
-                if(cls.isAssignableFrom(component.getClass())) {
-                    pComponent[0] =  component;
-                    visit.stop();
-                }
-            }
-            @Override
-            public boolean preCheck(Component component) {
-                return false;
-            }
-        });
-
-        return _Casts.uncheckedCast(pComponent[0]);
-    }
-
-}
diff --git 
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/timetaken/TimeTakenModel.java
 
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/timetaken/TimeTakenModel.java
index 93734dd739b..d9c6ac13ec2 100644
--- 
a/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/timetaken/TimeTakenModel.java
+++ 
b/viewers/wicket/model/src/main/java/org/apache/causeway/viewer/wicket/model/timetaken/TimeTakenModel.java
@@ -33,9 +33,8 @@
  *
  * @since 2.0
  */
-public class TimeTakenModel
+public record TimeTakenModel()
 implements IModel<String> {
-    private static final long serialVersionUID = 1L;
 
     public static IModel<String> createForPrototypingElseBlank(final 
MetaModelContext mmc) {
         return mmc.getSystemEnvironment().isPrototyping()
@@ -43,9 +42,6 @@ public static IModel<String> 
createForPrototypingElseBlank(final MetaModelContex
             : Model.of("");
     }
 
-    protected TimeTakenModel() {
-    }
-
     @Override
     public String getObject() {
 
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/about/AboutPanel.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/about/AboutPanel.java
index 739570cb8f5..b2720895236 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/about/AboutPanel.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/about/AboutPanel.java
@@ -79,7 +79,7 @@ public AboutPanel(final String id, final AboutModel 
aboutModel) {
         if(jarManifestModel == null) {
             Provider<InputStream> metaInfManifestProvider =
                     () -> 
servletContext.getResourceAsStream("/META-INF/MANIFEST.MF");
-            jarManifestModel = new JarManifestModel(metaInfManifestProvider);
+            jarManifestModel = JarManifestModel.of(metaInfManifestProvider);
         }
 
         add(new JarManifestPanel(ID_MANIFEST_ATTRIBUTES, jarManifestModel));
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/about/JarManifestModel.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/about/JarManifestModel.java
index c60c9bb021a..010a771dedd 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/about/JarManifestModel.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/about/JarManifestModel.java
@@ -20,7 +20,9 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.Serializable;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
@@ -36,22 +38,14 @@
 import org.apache.causeway.commons.internal.base._Strings;
 import org.apache.causeway.commons.internal.collections._Lists;
 import org.apache.causeway.commons.internal.context._Context;
-import org.apache.causeway.viewer.wicket.model.models.ModelAbstract;
 
-class JarManifestModel extends ModelAbstract<JarManifestModel> {
-
-    private static final long serialVersionUID = 1L;
-
-//    private static final List<String> VERSION_KEY_CANDIDATES =
-//            Arrays.asList("Implementation-Version", "Build-Time");
-
-    private final List<JarManifestAttributes> manifests = 
_Lists.newArrayList();
+record JarManifestModel(List<JarManifestAttributes> manifests) implements 
Serializable {
 
     /**
      * @param metaInfManifestProvider provide using 
<tt>getServletContext().getResourceAsStream("/META-INF/MANIFEST.MF")</tt>
      */
-    public JarManifestModel(final Provider<InputStream> 
metaInfManifestProvider) {
-        super();
+    public static JarManifestModel of(final Provider<InputStream> 
metaInfManifestProvider) {
+        var manifests = new ArrayList<JarManifestAttributes>();
         Manifest manifest;
         try(var metaInfManifestIs = metaInfManifestProvider.get()) {
             manifest = new Manifest(metaInfManifestIs);
@@ -66,7 +60,7 @@ public JarManifestModel(final Provider<InputStream> 
metaInfManifestProvider) {
         try {
             resEnum = 
_Context.getDefaultClassLoader().getResources(JarFile.MANIFEST_NAME);
         } catch (IOException e) {
-            return;
+            return new JarManifestModel(manifests);
         }
         final List<JarManifest> jarManifests = _Lists.newArrayList();
         while (resEnum.hasMoreElements()) {
@@ -89,6 +83,7 @@ public JarManifestModel(final Provider<InputStream> 
metaInfManifestProvider) {
         for (JarManifest jarManifest : jarManifests) {
             jarManifest.addAttributesTo(manifests);
         }
+        return new JarManifestModel(manifests);
     }
 
     private static class JarManifest implements Comparable<JarManifest> {
@@ -171,7 +166,7 @@ private static JarName asJarName(final URL url) {
         return new JarName(JarName.Type.OTHER, strippedPath);
     }
 
-    public static String stripSuffix(String path, final String suffix) {
+    private static String stripSuffix(String path, final String suffix) {
         int indexOf = path.indexOf(suffix);
         if(indexOf != -1) {
             path = path.substring(0, indexOf);
@@ -179,7 +174,7 @@ public static String stripSuffix(String path, final String 
suffix) {
         return path;
     }
 
-    static void addAttributes(final Manifest manifest, final 
List<JarManifestAttributes> attributes) {
+    private static void addAttributes(final Manifest manifest, final 
List<JarManifestAttributes> attributes) {
         final Attributes mainAttribs = manifest.getMainAttributes();
         Set<Entry<Object, Object>> entrySet = mainAttribs.entrySet();
         for (Entry<Object, Object> entry : entrySet) {
@@ -188,18 +183,4 @@ static void addAttributes(final Manifest manifest, final 
List<JarManifestAttribu
         }
     }
 
-    @Override
-    protected JarManifestModel load() {
-        return this;
-    }
-
-    @Override
-    public void setObject(final JarManifestModel ex) {
-        // no-op
-    }
-
-    public List<JarManifestAttributes> getDetail() {
-        return manifests;
-    }
-
 }
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/about/JarManifestPanel.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/about/JarManifestPanel.java
index 658dec998c7..174e41f68cc 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/about/JarManifestPanel.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/about/JarManifestPanel.java
@@ -22,6 +22,7 @@
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.Model;
 
 import org.apache.causeway.viewer.wicket.ui.panels.PanelUtil;
 
@@ -35,11 +36,11 @@ class JarManifestPanel extends Panel {
     private static final String ID_LINE = "manifestAttributeLine";
 
     public JarManifestPanel(String id, JarManifestModel manifestModel) {
-        super(id, manifestModel);
+        super(id, Model.of(manifestModel));
 
         final MarkupContainer container = new 
WebMarkupContainer(ID_MANIFEST_ATTRIBUTES);
         container.add(
-                new JarManifestListView(ID_MANIFEST_ATTRIBUTE, 
JarManifestPanel.ID_LINE, manifestModel.getDetail()));
+                new JarManifestListView(ID_MANIFEST_ATTRIBUTE, 
JarManifestPanel.ID_LINE, manifestModel.manifests()));
         add(container);
     }
 
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/errors/ExceptionModel.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/errors/ExceptionModel.java
index cd95c770650..e100cc7e01b 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/errors/ExceptionModel.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/errors/ExceptionModel.java
@@ -18,32 +18,36 @@
  */
 package org.apache.causeway.viewer.wicket.ui.errors;
 
+import java.io.Serializable;
 import java.util.List;
 import java.util.Optional;
+import java.util.function.Function;
 
 import org.apache.causeway.applib.exceptions.UnrecoverableException;
-import org.apache.causeway.applib.services.error.ErrorReportingService;
-import org.apache.causeway.applib.services.error.Ticket;
+import org.apache.causeway.applib.services.error.ErrorDetails;
 import org.apache.causeway.applib.services.exceprecog.Recognition;
 import org.apache.causeway.commons.internal.base._Casts;
 import org.apache.causeway.commons.internal.collections._Lists;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.core.metamodel.context.MetaModelContext;
 import org.apache.causeway.core.metamodel.spec.feature.ObjectMember;
-import org.apache.causeway.viewer.wicket.model.models.ModelAbstract;
 
-public class ExceptionModel extends ModelAbstract<List<StackTraceDetail>> {
-
-    private static final long serialVersionUID = 1L;
+/**
+ * Three cases: authorization exception, else recognized, else or not 
recognized.
+ */
+public record ExceptionModel(
+    ExceptionType exceptionType,
+    String mainMessage,
+    List<StackTraceDetail> stackTraceDetailList,
+    List<List<StackTraceDetail>> stackTraceDetailLists) implements 
Serializable {
 
     private static final String MAIN_MESSAGE_IF_NOT_RECOGNIZED = "Sorry, an 
unexpected error occurred.";
 
-    private List<StackTraceDetail> stackTraceDetailList;
-    private List<List<StackTraceDetail>> stackTraceDetailLists;
-    private boolean recognized;
-    private boolean authorizationCause;
-
-    private final String mainMessage;
+    public enum ExceptionType {
+        AUTHORIZATION_EXCEPTION,
+        RECOGNIZED,
+        NOT_RECOGNIZED
+    }
 
     public static ExceptionModel create(
             final MetaModelContext commonContext,
@@ -53,88 +57,55 @@ public static ExceptionModel create(
         var translationService = commonContext.getTranslationService();
         var recognizedMessage = 
recognition.map($->$.toMessage(translationService));
 
-        return new ExceptionModel(commonContext, 
recognizedMessage.orElse(null), ex);
-    }
-
-    /**
-     * Three cases: authorization exception, else recognized, else or not 
recognized.
-     */
-    private ExceptionModel(
-            final MetaModelContext commonContext, final String 
recognizedMessageIfAny, final Exception ex) {
-
-        super();
-
         final ObjectMember.AuthorizationException authorizationException =
-                causalChainOf(ex, ObjectMember.AuthorizationException.class);
+            causalChainOf(ex, ObjectMember.AuthorizationException.class);
+
+        String mainMessage = null;
+        ExceptionType exceptionType = null;
 
         if(authorizationException != null) {
-            this.authorizationCause = true;
-            this.mainMessage = authorizationException.getMessage();
+            exceptionType = ExceptionType.AUTHORIZATION_EXCEPTION;
+            mainMessage = authorizationException.getMessage();
         } else {
-            this.authorizationCause = false;
-            if(recognizedMessageIfAny != null) {
-                this.recognized = true;
-                this.mainMessage = recognizedMessageIfAny;
+            if(recognizedMessage.isPresent()) {
+                exceptionType = ExceptionType.RECOGNIZED;
+                mainMessage = recognizedMessage.get();
             } else {
-                this.recognized =false;
-
+                exceptionType = ExceptionType.NOT_RECOGNIZED;
                 // see if we can find a NonRecoverableException in the stack 
trace
 
                 UnrecoverableException nonRecoverableException =
-                _Exceptions.streamCausalChain(ex)
-                .filter(UnrecoverableException.class::isInstance)
-                .map(UnrecoverableException.class::cast)
-                .findFirst()
-                .orElse(null);
+                    _Exceptions.streamCausalChain(ex)
+                        .filter(UnrecoverableException.class::isInstance)
+                        .map(UnrecoverableException.class::cast)
+                        .findFirst()
+                        .orElse(null);
 
-                this.mainMessage = nonRecoverableException != null
+                mainMessage = nonRecoverableException != null
                         ? nonRecoverableException.getMessage()
-                                : MAIN_MESSAGE_IF_NOT_RECOGNIZED;
+                        : MAIN_MESSAGE_IF_NOT_RECOGNIZED;
             }
         }
-        stackTraceDetailList = asStackTrace(ex);
-        stackTraceDetailLists = asStackTraces(ex);
-    }
 
-    @Override
-    protected List<StackTraceDetail> load() {
-        return stackTraceDetailList;
+        return new ExceptionModel(exceptionType, mainMessage, 
asStackTrace(ex), asStackTraces(ex));
     }
 
-    private static <T extends Exception> T causalChainOf(final Exception ex, 
final Class<T> exType) {
 
-        final List<Throwable> causalChain = _Exceptions.getCausalChain(ex);
-        for (Throwable cause : causalChain) {
-            if(exType.isAssignableFrom(cause.getClass())) {
-                return _Casts.uncheckedCast(cause);
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public void setObject(final List<StackTraceDetail> stackTraceDetail) {
-        if(stackTraceDetail == null) {
-            return;
-        }
-        this.stackTraceDetailList = stackTraceDetail;
-    }
-
-    private Ticket ticket;
-    public Optional<Ticket> getTicket() {
-        return Optional.ofNullable(ticket);
-    }
-
-    /**
-     * Optionally called if an {@link ErrorReportingService} has been 
configured and returns a <tt>non-null</tt> ticket
-     * to represent the fact that the error has been recorded.
-     */
-    public void setTicket(final Ticket ticket) {
-        this.ticket = ticket;
-    }
+//    private Ticket ticket;
+//    public Optional<Ticket> getTicket() {
+//        return Optional.ofNullable(ticket);
+//    }
+//
+//    /**
+//     * Optionally called if an {@link ErrorReportingService} has been 
configured and returns a <tt>non-null</tt> ticket
+//     * to represent the fact that the error has been recorded.
+//     */
+//    public void setTicket(final Ticket ticket) {
+//        this.ticket = ticket;
+//    }
 
     public boolean isRecognized() {
-        return recognized;
+        return exceptionType==ExceptionType.RECOGNIZED;
     }
 
     public String getMainMessage() {
@@ -145,7 +116,7 @@ public String getMainMessage() {
      * Whether this was an authorization exception (so UI can suppress 
information, eg stack trace).
      */
     public boolean isAuthorizationException() {
-        return authorizationCause;
+        return exceptionType==ExceptionType.AUTHORIZATION_EXCEPTION;
     }
 
     public List<StackTraceDetail> getStackTrace() {
@@ -155,6 +126,38 @@ public List<List<StackTraceDetail>> getStackTraces() {
         return stackTraceDetailLists;
     }
 
+    public ErrorDetails asErrorDetails(Function<StackTraceDetail, String> 
formatter) {
+        final boolean recognized = isRecognized();
+        final boolean authorizationException = isAuthorizationException();
+
+        final List<String> stackDetailList = stackTraceDetailList
+            .stream()
+            .map(formatter)
+            .toList();
+
+        final List<List<StackTraceDetail>> stackTraces = getStackTraces();
+        final List<List<String>> stackDetailLists = _Lists.newArrayList();
+        for (List<StackTraceDetail> trace : stackTraces) {
+            stackDetailLists.add(trace.stream()
+                .map(formatter)
+                .toList());
+        }
+
+        return new ErrorDetails(mainMessage, recognized, 
authorizationException, stackDetailList, stackDetailLists);
+    }
+
+    // -- HELPER
+
+    private static <T extends Exception> T causalChainOf(final Exception ex, 
final Class<T> exType) {
+        final List<Throwable> causalChain = _Exceptions.getCausalChain(ex);
+        for (Throwable cause : causalChain) {
+            if(exType.isAssignableFrom(cause.getClass())) {
+                return _Casts.uncheckedCast(cause);
+            }
+        }
+        return null;
+    }
+
     private static List<StackTraceDetail> asStackTrace(final Throwable ex) {
         List<StackTraceDetail> stackTrace = _Lists.newArrayList();
         List<Throwable> causalChain = _Exceptions.getCausalChain(ex);
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/errors/ExceptionStackTracePanel.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/errors/ExceptionStackTracePanel.java
index 4b8175b3735..1606cfb7b04 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/errors/ExceptionStackTracePanel.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/errors/ExceptionStackTracePanel.java
@@ -18,8 +18,6 @@
  */
 package org.apache.causeway.viewer.wicket.ui.errors;
 
-import java.util.List;
-
 import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.MarkupContainer;
 import org.apache.wicket.Page;
@@ -32,7 +30,7 @@
 import org.apache.wicket.model.Model;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.request.resource.JavaScriptResourceReference;
-
+import org.apache.causeway.applib.services.error.ErrorReportingService;
 import org.apache.causeway.applib.services.error.Ticket;
 import org.apache.causeway.commons.functional.Try;
 import org.apache.causeway.viewer.wicket.model.models.PageType;
@@ -46,7 +44,7 @@
 import org.apache.causeway.viewer.wicket.ui.util.WktLinks;
 
 public class ExceptionStackTracePanel
-extends PanelBase<List<StackTraceDetail>> {
+extends PanelBase<ExceptionModel> {
 
     private static final long serialVersionUID = 1L;
 
@@ -81,16 +79,18 @@ public ExceptionStackTracePanel(
             final String id,
             final ExceptionModel exceptionModel) {
 
-        super(id, exceptionModel);
+        super(id, Model.of(exceptionModel));
 
-        var ticketIfAny = exceptionModel.getTicket();
+        var ticketOptional = lookupService(ErrorReportingService.class)
+            .map(errorReportingService->
+                
errorReportingService.reportError(exceptionModel.asErrorDetails(StackTraceDetail::line)));
 
-        var mainMessage = ticketIfAny
+        var mainMessage = ticketOptional
             .map(Ticket::getUserMessage)
             .orElseGet(exceptionModel::getMainMessage);
         Wkt.labelAdd(this, ID_MAIN_MESSAGE, mainMessage);
 
-        ticketIfAny
+        ticketOptional
             .map(Ticket::getMarkup)
             .ifPresentOrElse(ticketMarkup->{
                 Wkt.markupAdd(this, ID_TICKET_MARKUP, ticketMarkup);
@@ -101,7 +101,7 @@ public ExceptionStackTracePanel(
         final boolean suppressExceptionDetail =
                 exceptionModel.isAuthorizationException()
                 || exceptionModel.isRecognized()
-                || ticketIfAny
+                || ticketOptional
                     .map(Ticket::getStackTracePolicy)
                     .map(Ticket.StackTracePolicy.HIDE::equals)
                     .orElse(false);
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/pages/PageAbstract.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/pages/PageAbstract.java
index 53bbb3acec2..c09fb984ac2 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/pages/PageAbstract.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/pages/PageAbstract.java
@@ -369,7 +369,7 @@ protected boolean isShowBreadcrumbs() {
 
     protected void bookmarkPageIfShown(final BookmarkableModel model) {
         getBookmarkedPagesModel()
-        .ifPresent(bm->bm.bookmarkPage(model));
+        .ifPresent(bm->bm.add(model));
     }
 
     protected void removeAnyBookmark(final UiObjectWkt model) {
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/pages/error/ErrorPage.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/pages/error/ErrorPage.java
index 7a8861ff657..7fb579132a6 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/pages/error/ErrorPage.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/pages/error/ErrorPage.java
@@ -22,9 +22,6 @@
 
 import 
org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation;
 
-import org.apache.causeway.applib.services.error.ErrorDetails;
-import org.apache.causeway.applib.services.error.ErrorReportingService;
-import org.apache.causeway.applib.services.error.Ticket;
 import org.apache.causeway.applib.services.user.UserMemento;
 import org.apache.causeway.commons.internal.collections._Lists;
 import org.apache.causeway.viewer.wicket.model.util.PageParameterUtils;
@@ -49,35 +46,6 @@ public ErrorPage(final ExceptionModel exceptionModel) {
 
         addBookmarkedPages(themeDiv);
 
-        var errorReportingService = 
super.getMetaModelContext().getServiceRegistry()
-                .lookupService(ErrorReportingService.class).orElse(null);
-
-        if(errorReportingService != null) {
-
-            final String mainMessage = exceptionModel.getMainMessage();
-            final boolean recognized = exceptionModel.isRecognized();
-            final boolean authorizationException = 
exceptionModel.isAuthorizationException();
-
-            final List<StackTraceDetail> stackTrace = 
exceptionModel.getStackTrace();
-            final List<String> stackDetailList = transform(stackTrace);
-
-            final List<List<StackTraceDetail>> stackTraces = 
exceptionModel.getStackTraces();
-            final List<List<String>> stackDetailLists = _Lists.newArrayList();
-            for (List<StackTraceDetail> trace : stackTraces) {
-                stackDetailLists.add(transform(trace));
-            }
-
-            final ErrorDetails errorDetails =
-                    new ErrorDetails(mainMessage, recognized, 
authorizationException, stackDetailList, stackDetailLists);
-
-            final Ticket ticket = 
errorReportingService.reportError(errorDetails);
-
-            if (ticket != null) {
-                exceptionModel.setTicket(ticket);
-            }
-
-        }
-
         var pageClassRegistry = 
super.getServiceRegistry().lookupServiceElseFail(PageClassRegistry.class);
 
         themeDiv.add(new ExceptionStackTracePanel(pageClassRegistry, 
ID_EXCEPTION_STACK_TRACE, exceptionModel));

Reply via email to