diff options
author | Diego <diegolparra@gmail.com> | 2016-02-07 23:54:43 -0300 |
---|---|---|
committer | Diego <diegolparra@gmail.com> | 2016-02-07 23:54:43 -0300 |
commit | 783464d24f8e5a1877fc893eb991d0842c8128d7 (patch) | |
tree | c8133f25f2cb76e31c350517066c5bf339c2e740 /kamon-autoweave/src/main/scala/kamon/autoweave | |
parent | c90bb9467628f841040003d55f20969abd79958f (diff) | |
download | Kamon-783464d24f8e5a1877fc893eb991d0842c8128d7.tar.gz Kamon-783464d24f8e5a1877fc893eb991d0842c8128d7.tar.bz2 Kamon-783464d24f8e5a1877fc893eb991d0842c8128d7.zip |
= kamon-autoweave: refactor in order to load the com.sun.tools.attach.VirtualMachine from tools.jar
Diffstat (limited to 'kamon-autoweave/src/main/scala/kamon/autoweave')
-rw-r--r-- | kamon-autoweave/src/main/scala/kamon/autoweave/loader/AgentLoader.scala | 136 | ||||
-rw-r--r-- | kamon-autoweave/src/main/scala/kamon/autoweave/loader/AttachmentProviders.scala | 69 |
2 files changed, 91 insertions, 114 deletions
diff --git a/kamon-autoweave/src/main/scala/kamon/autoweave/loader/AgentLoader.scala b/kamon-autoweave/src/main/scala/kamon/autoweave/loader/AgentLoader.scala index eb0c46df..2f26a39d 100644 --- a/kamon-autoweave/src/main/scala/kamon/autoweave/loader/AgentLoader.scala +++ b/kamon-autoweave/src/main/scala/kamon/autoweave/loader/AgentLoader.scala @@ -15,20 +15,12 @@ package kamon.autoweave.loader -import java.io.{File, FileOutputStream, InputStream} +import java.io.{ File, FileOutputStream, InputStream } import java.lang.management.ManagementFactory -import java.lang.reflect.Constructor -import java.util import java.util.jar.Attributes.Name -import java.util.jar.{JarEntry, JarOutputStream, Manifest} - -import com.sun.tools.attach.spi.AttachProvider -import com.sun.tools.attach.{VirtualMachine, VirtualMachineDescriptor} -import kamon.autoweave.loader.resolver.OsResolver._ -import sun.tools.attach._ +import java.util.jar.{ JarEntry, JarOutputStream, Manifest } import scala.util.control.NoStackTrace -import scala.util.{Failure, Success, Try} object AgentLoader { @@ -47,14 +39,10 @@ object AgentLoader { /** * Loads an agent into a JVM. * - * @param agent The main agent class. + * @param agent The main agent class. * @param resources Array of classes to be included with agent. */ - def attachAgentToJVM(agent: Class[_], resources: Seq[Class[_]] = Seq.empty): Unit = { - val vm = attachToRunningJVM() - vm.loadAgent(generateAgentJar(agent, resources).getAbsolutePath) - vm.detach() - } + def attachAgentToJVM(agent: Class[_], resources: Seq[Class[_]] = Seq.empty): Unit = attachToRunningJVM(agent, resources) /** * Java variant @@ -81,6 +69,7 @@ object AgentLoader { mainAttributes.put(new Name("Agent-Class"), agent.getName) mainAttributes.put(new Name("Can-Retransform-Classes"), "true") mainAttributes.put(new Name("Can-Redefine-Classes"), "true") + mainAttributes.put(new Name("Can-Set-Native-Method-Prefix"), "true") val jos = new JarOutputStream(new FileOutputStream(jarFile), manifest) @@ -101,6 +90,23 @@ object AgentLoader { } /** + * Attach to the running JVM. + * + * @return + * Returns the attached VirtualMachine + */ + private def attachToRunningJVM(agent: Class[_], resources: Seq[Class[_]]): Unit = { + AttachmentProviders.resolve() match { + case Some(virtualMachine) ⇒ + val virtualMachineInstance = virtualMachine.getDeclaredMethod("attach", classOf[String]).invoke(null, getPidFromRuntimeMBean) + virtualMachine.getDeclaredMethod("loadAgent", classOf[String], classOf[String]) + .invoke(virtualMachineInstance, generateAgentJar(agent, resources).getAbsolutePath, "") + virtualMachine.getDeclaredMethod("detach").invoke(virtualMachineInstance) + case None ⇒ throw new RuntimeException(s"Error trying to use Attach API") with NoStackTrace + } + } + + /** * Gets bytes from InputStream. * * @param stream @@ -113,103 +119,5 @@ object AgentLoader { } private def unqualify(clazz: Class[_]): String = clazz.getName.replace('.', '/') + ".class" - - /** - * Gets the current HotSpotVirtualMachine implementation otherwise a failure. - * - * @return - * Returns the HotSpotVirtualMachine implementation of the running JVM. - */ - private def findVirtualMachineImplementation(): Try[Class[_ <: HotSpotVirtualMachine]] = currentOs match { - case Windows(_, _, _) ⇒ Success(classOf[WindowsVirtualMachine]) - case Mac(_, _, _) ⇒ Success(classOf[BsdVirtualMachine]) - case Solaris(_, _, _) ⇒ Success(classOf[SolarisVirtualMachine]) - case Linux(_, _, _) ⇒ Success(classOf[LinuxVirtualMachine]) - case UnknownOs(name, arch, version) ⇒ - Failure(new RuntimeException(s"Cannot use Attach API on unknown OS: $name Arch: $arch Version: $version") with NoStackTrace) - } - - /** - * Attach to the running JVM. - * - * @return - * Returns the attached VirtualMachine - */ - private def attachToRunningJVM(): VirtualMachine = { - val AttachProvider = new AttachProvider() { - override def name(): String = null - override def `type`(): String = null - override def attachVirtualMachine(id: String): VirtualMachine = null - override def listVirtualMachines(): util.List[VirtualMachineDescriptor] = null - } - - findVirtualMachineImplementation() match { - case Success(vmClass) ⇒ - val pid = getPidFromRuntimeMBean - // This is only done with Reflection to avoid the JVM pre-loading all the XyzVirtualMachine classes. - val newVM = vmConstructor(vmClass).newInstance(AttachProvider, pid) - newVM.asInstanceOf[VirtualMachine] - case Failure(reason) ⇒ throw reason - } - } - - /** - * `BsdVirtualMachine`, used when your platform is Mac - vmClass parameter, has the constructor - * without modifier (by default is `package-private`), so `AgentLoader` can not instance it, for that reason - * we need to set accessible that constructor via reflection. - * - * @param vmClass - * @return - */ - private def vmConstructor(vmClass: Class[_ <: HotSpotVirtualMachine]): Constructor[_] = { - currentOs match { - case Mac(_, _, _) ⇒ - val vmC = vmClass.getDeclaredConstructor(classOf[AttachProvider], classOf[String]) - vmC.setAccessible(true) - vmC - - case _ ⇒ vmClass.getConstructor(classOf[AttachProvider], classOf[String]) - } - } } -package object resolver { - - /* OS RESOLVERS */ - private[loader] object OsResolver { - private[resolver] trait OsIdentifiable { - def name: String - def arch: String - def version: String - } - - private[loader] case class Windows(name: String, arch: String, version: String) extends OsIdentifiable - private[loader] case class Mac(name: String, arch: String, version: String) extends OsIdentifiable - private[loader] case class Solaris(name: String, arch: String, version: String) extends OsIdentifiable - private[loader] case class Linux(name: String, arch: String, version: String) extends OsIdentifiable - private[loader] case class UnknownOs(name: String, arch: String, version: String) extends OsIdentifiable - - private[resolver] val osName = System.getProperty("os.name") - private[resolver] val osArch = System.getProperty("os.arch") - private[resolver] val osVersion = System.getProperty("os.version") - - private[this] val defaultWindowsName = "Windows" - private[this] val defaultMacName = "Mac OS X" - private[this] val defaultSolarisName = "Solaris" - private[this] val defaultLinuxName = "Linux" - private[this] val defaultLinuxUpperCaseName = defaultLinuxName.toUpperCase - - /** - * Resolver OS based on java properties. - */ - val currentOs: OsIdentifiable = osName match { - case os if os.startsWith(defaultWindowsName) ⇒ Windows(os, osArch, osVersion) - case os if os.startsWith(defaultMacName) ⇒ Mac(os, osArch, osVersion) - case os if os.startsWith(defaultSolarisName) ⇒ Solaris(os, osArch, osVersion) - case os if os.startsWith(defaultLinuxName) || os.startsWith(defaultLinuxUpperCaseName) ⇒ Linux(os, osArch, osVersion) - case other ⇒ UnknownOs(other, osArch, osVersion) - } - } - /* OS RESOLVERS */ - -} diff --git a/kamon-autoweave/src/main/scala/kamon/autoweave/loader/AttachmentProviders.scala b/kamon-autoweave/src/main/scala/kamon/autoweave/loader/AttachmentProviders.scala new file mode 100644 index 00000000..40f58502 --- /dev/null +++ b/kamon-autoweave/src/main/scala/kamon/autoweave/loader/AttachmentProviders.scala @@ -0,0 +1,69 @@ +/* ========================================================================================= + * Copyright © 2013-2016 the kamon project <http://kamon.io/> + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + * ========================================================================================= + */ + +package kamon.autoweave.loader + +import java.io.File +import java.net.{ URL, URLClassLoader } +import java.security.{ PrivilegedAction, AccessController } + +object AttachmentProviders extends { + + val VirtualMachineTyeName = "com.sun.tools.attach.VirtualMachine" + + sealed trait AttachmentProvider { + def toolsJarPath: String + + /** + * Gets the current HotSpotVirtualMachine implementation otherwise a None. + * + * @return + * Returns the HotSpotVirtualMachine implementation of the running JVM. + */ + def resolve(): Option[Class[_]] = { + val toolsJar = new File(System.getProperty("java.home").replace('\\', '/') + "/../" + toolsJarPath) + if (toolsJar.isFile && toolsJar.canRead) + Some(AccessController.doPrivileged(new ClassLoaderCreationAction(toolsJar)).loadClass(VirtualMachineTyeName)) + else None + } + } + + case object JVM extends AttachmentProvider { val toolsJarPath = "../lib/tools.jar" } + case object JDK extends AttachmentProvider { val toolsJarPath = "lib/tools.jar" } + case object MAC extends AttachmentProvider { val toolsJarPath = "../Classes/classes.jar" } + + private val providers = Seq(JVM, JDK, MAC) + + private final class ClassLoaderCreationAction(toolsJar: File) extends PrivilegedAction[ClassLoader] { + override def run(): ClassLoader = new URLClassLoader(Array[URL](toolsJar.toURI.toURL), null) + } + + def resolve(): Option[Class[_]] = { + import scala.util.control.Breaks._ + + var vmClazz: Option[Class[_]] = None + + breakable { + for (provider ← providers) { + val vmClass = provider.resolve() + if (vmClass.isDefined) { + vmClazz = vmClass + break + } + } + } + vmClazz + } +} |