Skip to content

Instantly share code, notes, and snippets.

@mkurz
Forked from shkhln/DevModeWorkarounds.scala
Created February 2, 2021 00:11
Show Gist options
  • Save mkurz/c8f20487242209e72c2cf47c8ed2c57c to your computer and use it in GitHub Desktop.
Save mkurz/c8f20487242209e72c2cf47c8ed2c57c to your computer and use it in GitHub Desktop.
import javax.inject.Inject
import scala.concurrent.Future
import play.api.{Configuration, Environment, Mode}
import play.api.inject.{ApplicationLifecycle, Binding, Module}
import play.api.libs.Files.DefaultTemporaryFileCreator
class DevModeWorkaroundsModule extends Module {
def bindings(environment: Environment, configuration: Configuration): Seq[Binding[_]] = {
Seq(bind[DevModeWorkarounds].toSelf.eagerly())
}
}
import java.lang.reflect.Field
import java.lang.reflect.Modifier
class DevModeWorkarounds @Inject()(env: Environment, lifecycle: ApplicationLifecycle, fileCreator: DefaultTemporaryFileCreator) {
def newInstance[T](_class: Class[T]): T = {
val ctr = _class.getDeclaredConstructor()
ctr.setAccessible(true)
ctr.newInstance()
}
def getDeclaredField(_class: Class[_], name: String): Field = {
try {
_class.getDeclaredField(name)
} catch {
case ex: NoSuchFieldException =>
if (_class.getSuperclass != null) {
getDeclaredField(_class.getSuperclass, name)
} else {
throw ex
}
}
}
def getStaticFieldValue[R](_class: Class[_], name: String): R = {
val f = getDeclaredField(_class, name)
f.setAccessible(true)
f.get(null).asInstanceOf[R]
}
def getFieldValue[R](instance: AnyRef, name: String): R = {
val f = getDeclaredField(instance.getClass, name)
f.setAccessible(true)
f.get(instance).asInstanceOf[R]
}
val modifiers = classOf[Field].getDeclaredField("modifiers")
modifiers.setAccessible(true)
def setStaticFieldValue(_class: Class[_], name: String, value: Any): Unit = {
val f = getDeclaredField(_class, name)
if (Modifier.isFinal(f.getModifiers)) {
modifiers.setInt(f, f.getModifiers() & ~Modifier.FINAL)
}
f.setAccessible(true)
f.set(null, value)
}
def setFieldValue(instance: AnyRef, name: String, value: Any): Unit = {
val f = getDeclaredField(instance.getClass, name)
if (Modifier.isFinal(f.getModifiers)) {
modifiers.setInt(f, f.getModifiers() & ~Modifier.FINAL)
}
f.setAccessible(true)
f.set(instance, value)
}
if (env.mode == Mode.Dev) {
// Guava
{
import com.google.common.base.FinalizableReferenceQueue
lifecycle.addStopHook { () =>
val frq = getFieldValue[FinalizableReferenceQueue](fileCreator, "play$api$libs$Files$DefaultTemporaryFileCreator$$frq")
frq.close()
Future.successful(())
}
}
// Jackson
{
import com.fasterxml.jackson.databind.`type`.TypeFactory
lifecycle.addStopHook { () =>
TypeFactory.defaultInstance().clearCache()
Future.successful(())
}
}
// Logback
{
import scala.collection.JavaConverters._
import ch.qos.logback.classic.{AsyncAppender, Logger, LoggerContext}
val ctx = org.slf4j.LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext]
for (logger <- ctx.getLoggerList.asScala) {
val it = logger.asInstanceOf[Logger].iteratorForAppenders
while (it.hasNext) {
val appender = it.next
if (appender.isInstanceOf[AsyncAppender]) {
getFieldValue[Thread](appender, "worker").setContextClassLoader(null)
}
}
}
}
// Netty
{
setStaticFieldValue(classOf[org.jboss.netty.channel.DefaultChannelFuture], "CANCELLED", new Throwable())
}
// Netty again
{
import java.util.concurrent.CancellationException
import play.shaded.ahc.io.netty.util.concurrent.{DefaultPromise, GlobalEventExecutor, ImmediateEventExecutor}
import play.shaded.ahc.io.netty.util.internal.ThrowableUtil
setStaticFieldValue(classOf[GlobalEventExecutor], "INSTANCE", newInstance(classOf[GlobalEventExecutor]))
setStaticFieldValue(classOf[ImmediateEventExecutor], "INSTANCE", newInstance(classOf[ImmediateEventExecutor]))
val causeHolder = getStaticFieldValue[Object](classOf[DefaultPromise[_]], "CANCELLATION_CAUSE_HOLDER")
val throwable = ThrowableUtil.unknownStackTrace(
new CancellationException(), classOf[DefaultPromise[_]], "cancel(...)")
setFieldValue(causeHolder, "cause", throwable)
}
// Ebean
{
import java.util.concurrent.ConcurrentMap
import io.ebeaninternal.server.deploy.parse.AnnotationBase
import io.ebeaninternal.server.lib.ShutdownManager
val shutdownHook = getStaticFieldValue[Thread](classOf[ShutdownManager], "shutdownHook")
shutdownHook.setContextClassLoader(null)
lifecycle.addStopHook { () =>
System.setProperty("ebean.datasource.deregisterAllDrivers", "true")
io.ebean.EbeanServerFactory.shutdown()
System.clearProperty("ebean.datasource.deregisterAllDrivers")
val valueMethods = getStaticFieldValue[ConcurrentMap[_, _]](classOf[AnnotationBase], "valueMethods")
valueMethods.clear()
Future.successful(())
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment