Last active
September 25, 2017 21:12
-
-
Save jroper/c1ab6a9842ee489f55de to your computer and use it in GitHub Desktop.
Taking global static variables to a new level on the JVM...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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 => | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@hho Yes, you shouldn't put non Strings into Properties. But you also shouldn't be using global variables. Two wrongs make a right!