Axon, JPA event sourced repositories, events replay and Play 2.1 reload problem

Hi,

First of all - thanks for Axon. This is a great piece of code. :slight_smile:

Now the problem:

I am trying to use Axon 2.1 inside Play 2.1 framework with JPA event sourced repositories.
At the application startup I am replaying all events to the in-memory view model.

Everything works smoothly until I change anything while Play is running.

Play then recompiles and reloads app (and spring context) and after that reload, my events are not replayed…

What is more, any new event applied from any aggregate also is not delivered (even to that aggregate’s @EventHandler annotated methods).

I can only guess that this is due to some Unit of Work problems with transactions in JPA behind the scenes after the reload.

Does anyone have any idea where to dig?

Greets,
Peter

Hi Peter,

How are you starting up your Spring ApplicationContext? If you’re using the lifecycle in GlobalSettings then you should ensure that you are closing the context through the onStop method.

Seamus

Hi Seamus,

Thanks fro your replay.

Yes, I am closing context.

Below you can see my Global.scala:

object Global extends GlobalSettings {
var ctx: AnnotationConfigApplicationContext = _

override def onStop(app: Application) = {
ctx.stop

}

override def onStart(app: Application) = {
ctx = new AnnotationConfigApplicationContext(classOf[AppConfig])
ctx.getBean(classOf[ReplayingCluster]).startReplay
}

override def getControllerInstance[A](clazz: Class[A]) = ctx.getBean(clazz)
}

Hi again,

I did some debuging and found that there is nothing wrong with Unit of Work nor transactions.

Simply, Play after reloading application, loads classes in new classloader (as far as I understand it).

And during replay at restart AbstractMessageHandler#matches(Message message) checks if event can be handled by this handler and invokes Class#isAssignableFrom(Class clazz) in line 65, which returns false, even though this is the same class (but loaded with different classloaders).

Still don’t know why handler’s payload type class is loaded in different classloader than event’s…

Any further ideas?

Greets,
Peter

And hi again :wink:

I found out, that MethodMessageHandlerInspector#INSPECTORS is responsible for all my problems.
It caches MethodMessageHandlerInspector with my event class reference, and after reload, this MethodMessageHandlerInspector class is not beeing reloaded and still contains inspector with previously loaded event class, hence I have two classes of my event.

Any ideas how to clear that INSPECTORS map?

PS.
I think that this may be an issue also when running Axon in OSGi environment, or any other that plays with classloaders.

And the hacky solution:

override def onStop(app: Application) = {
val declaredField = classOf[MethodMessageHandlerInspector].getDeclaredField(“INSPECTORS”)
declaredField.setAccessible(true)
declaredField.get().asInstanceOf[ConcurrentMap[Any, Any]].clear()
ctx.stop()
ctx.destroy()
}

It’s good enough for development, but it is ugly and hacky…

If anyone has any better solution, please share. :slight_smile:

Greets,
Peter

Hi Piotr,

thanks for the pointer. It seems that the caching of the MethodMessageHandlerInspector is a bit too agressive. It uses just the class name to cache the inspectors. Using the actual class instance instead will probably solve the problem.
Meanwhile, I am looking around for a way to cache the entries in such a way, that entries for classes that are no longer in use can be removed.

Cheers,

Allard

Hi Allard,

Using actual class should solve the problem. :slight_smile:
Will this be included in any further release?

Greets,
Peter

It will be in the next release. Not sure if that will be 2.3 or 2.2.1.

Cheers,

Allard

Cool,

Thank you very much. :slight_smile:

Greets,
Peter