Replay Issues

Java: 1.7.0_25 (Linux x64)
Axon: 2.0.3 / 2.1-SNAPSHOT @ e3dcd0739a4
Spring: 3.2.1.RELEASE
Spring-Data: 1.3.0.RELEASE
Hibernate: 4.1.9.Final

UnitEntry.java:
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = “MASKS”)
@Sort(type = SortType.NATURAL)
private SortedSet masks;

protected UnitEntry() {
this.masks = newTreeSet();
}

public void addMask(final String mask) {
this.billToCentreMasks.add(mask);
}

UnitEntryListener.java:
@EventHandler
public void addMask(MaskAddedEvent event) {
repository.save(onUnit(event.getUnitId()).addMask(event.getMask()));
}

private UnitEntry onUnit(UnitId unitId) {
return repository.findOne(unitId.toString());
}

When I use a command to add a new mask to the Unit the MaskAddedEvent is applied and the above UnitEntryListener is executed and successfully calls the UnitEntry.addMask method. Even with a freshly created UnitEntry and I were to break at the UserEntry.addMask method then I would see that the masks class attribute would be a Hibernate PersistentSortedSet.

<axon:cluster id=“unitsReplayingCluster”>
<axon:replay-config event-store=“eventStore” transaction-manager=“transactionManager”/>
axon:selectors
<axon:package prefix=“com.sitelist.units.listeners”/>
</axon:selectors>
</axon:cluster>

However when I replay the cluster for this listener I receive a NullPointerException on the UserEntry.addMask method. I have tried wrapping the call to startReplay() in a Spring transaction, I’ve added @Transactional to all methods in the listener, set the axon:replay-config commit-threshold to 1. I’m at a loss as to why replaying doesn’t set the UnitEntity.masks to a PersistentSortedSet when running it from a command will.

Any assistance would be greatly appreciated.

Randy.

Hi Randy,

given you’re doing a replay, it’s likely that there are no ‘masks’ to start with. I don’t know how Hibernate behaves when the are no entries to put in the set.
What I do see in the documentation, is that they recommend initializing collections in the class itself. Hibernate will (when loading instances) replace the default implementation with a Hibernate specific one (when needed).
Your code does contain initializing logic for the masks field. That makes this a very strange issue. Axon’s replay logic already creates the necessary transactions for you, so you don’t need to worry about that. In fact, it can be in your way if you wrap the logic in extra transactions (since the actual commit may be postponed for longer than you’d want it to).
Perhaps you can use a field breakpoint to find out when the field’s value is set to null?

Cheers,

Allard

Hi Allard,

I tried as you suggested but the breakpoint was never triggered.

I am initalising the attribute in the constructor so I put my breakpoint into the default constructor and I discovered strange behaviour again. I cleaned the database to start from scratch.

The first test is to capture when a command to add a mask has been dispatched:

  • breakpoint added to UnitEntry constructor and addMask method
  • dispatch command to add mask
  • IntelliJ breaks on the constructor and the attribute is initialised; pressed F9 to continue running
  • IntelliJ breaks on the addMask method and the attribute is now a PersistedSortedSet
  • the addMask method works as desired and is properly persisted

The second test was to run the replay:

  • breakpoint added to the UnitEntry constructor and addMask method
  • execute the startReplay on the cluster
  • IntelliJ did NOT break in the constructor
  • IntelliJ breaks on the addMask method and the attribute is null

Dispatching a command results in the execution of the default constructor of UnitEntry yet it is not called when replaying. There are no other event listeners in the application that update the UnitEntry.

My only work around now is to separate the masks from UnitEntry by creating another JPA entity and Spring-Data repository. The listener has been updated to use this new repository and I have updated the beforeReplay method to delete all entities from this table and flush.

Randy.

Hi Randy,

I’ve built an integration test to see what happens, but I cannot reproduce the problem. Does your UnitEntry class have any other constructors? How do you initialize the UnitEntry on the very first event?

Cheers,

Allard

Hi Allard,

There is another constructor that takes unchangeable identifiers. However the first line in that constructor is to call this(). I did not mention earlier that I even tried setting the class attributes to “newTreeSet()” in the definition, ie: “private SortedSet masks = newTreeSet();”.

I have never had this problem with Hibernate and it is odd that it only happens during replay and not during command execution.

I have committed my code changes using separate entities. Spring-Data repositories are cheap.

Thanks for your assistance.

Randy.