Skip to content

Instantly share code, notes, and snippets.

@jroper
Last active September 25, 2017 21:12
Show Gist options
  • Save jroper/c1ab6a9842ee489f55de to your computer and use it in GitHub Desktop.
Save jroper/c1ab6a9842ee489f55de to your computer and use it in GitHub Desktop.
Taking global static variables to a new level on the JVM...
/**
* Provides a global (cross classloader) static var.
*
* Where might you use this? Anywhere where you want to be evil ;)
*
* But, my use case was in a dynamic classloading environment where you want to load a library that
* depends on a native library. Only one classloader can ever load and link the classes associated
* with the native library, so if a second classloader (for example if a dynamic reload was done)
* wanted to use it to, it couldn't. This provided a cross classloader mechanism for storing the
* classloader that loaded the native library.
*
* Of course, it's not thread safe.
*
* This does not leak classloaders (unless the value passed to it references a classloader that
* shouldn't be leaked). It uses an MBeanServer to store an AtomicReference as an mbean, exposing
* the get method of the AtomicReference as an mbean operation, so that the value can be retrieved.
*/
object GlobalStaticVar {
import javax.management._
import javax.management.modelmbean._
import java.lang.management._
import java.util.concurrent.atomic.AtomicReference
import scala.reflect.ClassTag
private def objectName(name: String) = {
new ObjectName(":type=GlobalStaticVar,name=" + name)
}
/**
* Set a global static variable with the given name.
*/
def set(name: String, value: AnyRef): Unit = {
val reference = new AtomicReference[AnyRef](value)
// Now we construct a MBean that exposes the AtomicReference.get method
val getMethod = classOf[AtomicReference[_]].getMethod("get")
val getInfo = new ModelMBeanOperationInfo("The value", getMethod)
val mmbi = new ModelMBeanInfoSupport("GlobalStaticVar",
"A global static variable",
null, // no attributes
null, // no constructors
Array(getInfo), // the operation
null); // no notifications
val mmb = new RequiredModelMBean(mmbi)
mmb.setManagedResource(reference, "ObjectReference")
// Register the Model MBean in the MBean Server
ManagementFactory.getPlatformMBeanServer.registerMBean(mmb, objectName(name))
}
/**
* Get a global static variable by the given name.
*/
def get[T](name: String)(implicit ct: ClassTag[T]): Option[T] = {
try {
val value = ManagementFactory.getPlatformMBeanServer.invoke(objectName(name), "get", Array.empty, Array.empty)
if (ct.runtimeClass.isInstance(value)) {
Some(value.asInstanceOf[T])
} else {
throw new ClassCastException(s"Global static var $name is not an instance of ${ct.runtimeClass}, but is actually a ${Option(value).fold("null")(_.getClass.getName)}")
}
} catch {
case e: InstanceNotFoundException =>
None
}
}
/**
* Clear a global static variable with the given name.
*/
def remove(name: String): Unit = {
try {
ManagementFactory.getPlatformMBeanServer.unregisterMBean(objectName(name))
} catch {
case e: InstanceNotFoundException =>
}
}
}
@jroper
Copy link
Author

jroper commented Jul 30, 2014

@hho Yes, you shouldn't put non Strings into Properties. But you also shouldn't be using global variables. Two wrongs make a right!

@nayanSingh
Copy link

@jroper good

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment