You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2015/10/23 17:20:59 UTC

isis git commit: ISIS-1210: implementation of ErrorReportingService

Repository: isis
Updated Branches:
  refs/heads/master 7b8bae202 -> 714dd129e


ISIS-1210: implementation of ErrorReportingService

... with support in the Wicket UI viewer


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/714dd129
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/714dd129
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/714dd129

Branch: refs/heads/master
Commit: 714dd129e0ed6993fea6a57385a9f6396f3a1588
Parents: 7b8bae2
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Fri Oct 23 16:10:49 2015 +0100
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Fri Oct 23 16:10:49 2015 +0100

----------------------------------------------------------------------
 .../applib/services/error/ErrorDetails.java     | 65 ++++++++++++++++
 .../services/error/ErrorReportingService.java   | 28 +++++++
 .../isis/applib/services/error/Ticket.java      | 80 ++++++++++++++++++++
 .../viewer/wicket/ui/errors/ExceptionModel.java | 18 +++++
 .../ui/errors/ExceptionStackTracePanel.html     | 18 ++++-
 .../ui/errors/ExceptionStackTracePanel.java     | 27 ++++++-
 .../viewer/wicket/ui/pages/error/ErrorPage.java | 38 ++++++++++
 7 files changed, 269 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/714dd129/core/applib/src/main/java/org/apache/isis/applib/services/error/ErrorDetails.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/error/ErrorDetails.java b/core/applib/src/main/java/org/apache/isis/applib/services/error/ErrorDetails.java
new file mode 100644
index 0000000..2ce4211
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/error/ErrorDetails.java
@@ -0,0 +1,65 @@
+/*
+ *  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.isis.applib.services.error;
+
+import java.util.List;
+
+/**
+ * Details of the error (obtained from the thrown exception), passed as part of the request to the
+ * {@link ErrorReportingService}.
+ *
+ * <p>
+ *     Implementation note: a class has been used here so that additional fields might be added in the future.
+ * </p>
+ */
+public class ErrorDetails {
+
+    private final String mainMessage;
+    private final boolean recognized;
+    private final boolean authorizationCause;
+    private final List<String> stackTraceDetailList;
+
+    public ErrorDetails(
+            final String mainMessage,
+            final boolean recognized,
+            final boolean authorizationCause,
+            final List<String> stackTraceDetailList) {
+        this.mainMessage = mainMessage;
+        this.recognized = recognized;
+        this.authorizationCause = authorizationCause;
+        this.stackTraceDetailList = stackTraceDetailList;
+    }
+
+    public String getMainMessage() {
+        return mainMessage;
+    }
+
+    public boolean isRecognized() {
+        return recognized;
+    }
+
+    public boolean isAuthorizationCause() {
+        return authorizationCause;
+    }
+
+    public List<String> getStackTraceDetailList() {
+        return stackTraceDetailList;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/714dd129/core/applib/src/main/java/org/apache/isis/applib/services/error/ErrorReportingService.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/error/ErrorReportingService.java b/core/applib/src/main/java/org/apache/isis/applib/services/error/ErrorReportingService.java
new file mode 100644
index 0000000..7198483
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/error/ErrorReportingService.java
@@ -0,0 +1,28 @@
+/*
+ *  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.isis.applib.services.error;
+
+/**
+ * Optional SPI service.
+ */
+public interface ErrorReportingService {
+
+    Ticket reportError(final ErrorDetails errorDetails);
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/714dd129/core/applib/src/main/java/org/apache/isis/applib/services/error/Ticket.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/error/Ticket.java b/core/applib/src/main/java/org/apache/isis/applib/services/error/Ticket.java
new file mode 100644
index 0000000..ec8d465
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/error/Ticket.java
@@ -0,0 +1,80 @@
+/*
+ *  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.isis.applib.services.error;
+
+import java.io.Serializable;
+
+/**
+ * Response from the {@link ErrorReportingService}, containing information to show to the end-user.
+ *
+ * <p>
+ *     Implementation notes:
+ *     <ul>
+ *         <li>a class has been used here so that additional fields might be added in the future.</li>
+ *         <li>the class is {@link Serializable}</li> so that it can be stored by the Wicket viewer as a Wicket model.
+ *     </ul>
+ * </p>
+ */
+public class Ticket implements Serializable {
+
+    private final String reference;
+    private final String userMessage;
+    private final String details;
+
+    public Ticket(final String reference, final String userMessage, final String details) {
+        this.reference = reference;
+        this.userMessage = userMessage;
+        this.details = details;
+    }
+
+    /**
+     * A unique identifier that the end-user can use to track any follow-up from this error.
+     *
+     * <p>
+     *     For example, an implementation might automatically log an issue in a bug tracking system such as JIRA, in
+     *     which case the {@link #getReference() reference} would be the JIRA issue number <tt>XXX-1234</tt>.
+     * </p>
+     */
+    public String getReference() {
+        return reference;
+    }
+
+    /**
+     * Message to display to the user.
+     *
+     * <p>
+     *     Typically this message should be short, one line long.
+     * </p>
+     */
+    public String getUserMessage() {
+        return userMessage;
+    }
+
+    /**
+     * Optional additional details to show to the end-user.
+     *
+     * <p>
+     *     For example, these might include text on how to recover from the error, or workarounds, or just further
+     *     details on contacting the help desk if the issue is severe and requires immediate attention.
+     * </p>
+     */
+    public String getDetails() {
+        return details;
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/714dd129/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionModel.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionModel.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionModel.java
index 936c3fd..e4e620f 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionModel.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionModel.java
@@ -20,10 +20,14 @@ package org.apache.isis.viewer.wicket.ui.errors;
 
 import java.util.Iterator;
 import java.util.List;
+
 import com.google.common.base.Throwables;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
+
 import org.apache.isis.applib.NonRecoverableException;
+import org.apache.isis.applib.services.error.ErrorReportingService;
+import org.apache.isis.applib.services.error.Ticket;
 import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
 import org.apache.isis.viewer.wicket.model.models.ModelAbstract;
 
@@ -98,6 +102,20 @@ public class ExceptionModel extends ModelAbstract<List<StackTraceDetail>> {
         this.stackTraceDetailList = stackTraceDetail;
     }
 
+
+    private Ticket ticket;
+    public Ticket getTicket() {
+        return 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;
     }

http://git-wip-us.apache.org/repos/asf/isis/blob/714dd129/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionStackTracePanel.html
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionStackTracePanel.html b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionStackTracePanel.html
index 2d61e32..4277658 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionStackTracePanel.html
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionStackTracePanel.html
@@ -31,9 +31,23 @@
         <wicket:panel>
             <div class="exceptionStackTracePanel">
                 <div class="errorInfo clear">
-                    <h4 wicket:id="mainMessage" class="mainMessage">[main message text]</h4>
+                    <div class="panel">
+                        <div class="panel-heading">
+                            <h2 class="mainMessage">
+                                <span class="fa fa-fw fa-warning fontAwesomeIcon" style="color:#C9302C;"></span>
+                                <span wicket:id="mainMessage">[main message text]</span>
+                            </h2>
+                        </div>
+                        <div class="panel-body" wicket:id="userDetail">
+                            <h3><span wicket:id="details">details</span></h3>
+                        </div>
+                    </div>
+                    <br/>
                     <div class="errorDetail panel panel-default" wicket:id="exceptionDetail">
-                        <div class="heading panel-heading"><span class="panel-title">Show detail</span></div>
+                        <div class="heading panel-heading">
+                            <a class="btn btn-primary" href="#" role="button">Exception details</a>
+                            <span class="panel-title"></span>
+                        </div>
                         <div class="content panel-body">
                             <div class="exceptionStackTrace">
                                 <h3>Stack trace:</h3>

http://git-wip-us.apache.org/repos/asf/isis/blob/714dd129/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionStackTracePanel.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionStackTracePanel.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionStackTracePanel.java
index 7504c47..fb81315 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionStackTracePanel.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/errors/ExceptionStackTracePanel.java
@@ -24,8 +24,12 @@ import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.head.JavaScriptReferenceHeaderItem;
 import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.basic.MultiLineLabel;
 import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.Model;
 import org.apache.wicket.request.resource.JavaScriptResourceReference;
+
+import org.apache.isis.applib.services.error.Ticket;
 import org.apache.isis.viewer.wicket.ui.util.Components;
 
 public class ExceptionStackTracePanel extends Panel {
@@ -34,6 +38,8 @@ public class ExceptionStackTracePanel extends Panel {
 
     private static final String ID_MAIN_MESSAGE = "mainMessage";
 
+    private static final String ID_USER_DETAIL = "userDetail";
+    private static final String ID_DETAILS = "details";
     private static final String ID_EXCEPTION_DETAIL = "exceptionDetail";
 
     private static final String ID_STACK_TRACE_ELEMENT = "stackTraceElement";
@@ -44,7 +50,12 @@ public class ExceptionStackTracePanel extends Panel {
     public ExceptionStackTracePanel(String id, ExceptionModel exceptionModel) {
         super(id, exceptionModel);
 
-        final String mainMessage = exceptionModel.getMainMessage();
+        final Ticket ticket = exceptionModel.getTicket();
+        final String mainMessage =
+                ticket != null && ticket.getUserMessage() != null
+                        ? ticket.getUserMessage()
+                        : exceptionModel.getMainMessage();
+
         final Label label = new Label(ID_MAIN_MESSAGE, mainMessage);
 
         // to avoid potential XSS attacks, no longer escape model strings
@@ -53,8 +64,18 @@ public class ExceptionStackTracePanel extends Panel {
 
         add(label);
 
-        final boolean suppressDetail = exceptionModel.isAuthorizationException() || exceptionModel.isRecognized();
-        if(suppressDetail) {
+        final String ticketDetail = ticket != null ? ticket.getDetails(): null;
+        if(ticketDetail == null) {
+            Components.permanentlyHide(this, ID_USER_DETAIL);
+        } else {
+            final WebMarkupContainer panel = new WebMarkupContainer(ID_USER_DETAIL);
+            final MultiLineLabel details = new MultiLineLabel(ID_DETAILS, Model.of(ticketDetail));
+            panel.add(details);
+            add(panel);
+        }
+
+        final boolean suppressExceptionDetail = exceptionModel.isAuthorizationException() || exceptionModel.isRecognized();
+        if(suppressExceptionDetail) {
             Components.permanentlyHide(this, ID_EXCEPTION_DETAIL);
         } else {
 

http://git-wip-us.apache.org/repos/asf/isis/blob/714dd129/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.java
----------------------------------------------------------------------
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.java
index ef22601..6824c8b 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.java
@@ -19,10 +19,22 @@
 
 package org.apache.isis.viewer.wicket.ui.pages.error;
 
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+
 import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation;
+
+import org.apache.isis.applib.services.error.ErrorDetails;
+import org.apache.isis.applib.services.error.ErrorReportingService;
+import org.apache.isis.applib.services.error.Ticket;
 import org.apache.isis.viewer.wicket.model.common.PageParametersUtils;
 import org.apache.isis.viewer.wicket.ui.errors.ExceptionModel;
 import org.apache.isis.viewer.wicket.ui.errors.ExceptionStackTracePanel;
+import org.apache.isis.viewer.wicket.ui.errors.StackTraceDetail;
 import org.apache.isis.viewer.wicket.ui.pages.PageAbstract;
 
 /**
@@ -41,6 +53,32 @@ public class ErrorPage extends PageAbstract {
 
         addBookmarkedPages(themeDiv);
 
+        final ErrorReportingService errorReportingService = getServicesInjector()
+                .lookupService(ErrorReportingService.class);
+        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> stackDetails = Lists.transform(stackTrace, new Function<StackTraceDetail, String>() {
+                @Nullable @Override public String apply(final StackTraceDetail stackTraceDetail) {
+                    return stackTraceDetail.getLine();
+                }
+            });
+
+            final ErrorDetails errorDetails = new ErrorDetails(mainMessage, recognized, authorizationException,
+                    stackDetails);
+
+            final Ticket ticket = errorReportingService.reportError(errorDetails);
+
+            if (ticket != null) {
+                exceptionModel.setTicket(ticket);
+            }
+
+        }
+
         themeDiv.add(new ExceptionStackTracePanel(ID_EXCEPTION_STACK_TRACE, exceptionModel));
 
     }