Skip to content

Commit

Permalink
Make exception handler work for saga's.
Browse files Browse the repository at this point in the history
  • Loading branch information
gklijs committed Mar 22, 2023
1 parent ea76923 commit 33fe1b8
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 21 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2018. Axon Framework
* Copyright (c) 2010-2023. Axon Framework
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -118,7 +118,7 @@ public final Object handle(EventMessage<?> event) {

private Object handle(MessageHandlingMember<? super T> handler, EventMessage<?> event) {
try {
return executeWithResult(() -> handler.handle(event, sagaInstance));
return executeWithResult(() -> metaModel.chainedInterceptor().handle(event, sagaInstance, handler));
} catch (RuntimeException | Error e) {
throw e;
} catch (Exception e) {
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2018. Axon Framework
* Copyright (c) 2010-2023. Axon Framework
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,14 +17,15 @@
package org.axonframework.modelling.saga.metamodel;

import org.axonframework.eventhandling.EventMessage;
import org.axonframework.modelling.saga.AssociationValue;
import org.axonframework.modelling.saga.SagaMethodMessageHandlingMember;
import org.axonframework.messaging.annotation.AnnotatedHandlerInspector;
import org.axonframework.messaging.annotation.ClasspathHandlerDefinition;
import org.axonframework.messaging.annotation.ClasspathParameterResolverFactory;
import org.axonframework.messaging.annotation.HandlerDefinition;
import org.axonframework.messaging.annotation.MessageHandlerInterceptorMemberChain;
import org.axonframework.messaging.annotation.MessageHandlingMember;
import org.axonframework.messaging.annotation.ParameterResolverFactory;
import org.axonframework.modelling.saga.AssociationValue;
import org.axonframework.modelling.saga.SagaMethodMessageHandlingMember;

import java.util.List;
import java.util.Map;
Expand All @@ -43,16 +44,16 @@ public class AnnotationSagaMetaModelFactory implements SagaMetaModelFactory {
private final HandlerDefinition handlerDefinition;

/**
* Initializes a {@link AnnotationSagaMetaModelFactory} with {@link ClasspathParameterResolverFactory} and {@link
* ClasspathHandlerDefinition}.
* Initializes a {@link AnnotationSagaMetaModelFactory} with {@link ClasspathParameterResolverFactory} and
* {@link ClasspathHandlerDefinition}.
*/
public AnnotationSagaMetaModelFactory() {
this(ClasspathParameterResolverFactory.forClassLoader(Thread.currentThread().getContextClassLoader()));
}

/**
* Initializes a {@link AnnotationSagaMetaModelFactory} with given {@code parameterResolverFactory} and {@link
* ClasspathHandlerDefinition}.
* Initializes a {@link AnnotationSagaMetaModelFactory} with given {@code parameterResolverFactory} and
* {@link ClasspathHandlerDefinition}.
*
* @param parameterResolverFactory factory for event handler parameter resolvers
*/
Expand All @@ -62,8 +63,8 @@ public AnnotationSagaMetaModelFactory(ParameterResolverFactory parameterResolver
}

/**
* Initializes a {@link AnnotationSagaMetaModelFactory} with given {@code parameterResolverFactory} and given {@code
* handlerDefinition}.
* Initializes a {@link AnnotationSagaMetaModelFactory} with given {@code parameterResolverFactory} and given
* {@code handlerDefinition}.
*
* @param parameterResolverFactory factory for event handler parameter resolvers
* @param handlerDefinition the handler definition used to create concrete handlers
Expand All @@ -86,15 +87,23 @@ private <T> SagaModel<T> doCreateModel(Class<T> sagaType) {
parameterResolverFactory,
handlerDefinition);

return new InspectedSagaModel<>(handlerInspector.getHandlers());
return new InspectedSagaModel<>(
handlerInspector.getHandlers(sagaType).collect(Collectors.toList()),
handlerInspector.chainedInterceptor(sagaType)
);
}

private class InspectedSagaModel<T> implements SagaModel<T> {

private final List<MessageHandlingMember<? super T>> handlers;
private final MessageHandlerInterceptorMemberChain<T> interceptorMemberChain;

public InspectedSagaModel(List<MessageHandlingMember<? super T>> handlers) {
public InspectedSagaModel(
List<MessageHandlingMember<? super T>> handlers,
MessageHandlerInterceptorMemberChain<T> interceptorMemberChain
) {
this.handlers = handlers;
this.interceptorMemberChain = interceptorMemberChain;
}

@Override
Expand Down Expand Up @@ -125,6 +134,11 @@ public boolean hasHandlerMethod(EventMessage<?> eventMessage) {
return false;
}

@Override
public MessageHandlerInterceptorMemberChain<T> chainedInterceptor() {
return interceptorMemberChain;
}

@Override
public SagaMetaModelFactory modelFactory() {
return AnnotationSagaMetaModelFactory.this;
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2018. Axon Framework
* Copyright (c) 2010-2023. Axon Framework
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,11 +17,14 @@
package org.axonframework.modelling.saga.metamodel;

import org.axonframework.eventhandling.EventMessage;
import org.axonframework.modelling.saga.AssociationValue;
import org.axonframework.messaging.Message;
import org.axonframework.messaging.annotation.MessageHandlerInterceptorMemberChain;
import org.axonframework.messaging.annotation.MessageHandlingMember;
import org.axonframework.modelling.saga.AssociationValue;

import java.util.List;
import java.util.Optional;
import javax.annotation.Nonnull;

/**
* Interface of a model that describes a Saga of type {@code T}. Use the SagaModel to obtain associations and
Expand Down Expand Up @@ -58,6 +61,29 @@ default boolean hasHandlerMethod(EventMessage<?> eventMessage) {
return !findHandlerMethods(eventMessage).isEmpty();
}

/**
* Returns an Interceptor Chain of annotated interceptor methods defined on the given {@code type}. The given chain
* will invoke all relevant interceptors in an order defined by the handler definition.
*
* @return an interceptor chain that invokes the interceptor handlers
*/
default MessageHandlerInterceptorMemberChain<T> chainedInterceptor() {
return NoMoreInterceptors.instance();
}

class NoMoreInterceptors<T> implements MessageHandlerInterceptorMemberChain<T> {

static <T> MessageHandlerInterceptorMemberChain<T> instance() {
return new NoMoreInterceptors<>();
}

@Override
public Object handle(@Nonnull Message<?> message, @Nonnull T target,
@Nonnull MessageHandlingMember<? super T> handler) throws Exception {
return handler.handle(message, target);
}
}

/**
* Returns the factory that created this model.
*
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2018. Axon Framework
* Copyright (c) 2010-2023. Axon Framework
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,20 +16,26 @@

package org.axonframework.modelling.saga.metamodel;

import org.axonframework.common.AxonException;
import org.axonframework.eventhandling.EventMessage;
import org.axonframework.messaging.annotation.MessageHandlingMember;
import org.axonframework.messaging.interceptors.ExceptionHandler;
import org.axonframework.modelling.saga.AssociationValue;
import org.axonframework.modelling.saga.SagaEventHandler;
import org.axonframework.modelling.saga.StartSaga;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.invoke.MethodHandles;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.axonframework.eventhandling.GenericEventMessage.asEventMessage;
import static org.junit.jupiter.api.Assertions.*;

class AnnotationSagaMetaModelFactoryTest {

private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private AnnotationSagaMetaModelFactory testSubject;

@BeforeEach
Expand All @@ -47,12 +53,39 @@ void inspectSaga() {
assertEquals("property", actual.get().getKey());
}

@Test
void chainedInterceptorShouldDefaultToNoMoreInterceptors() {
SagaModel<MySaga> sagaModel = testSubject.modelOf(MySaga.class);

MySaga saga = new MySaga();
EventMessage<MySagaStartEvent> event = asEventMessage(new MySagaStartEvent("foo"));
Optional<MessageHandlingMember<? super MySaga>> handler = sagaModel
.findHandlerMethods(event).stream().findFirst();
assertTrue(handler.isPresent());
assertThrows(FooException.class, () -> sagaModel.chainedInterceptor().handle(event, saga, handler.get()));
}

@Test
void exceptionShouldBeCaughtByExceptionHandler() throws Exception {
SagaModel<MySagaWithErrorHandler> sagaModel = testSubject.modelOf(MySagaWithErrorHandler.class);

MySagaWithErrorHandler saga = new MySagaWithErrorHandler();
EventMessage<MySagaStartEvent> event = asEventMessage(new MySagaStartEvent("foo"));
Optional<MessageHandlingMember<? super MySagaWithErrorHandler>> handler = sagaModel
.findHandlerMethods(event).stream().findFirst();
assertTrue(handler.isPresent());
Object result = sagaModel.chainedInterceptor().handle(event, saga, handler.get());
assertNull(result);
}

public static class MySaga {

@StartSaga
@SagaEventHandler(associationProperty = "property")
public void handle(MySagaStartEvent event) {

if ("foo".equals(event.getProperty())) {
throw new FooException("value was foo");
}
}

@SagaEventHandler(associationProperty = "property")
Expand All @@ -66,6 +99,14 @@ public void handle(MySagaEndEvent event) {
}
}

public static class MySagaWithErrorHandler extends MySaga {

@ExceptionHandler
public void on(Exception e) {
logger.info("caught", e);
}
}

public abstract static class MySagaEvent {

private final String property;
Expand All @@ -80,20 +121,32 @@ public String getProperty() {
}

private static class MySagaStartEvent extends MySagaEvent {

public MySagaStartEvent(String property) {
super(property);
}
}

private static class MySagaUpdateEvent extends MySagaEvent {

public MySagaUpdateEvent(String property) {
super(property);
}
}

private static class MySagaEndEvent extends MySagaEvent {

public MySagaEndEvent(String property) {
super(property);
}
}

private static class FooException extends AxonException {

private static final long serialVersionUID = 6212176261668474654L;

public FooException(String message) {
super(message);
}
}
}

0 comments on commit 33fe1b8

Please sign in to comment.