This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/master by this push:
new 2ccc82aad5 ISIS-3012: decouples RO from javax.jdo
2ccc82aad5 is described below
commit 2ccc82aad55202b42ed3685b5b5e79864f557017
Author: Andi Huber <[email protected]>
AuthorDate: Thu Apr 21 10:07:19 2022 +0200
ISIS-3012: decouples RO from javax.jdo
---
.../exceprecog/ExceptionRecognizerAbstract.java | 5 ++-
.../services/exceprecog/RootCauseFinder.java | 18 ++++++--
.../isis/commons/internal/collections/_Lists.java | 8 ++++
.../commons/internal/exceptions/_Exceptions.java | 26 ++++++++---
.../jdo/spring/IsisModulePersistenceJdoSpring.java | 22 ++++++++++
...tsApplicationExceptionMapper_Test_Contract.java | 20 +++++----
viewers/restfulobjects/viewer/pom.xml | 11 -----
.../viewer/mappers/ExceptionMapperAbstract.java | 10 +++--
.../viewer/mappers/entity/ExceptionDetail.java | 50 +++++++++++-----------
9 files changed, 110 insertions(+), 60 deletions(-)
diff --git
a/api/applib/src/main/java/org/apache/isis/applib/services/exceprecog/ExceptionRecognizerAbstract.java
b/api/applib/src/main/java/org/apache/isis/applib/services/exceprecog/ExceptionRecognizerAbstract.java
index 57ad5102d3..4c629c122b 100644
---
a/api/applib/src/main/java/org/apache/isis/applib/services/exceprecog/ExceptionRecognizerAbstract.java
+++
b/api/applib/src/main/java/org/apache/isis/applib/services/exceprecog/ExceptionRecognizerAbstract.java
@@ -123,8 +123,9 @@ public abstract class ExceptionRecognizerAbstract
implements ExceptionRecognizer
return translatableMessage.translate(translationService,
translationContext);
}
}
- final Throwable rootCause = _Exceptions.getRootCause(throwable);
- final String formattedMessage =
rootCauseMessageFormatter.apply(rootCause);
+ final String formattedMessage = _Exceptions.getRootCause(throwable)
+ .map(rootCauseMessageFormatter::apply)
+ .orElse("");
return formattedMessage;
})
.filter(_NullSafe::isPresent)
diff --git
a/persistence/jdo/spring/src/main/java/org/apache/isis/persistence/jdo/spring/IsisModulePersistenceJdoSpring.java
b/api/applib/src/main/java/org/apache/isis/applib/services/exceprecog/RootCauseFinder.java
similarity index 63%
copy from
persistence/jdo/spring/src/main/java/org/apache/isis/persistence/jdo/spring/IsisModulePersistenceJdoSpring.java
copy to
api/applib/src/main/java/org/apache/isis/applib/services/exceprecog/RootCauseFinder.java
index e68ae34762..61f69a0db0 100644
---
a/persistence/jdo/spring/src/main/java/org/apache/isis/persistence/jdo/spring/IsisModulePersistenceJdoSpring.java
+++
b/api/applib/src/main/java/org/apache/isis/applib/services/exceprecog/RootCauseFinder.java
@@ -16,11 +16,21 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.isis.persistence.jdo.spring;
+package org.apache.isis.applib.services.exceprecog;
-import org.springframework.context.annotation.Configuration;
+import java.util.Optional;
+import java.util.function.Function;
-@Configuration
-public class IsisModulePersistenceJdoSpring {
+/**
+ * SPI to help {@link Exception} root cause finding.
+ * <p>
+ * Either returns a more specific {@link Throwable} or an {@link
Optional#empty()}.
+ * @apiNote Specifically introduced to handle JDOException's {@code
Throwable[] nested}.
+ *
+ * @since 2.0 {@index}
+ */
+@FunctionalInterface
+public interface RootCauseFinder
+extends Function<Throwable, Optional<Throwable>> {
}
diff --git
a/commons/src/main/java/org/apache/isis/commons/internal/collections/_Lists.java
b/commons/src/main/java/org/apache/isis/commons/internal/collections/_Lists.java
index 31d5075192..1dec99f051 100644
---
a/commons/src/main/java/org/apache/isis/commons/internal/collections/_Lists.java
+++
b/commons/src/main/java/org/apache/isis/commons/internal/collections/_Lists.java
@@ -24,6 +24,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -62,6 +63,13 @@ public final class _Lists {
return list.get(list.size()-1);
}
+ public static <T> Optional<T> lastElement(final @Nullable List<T> list) {
+ if(_NullSafe.isEmpty(list)) {
+ return Optional.empty();
+ }
+ return Optional.ofNullable(list.get(list.size()-1));
+ }
+
// -- UNMODIFIABLE LIST
/**
diff --git
a/commons/src/main/java/org/apache/isis/commons/internal/exceptions/_Exceptions.java
b/commons/src/main/java/org/apache/isis/commons/internal/exceptions/_Exceptions.java
index 219fce305c..8a00655b0f 100644
---
a/commons/src/main/java/org/apache/isis/commons/internal/exceptions/_Exceptions.java
+++
b/commons/src/main/java/org/apache/isis/commons/internal/exceptions/_Exceptions.java
@@ -24,6 +24,7 @@ import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
+import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -344,8 +345,26 @@ public final class _Exceptions {
return chain.stream();
}
- public static Throwable getRootCause(final @Nullable Throwable ex) {
- return _Lists.lastElementIfAny(getCausalChain(ex));
+ public static Optional<Throwable> getRootCause(final @Nullable Throwable
ex) {
+ return _Lists.lastElement(getCausalChain(ex));
+ }
+
+ /**
+ * Variant to support custom root cause finders.
+ * @param rootCauseFinders -
+ * either returns a more specific {@link Throwable} or an {@link
Optional#empty()}.
+ * @implNote non-recursive
+ */
+ public static Optional<Throwable> getRootCause(
+ final @Nullable Throwable ex,
+ final @Nullable List<? extends Function<Throwable,
Optional<Throwable>>> rootCauseFinders) {
+ return getRootCause(ex)
+ .map(rootCause->_NullSafe.stream(rootCauseFinders)
+ .map(finder->finder.apply(rootCause))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .findFirst()
+ .orElse(rootCause));
}
// -- SWALLOW
@@ -480,7 +499,4 @@ public final class _Exceptions {
}
-
-
-
}
diff --git
a/persistence/jdo/spring/src/main/java/org/apache/isis/persistence/jdo/spring/IsisModulePersistenceJdoSpring.java
b/persistence/jdo/spring/src/main/java/org/apache/isis/persistence/jdo/spring/IsisModulePersistenceJdoSpring.java
index e68ae34762..b7845aa6f6 100644
---
a/persistence/jdo/spring/src/main/java/org/apache/isis/persistence/jdo/spring/IsisModulePersistenceJdoSpring.java
+++
b/persistence/jdo/spring/src/main/java/org/apache/isis/persistence/jdo/spring/IsisModulePersistenceJdoSpring.java
@@ -18,9 +18,31 @@
*/
package org.apache.isis.persistence.jdo.spring;
+import java.util.Optional;
+
+import javax.jdo.JDOException;
+
+import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.apache.isis.applib.services.exceprecog.RootCauseFinder;
+
@Configuration
public class IsisModulePersistenceJdoSpring {
+ @Bean
+ public RootCauseFinder getJdoExceptionRootCauseFinder() {
+ return (final Throwable ex) -> {
+ if (ex instanceof JDOException) {
+ final JDOException jdoException = (JDOException) ex;
+ final Throwable[] nestedExceptions =
jdoException.getNestedExceptions();
+ return nestedExceptions != null
+ && nestedExceptions.length > 0
+ ? Optional.ofNullable(nestedExceptions[0])
+ : Optional.empty();
+ }
+ return Optional.empty();
+ };
+ }
+
}
diff --git
a/viewers/restfulobjects/testing/src/main/java/org/apache/isis/viewer/restfulobjects/testing/RestfulObjectsApplicationExceptionMapper_Test_Contract.java
b/viewers/restfulobjects/testing/src/main/java/org/apache/isis/viewer/restfulobjects/testing/RestfulObjectsApplicationExceptionMapper_Test_Contract.java
index 3ecc924f65..b8b92faafb 100644
---
a/viewers/restfulobjects/testing/src/main/java/org/apache/isis/viewer/restfulobjects/testing/RestfulObjectsApplicationExceptionMapper_Test_Contract.java
+++
b/viewers/restfulobjects/testing/src/main/java/org/apache/isis/viewer/restfulobjects/testing/RestfulObjectsApplicationExceptionMapper_Test_Contract.java
@@ -38,6 +38,8 @@ import
org.apache.isis.viewer.restfulobjects.applib.util.JsonMapper;
import
org.apache.isis.viewer.restfulobjects.rendering.RestfulObjectsApplicationException;
import
org.apache.isis.viewer.restfulobjects.viewer.mappers.ExceptionMapperForRestfulObjectsApplication;
+import lombok.val;
+
/**
* contract test.
*/
@@ -127,9 +129,9 @@ public abstract class
RestfulObjectsApplicationExceptionMapper_Test_Contract {
// given
context.allowing(mockHttpHeaders);
- final Exception rootCause = new Exception("bozfoz");
- final Exception cause = new Exception("barfoo", rootCause);
- final RestfulObjectsApplicationException ex =
RestfulObjectsApplicationException.createWithCauseAndMessage(HttpStatusCode.BAD_REQUEST,
cause, "foobar");
+ val cause = new Exception("barfoo", new
Exception("root-cause-message"));
+ val ex = RestfulObjectsApplicationException
+ .createWithCauseAndMessage(HttpStatusCode.BAD_REQUEST, cause,
"foobar");
// when
final Response response = exceptionMapper.toResponse(ex);
@@ -140,12 +142,12 @@ public abstract class
RestfulObjectsApplicationExceptionMapper_Test_Contract {
// then
assertThat((String) response.getMetadata().get("Warning").get(0),
is("199 RestfulObjects foobar"));
assertThat(jsonRepr.getString("message"), is("foobar"));
- final JsonRepresentation detail = jsonRepr.getRepresentation("detail");
- assertThat(detail, is(not(nullValue())));
- assertThat(detail.getString("message"), is("foobar"));
- final JsonRepresentation causedBy =
detail.getRepresentation("causedBy");
- assertThat(causedBy, is(not(nullValue())));
- assertThat(causedBy.getString("message"), is(cause.getMessage()));
+ val detailJson = jsonRepr.getRepresentation("detail");
+ assertThat(detailJson, is(not(nullValue())));
+ assertThat(detailJson.getString("message"), is("foobar"));
+ val causedByJson = detailJson.getRepresentation("causedBy");
+ assertThat(causedByJson, is(not(nullValue())));
+ assertThat(causedByJson.getString("message"),
is("root-cause-message"));
}
}
diff --git a/viewers/restfulobjects/viewer/pom.xml
b/viewers/restfulobjects/viewer/pom.xml
index c8c1de5a83..5db682ce50 100644
--- a/viewers/restfulobjects/viewer/pom.xml
+++ b/viewers/restfulobjects/viewer/pom.xml
@@ -52,17 +52,6 @@
<artifactId>isis-viewer-restfulobjects-rendering</artifactId>
</dependency>
- <!-- JDO API (non transient, provided by plugin) -->
- <dependency>
- <!-- v3.2 not available yet, use datanucleus staging
for now -->
- <!-- <groupId>javax.jdo</groupId> -->
- <!-- <artifactId>jdo-api</artifactId> -->
- <groupId>org.datanucleus</groupId>
- <artifactId>javax.jdo</artifactId>
- <!-- provided by plugins -->
- <scope>provided</scope>
- </dependency>
-
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
diff --git
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/mappers/ExceptionMapperAbstract.java
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/mappers/ExceptionMapperAbstract.java
index 769fd69663..065e7593f3 100644
---
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/mappers/ExceptionMapperAbstract.java
+++
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/mappers/ExceptionMapperAbstract.java
@@ -28,7 +28,10 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.ext.ExceptionMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+
import org.apache.isis.applib.exceptions.RecoverableException;
+import org.apache.isis.applib.services.exceprecog.RootCauseFinder;
import org.apache.isis.commons.internal.exceptions._Exceptions;
import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation;
import org.apache.isis.viewer.restfulobjects.applib.RepresentationType;
@@ -43,8 +46,8 @@ import static
org.apache.isis.commons.internal.base._NullSafe.stream;
public abstract class ExceptionMapperAbstract<T extends Throwable> implements
ExceptionMapper<T> {
- @Context
- protected HttpHeaders httpHeaders;
+ @Context protected HttpHeaders httpHeaders;
+ @Autowired(required = false) protected List<RootCauseFinder>
rootCauseFinders;
Response buildResponse(final T ex) {
return buildResponse(ex, determineStatusCode(ex));
@@ -132,10 +135,11 @@ public abstract class ExceptionMapperAbstract<T extends
Throwable> implements Ex
private ExceptionDetail detailIfRequired(
final RestfulResponse.HttpStatusCode httpStatusCode,
final Throwable ex) {
+
return httpStatusCode == RestfulResponse.HttpStatusCode.NOT_FOUND ||
httpStatusCode == RestfulResponse.HttpStatusCode.OK
? null
- : new ExceptionDetail(ex);
+ : new ExceptionDetail(ex, rootCauseFinders);
}
private Response buildResponse(
diff --git
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/mappers/entity/ExceptionDetail.java
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/mappers/entity/ExceptionDetail.java
index e51abe7af1..8c0a6abc3e 100644
---
a/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/mappers/entity/ExceptionDetail.java
+++
b/viewers/restfulobjects/viewer/src/main/java/org/apache/isis/viewer/restfulobjects/viewer/mappers/entity/ExceptionDetail.java
@@ -19,8 +19,8 @@
package org.apache.isis.viewer.restfulobjects.viewer.mappers.entity;
import java.util.List;
+import java.util.stream.Stream;
-import javax.jdo.JDOException;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
@@ -28,9 +28,14 @@ import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
+import org.springframework.lang.Nullable;
+
+import org.apache.isis.applib.services.exceprecog.RootCauseFinder;
import org.apache.isis.commons.internal.collections._Lists;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
import lombok.Getter;
+import lombok.NoArgsConstructor;
@XmlRootElement(
name = "exceptionDetail"
@@ -45,12 +50,9 @@ import lombok.Getter;
}
)
@XmlAccessorType(XmlAccessType.FIELD)
+@NoArgsConstructor
public class ExceptionDetail {
- private static String format(final StackTraceElement stackTraceElement) {
- return stackTraceElement.toString();
- }
-
@Getter private String className;
@Getter private String message;
@@ -60,32 +62,28 @@ public class ExceptionDetail {
@Getter private ExceptionDetail causedBy;
- public ExceptionDetail() {
- }
-
- public ExceptionDetail(final Throwable ex) {
+ public ExceptionDetail(
+ final Throwable ex,
+ final @Nullable List<RootCauseFinder> rootCauseFinders) {
this.className = ex.getClass().getName();
this.message = ex.getMessage();
- final StackTraceElement[] stackTraceElements = ex.getStackTrace();
- for (final StackTraceElement stackTraceElement : stackTraceElements) {
- this.stackTrace.add(format(stackTraceElement));
- }
- final Throwable cause = causeOf(ex);
- if (cause != null && cause != ex) {
- this.causedBy = new ExceptionDetail(cause);
- }
+ Stream.of(ex.getStackTrace())
+ .map(ExceptionDetail::format)
+ .forEach(stackTrace::add);
+
+ this.causedBy = _Exceptions.getRootCause(ex, rootCauseFinders)
+ .filter(cause->cause!=ex)
+ .map(cause->new ExceptionDetail(cause, rootCauseFinders))
+ .orElse(null);
}
- private static Throwable causeOf(Throwable ex) {
- if (ex instanceof JDOException) {
- final JDOException jdoException = (JDOException) ex;
- final Throwable[] nestedExceptions =
jdoException.getNestedExceptions();
- return nestedExceptions != null && nestedExceptions.length > 0?
nestedExceptions[0]: null;
- }
- else {
- return ex.getCause();
- }
+ // -- HELPER
+
+ private static String format(final StackTraceElement stackTraceElement) {
+ return stackTraceElement.toString();
}
+
+
}