Finish new class loader mechanism for 3.3 agent core..
This commit is contained in:
parent
c1b92fd39f
commit
570ee752f9
|
|
@ -63,6 +63,11 @@
|
|||
<artifactId>apm-network</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.skywalking</groupId>
|
||||
<artifactId>apm-util</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy</artifactId>
|
||||
|
|
|
|||
|
|
@ -46,18 +46,26 @@ import org.skywalking.apm.logging.LogManager;
|
|||
*/
|
||||
public class AgentClassLoader extends ClassLoader {
|
||||
private static final ILog logger = LogManager.getLogger(AgentClassLoader.class);
|
||||
private static AgentClassLoader LOADER;
|
||||
/**
|
||||
* The default class loader for the agent.
|
||||
*/
|
||||
private static AgentClassLoader DEFAULT_LOADER;
|
||||
|
||||
private List<File> classpath;
|
||||
private List<Jar> allJars;
|
||||
private ReentrantLock jarScanLock = new ReentrantLock();
|
||||
|
||||
public static AgentClassLoader getDefault() {
|
||||
return LOADER;
|
||||
return DEFAULT_LOADER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the default
|
||||
* @return
|
||||
* @throws AgentPackageNotFoundException
|
||||
*/
|
||||
public static AgentClassLoader initDefaultLoader() throws AgentPackageNotFoundException {
|
||||
LOADER = new AgentClassLoader(PluginBootstrap.class.getClassLoader());
|
||||
DEFAULT_LOADER = new AgentClassLoader(PluginBootstrap.class.getClassLoader());
|
||||
return getDefault();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,21 +42,13 @@ import org.skywalking.apm.logging.LogManager;
|
|||
* This is a very important class in sky-walking's auto-instrumentation mechanism. If you want to fully understand why
|
||||
* need this, and how it works, you need have knowledge about Classloader appointment mechanism.
|
||||
* <p>
|
||||
* The loader will load a class, and focus the target class loader (be intercepted class's classloader) loads it.
|
||||
* <p>
|
||||
* If the target class and target class loader are same, the loaded classes( {@link InstanceConstructorInterceptor},
|
||||
* {@link InstanceMethodsAroundInterceptor} and {@link StaticMethodsAroundInterceptor} implementations) stay in
|
||||
* singleton.
|
||||
* <p>
|
||||
* Created by wusheng on 16/8/2.
|
||||
*/
|
||||
public class InterceptorInstanceLoader {
|
||||
private static final ILog logger = LogManager.getLogger(InterceptorInstanceLoader.class);
|
||||
|
||||
private static ConcurrentHashMap<String, Object> INSTANCE_CACHE = new ConcurrentHashMap<String, Object>();
|
||||
|
||||
private static ReentrantLock INSTANCE_LOAD_LOCK = new ReentrantLock();
|
||||
|
||||
private static Map<ClassLoader, ClassLoader> EXTEND_PLUGIN_CLASSLOADERS = new HashMap<ClassLoader, ClassLoader>();
|
||||
|
||||
/**
|
||||
|
|
@ -64,10 +56,10 @@ public class InterceptorInstanceLoader {
|
|||
* Create {@link AgentClassLoader} for each targetClassLoader, as an extend classloader.
|
||||
* It can load interceptor classes from plugins, activations folders.
|
||||
*
|
||||
* @param className
|
||||
* @param targetClassLoader
|
||||
* @param <T>
|
||||
* @return
|
||||
* @param className the interceptor class, which is expected to be found
|
||||
* @param targetClassLoader the class loader for current application context
|
||||
* @param <T> expected type
|
||||
* @return the type reference.
|
||||
* @throws InvocationTargetException
|
||||
* @throws IllegalAccessException
|
||||
* @throws InstantiationException
|
||||
|
|
@ -100,137 +92,4 @@ public class InterceptorInstanceLoader {
|
|||
|
||||
return (T) inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Old load method, just for backup
|
||||
*
|
||||
* @param className
|
||||
* @param targetClassLoader
|
||||
* @param <T>
|
||||
* @return
|
||||
* @throws InvocationTargetException
|
||||
* @throws IllegalAccessException
|
||||
* @throws InstantiationException
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public static <T> T load0(String className, ClassLoader targetClassLoader)
|
||||
throws InvocationTargetException, IllegalAccessException, InstantiationException, ClassNotFoundException {
|
||||
if (targetClassLoader == null) {
|
||||
targetClassLoader = InterceptorInstanceLoader.class.getClassLoader();
|
||||
}
|
||||
String instanceKey = className + "_OF_" + targetClassLoader.getClass().getName() + "@" + Integer.toHexString(targetClassLoader.hashCode());
|
||||
Object inst = INSTANCE_CACHE.get(instanceKey);
|
||||
if (inst == null) {
|
||||
if (InterceptorInstanceLoader.class.getClassLoader().equals(targetClassLoader)) {
|
||||
inst = targetClassLoader.loadClass(className).newInstance();
|
||||
} else {
|
||||
INSTANCE_LOAD_LOCK.lock();
|
||||
try {
|
||||
try {
|
||||
inst = findLoadedClass(className, targetClassLoader);
|
||||
if (inst == null) {
|
||||
inst = loadBinary(className, targetClassLoader);
|
||||
}
|
||||
if (inst == null) {
|
||||
throw new ClassNotFoundException(targetClassLoader.toString() + " load interceptor class:" + className + " failure.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ClassNotFoundException(targetClassLoader.toString() + " load interceptor class:" + className + " failure.", e);
|
||||
}
|
||||
} finally {
|
||||
INSTANCE_LOAD_LOCK.unlock();
|
||||
}
|
||||
}
|
||||
if (inst != null) {
|
||||
INSTANCE_CACHE.put(instanceKey, inst);
|
||||
}
|
||||
}
|
||||
|
||||
return (T) inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* load class from class binary files.
|
||||
* Most likely all the interceptor implementations should be loaded by this.
|
||||
*
|
||||
* @param className interceptor class name.
|
||||
* @param targetClassLoader the classloader, which should load the interceptor.
|
||||
* @param <T>
|
||||
* @return interceptor instance.
|
||||
* @throws InvocationTargetException
|
||||
* @throws IllegalAccessException
|
||||
* @throws InstantiationException
|
||||
*/
|
||||
private static <T> T loadBinary(String className,
|
||||
ClassLoader targetClassLoader) throws InvocationTargetException, IllegalAccessException, InstantiationException {
|
||||
String path = "/" + className.replace('.', '/').concat(".class");
|
||||
byte[] data = null;
|
||||
BufferedInputStream is = null;
|
||||
ByteArrayOutputStream baos = null;
|
||||
try {
|
||||
logger.debug("Read binary code of {} using classload {}", className, InterceptorInstanceLoader.class.getClassLoader());
|
||||
is = new BufferedInputStream(InterceptorInstanceLoader.class.getResourceAsStream(path));
|
||||
baos = new ByteArrayOutputStream();
|
||||
int ch = 0;
|
||||
while ((ch = is.read()) != -1) {
|
||||
baos.write(ch);
|
||||
}
|
||||
data = baos.toByteArray();
|
||||
} catch (IOException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
} finally {
|
||||
if (is != null)
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
if (baos != null)
|
||||
try {
|
||||
baos.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
Method defineClassMethod = null;
|
||||
Class<?> targetClassLoaderType = targetClassLoader.getClass();
|
||||
while (defineClassMethod == null && targetClassLoaderType != null) {
|
||||
try {
|
||||
defineClassMethod = targetClassLoaderType.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ProtectionDomain.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
targetClassLoaderType = targetClassLoaderType.getSuperclass();
|
||||
}
|
||||
}
|
||||
defineClassMethod.setAccessible(true);
|
||||
logger.debug("load binary code of {} to classloader {}", className, targetClassLoader);
|
||||
Class<?> type = (Class<?>) defineClassMethod.invoke(targetClassLoader, className, data, 0, data.length, null);
|
||||
return (T) type.newInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find loaded class in the current classloader.
|
||||
* Just in case some classes have already been loaded for some reason.
|
||||
*
|
||||
* @param className interceptor class name.
|
||||
* @param targetClassLoader the classloader, which should load the interceptor.
|
||||
* @param <T>
|
||||
* @return interceptor instance.
|
||||
*/
|
||||
private static <T> T findLoadedClass(String className,
|
||||
ClassLoader targetClassLoader) throws InvocationTargetException, IllegalAccessException, InstantiationException {
|
||||
Method defineClassMethod = null;
|
||||
Class<?> targetClassLoaderType = targetClassLoader.getClass();
|
||||
while (defineClassMethod == null && targetClassLoaderType != null) {
|
||||
try {
|
||||
defineClassMethod = targetClassLoaderType.getDeclaredMethod("findLoadedClass", String.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
targetClassLoaderType = targetClassLoaderType.getSuperclass();
|
||||
}
|
||||
}
|
||||
defineClassMethod.setAccessible(true);
|
||||
Class<?> type = (Class<?>) defineClassMethod.invoke(targetClassLoader, className);
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
return (T) type.newInstance();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017, OpenSkywalking Organization All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Project repository: https://github.com/OpenSkywalking/skywalking
|
||||
*/
|
||||
|
||||
package org.skywalking.apm.agent.core.plugin.loader;
|
||||
|
||||
/**
|
||||
* The <code>NotImplementationException</code> represents that, a method didn't implement yet. Most likely, this method is not needed in current version, but be called by unexpected.
|
||||
*
|
||||
* @author wusheng
|
||||
*/
|
||||
public class NotImplementationException extends RuntimeException {
|
||||
}
|
||||
|
|
@ -36,13 +36,13 @@
|
|||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>4.3.10.RELEASE</version>
|
||||
<scope>provided</scope>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<version>4.3.8.RELEASE</version>
|
||||
<scope>provided</scope>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
|
|
|
|||
|
|
@ -42,12 +42,4 @@
|
|||
<compiler.version>1.6</compiler.version>
|
||||
<shade.package>org.skywalking.apm.dependencies</shade.package>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.skywalking</groupId>
|
||||
<artifactId>apm-util</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
|||
Loading…
Reference in New Issue