提交 570ee752 编写于 作者: wu-sheng's avatar wu-sheng

Finish new class loader mechanism for 3.3 agent core..

上级 c1b92fd3
......@@ -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();
}
}
/*
* 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>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册