Issues with multiple sagas that are associated with the same values

On our projects we have started using the saga functionality which
meets our requirements perfectly. However we have come across some
issues when using the AnnotatedSagaManager with the JpaSagaRepository.
We have 3 sagas that handle the same event and are all associated with
the same value.

1) The first problem is this exception

java.lang.ClassCastException
  at java.lang.Class.cast(Class.java:2990)
  at
org.axonframework.saga.repository.jpa.JpaSagaRepository.loadSaga(JpaSagaRepository.java:
91)
  at
org.axonframework.saga.repository.AbstractSagaRepository.load(AbstractSagaRepository.java:
65)
  at
org.axonframework.saga.repository.AbstractSagaRepository.find(AbstractSagaRepository.java:
53)
  at
org.axonframework.saga.annotation.AnnotatedSagaManager.findSagas(AnnotatedSagaManager.java:
116)
  at
org.axonframework.saga.annotation.AnnotatedSagaManager.findSagas(AnnotatedSagaManager.java:
104)
  at org.axonframework.saga.AbstractSagaManager
$SagaLookupAndInvocationTask.run(AbstractSagaManager.java:227)
  at org.axonframework.saga.AsynchronousSagaExecutor
$Task.execute(AsynchronousSagaExecutor.java:94)
  at
org.axonframework.saga.AsynchronousSagaExecutor.doHandle(AsynchronousSagaExecutor.java:
61)
  at
org.axonframework.saga.AsynchronousSagaExecutor.doHandle(AsynchronousSagaExecutor.java:
32)
  at org.axonframework.eventhandling.AsynchronousExecutionWrapper
$1.doHandle(AsynchronousExecutionWrapper.java:149)
  at
org.axonframework.eventhandling.EventProcessingScheduler.handleEventBatch(EventProcessingScheduler.java:
317)
  at
org.axonframework.eventhandling.EventProcessingScheduler.processOrRetryBatch(EventProcessingScheduler.java:
239)
  at
org.axonframework.eventhandling.EventProcessingScheduler.run(EventProcessingScheduler.java:
200)
  at java.util.concurrent.ThreadPoolExecutor
$Worker.runTask(ThreadPoolExecutor.java:886)
  at java.util.concurrent.ThreadPoolExecutor
$Worker.run(ThreadPoolExecutor.java:908)
  at java.lang.Thread.run(Thread.java:619)

When the AnnotatedSagaManager loops through the managed saga it
attempts to load a saga as the wrong type. This seems to be because
the associatedValue lookup matches more than one saga

2) The second problem is that the AbstractSagaRepository#find method
can return null sagas in the returned set. The following exception is
then thrown trying the execute the null saga

java.lang.NullPointerException
  at org.axonframework.saga.AbstractSagaManager
$SagaLookupAndInvocationTask.run(AbstractSagaManager.java:229)
  at org.axonframework.saga.AsynchronousSagaExecutor
$Task.execute(AsynchronousSagaExecutor.java:94)
  at
org.axonframework.saga.AsynchronousSagaExecutor.doHandle(AsynchronousSagaExecutor.java:
61)
  at
org.axonframework.saga.AsynchronousSagaExecutor.doHandle(AsynchronousSagaExecutor.java:
32)
  at org.axonframework.eventhandling.AsynchronousExecutionWrapper
$1.doHandle(AsynchronousExecutionWrapper.java:149)
  at
org.axonframework.eventhandling.EventProcessingScheduler.handleEventBatch(EventProcessingScheduler.java:
317)
  at
org.axonframework.eventhandling.EventProcessingScheduler.processOrRetryBatch(EventProcessingScheduler.java:
239)
  at
org.axonframework.eventhandling.EventProcessingScheduler.run(EventProcessingScheduler.java:
200)
  at java.util.concurrent.ThreadPoolExecutor
$Worker.runTask(ThreadPoolExecutor.java:886)
  at java.util.concurrent.ThreadPoolExecutor
$Worker.run(ThreadPoolExecutor.java:908)
  at java.lang.Thread.run(Thread.java:619)

Fix:

To fix the problem temporarily we have created a subclass of the
JpaSagaRepository that handles the ClassCastException and does not add
null values to the set of saga. Here is the code

@Override
    protected <T extends Saga> T loadSaga(Class<T> type, String
sagaId) {
        try {
            return super.loadSaga(type, sagaId);
        } catch (ClassCastException cce) {
            return null;
        }
    }

    @Override
    public <T extends Saga> Set<T> find(Class<T> type,
Set<AssociationValue> associationValues) {
        Set<String> sagaIdentifiers = new HashSet<String>();
        Set<T> result = new HashSet<T>();
        for (AssociationValue associationValue : associationValues) {
            Set<String> identifiers =
getAssociationValueMap().findSagas(associationValue);
            if (identifiers != null) {
                sagaIdentifiers.addAll(identifiers);
            }
        }
        if (!sagaIdentifiers.isEmpty()) {
            for (String sagaId : sagaIdentifiers) {
                T cachedSaga = load(type, sagaId);
                if(cachedSaga != null) {
                    result.add(cachedSaga);
                }
            }
        }
        return result;
    }

Hi,

thanks for reporting this. I managed to reproduce and fix both problem. They are closely related. Your workaround looks fine, given the fact that there is probably no better option than catching the exceptions, for now.
I’ll include the fix in 1.1.

Cheers,

Allard

Thanks for the fast response. I’ll keep an eye on the issue tracker

The issue is gone from the tracker already :wink:
Here it is: http://code.google.com/p/axonframework/issues/detail?id=148

Cheers,

Allard