Reputation: 27727
When a third party dependency attempts to load a class defined in a Play application using
Class.forName(className, true, Thread.currentThread().getContextClassLoader());
Play will throw a ClassNotFoundException
because the context class loader is of type PlayDependencyClassLoader
which apparently only contains classes defined in jar dependencies.
Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: eventstore.Commit
at org.mapdb.SerializerPojo.classForName(SerializerPojo.java:96)
at org.mapdb.SerializerPojo$1.deserialize(SerializerPojo.java:74)
at org.mapdb.SerializerPojo$1.deserialize(SerializerPojo.java:39)
This only occurs when Play is started with play run
. Starting Play with play start
loads the class correctly.
It would be a shame to sacrifice the class hot-swapping because of this behavior. Is there a known workaround?
Upvotes: 2
Views: 817
Reputation: 3251
Play's HttpExecutionContext
can also be used to propagate the ClassLoader
across threads. The HttpExecutionContext
also propagates the Http.Context
thread local, if one is set.
See my answer here: How to use Http.Context.current() in a Promise in Play?
See also Play issue #2847 – classloader issues when using "run".
Upvotes: 1
Reputation: 27727
A hacky type fix for now is to wrap invocations of third party libraries that use Class.forName
with a function like this:
// see Play classloader bug https://github.com/playframework/playframework/issues/822
def fixClassLoader[E](f: () => E) = {
val old = Thread.currentThread().getContextClassLoader()
try {
Thread.currentThread().setContextClassLoader(Play.application.classloader)
f()
} finally {
Thread.currentThread().setContextClassLoader(old)
}
}
Upvotes: 0