/*
* Copyright 2017 NAVER Corp.
*
* 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 com.navercorp.pinpoint.profiler.instrument.classloading;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import com.navercorp.pinpoint.profiler.instrument.classloading.ClassInjector;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.LoaderClassPath;
import javassist.NotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.navercorp.pinpoint.exception.PinpointException;
/**
* @deprecated Since 1.6.1
* @author Jongho Moon
*/
@Deprecated
public class LegacyProfilerPluginClassInjector implements ClassInjector {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private static final Method DEFINE_CLASS;
static {
try {
DEFINE_CLASS = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
DEFINE_CLASS.setAccessible(true);
} catch (Exception e) {
throw new PinpointException("Cannot access ClassLoader.defineClass(String, byte[], int, int)", e);
}
}
private final ClassLoader sourceClassLoader;
public LegacyProfilerPluginClassInjector(ClassLoader sourceClassLoader) {
if (sourceClassLoader == null) {
throw new NullPointerException("sourceClassLoader must not be null");
}
this.sourceClassLoader = sourceClassLoader;
}
@Override
@SuppressWarnings("unchecked")
public <T> Class<? extends T> injectClass(ClassLoader classLoader, String className) {
classLoader = getClassLoader(classLoader);
try {
return (Class<T>)loadFromOtherClassLoader(classLoader, className);
} catch (Exception e) {
logger.warn("Failed to load plugin class {} with classLoader {}", className, classLoader, e);
throw new PinpointException("Failed to load plugin class " + className + " with classLoader " + classLoader, e);
}
}
private Class<?> loadFromOtherClassLoader(ClassLoader classLoader, String className) throws NotFoundException, IllegalArgumentException, IOException, CannotCompileException, IllegalAccessException, InvocationTargetException {
ClassPool pool = new ClassPool();
pool.appendClassPath(new LoaderClassPath(classLoader));
pool.appendClassPath(new LoaderClassPath(sourceClassLoader));
return loadFromOtherClassLoader(pool, classLoader, className);
}
private Class<?> loadFromOtherClassLoader(ClassPool pool, ClassLoader classLoader, String className) throws NotFoundException, IOException, CannotCompileException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Class<?> c = null;
try {
c = classLoader.loadClass(className);
} catch (ClassNotFoundException ignore) {
}
if (c != null) {
return c;
}
CtClass ct = pool.get(className);
if (ct == null) {
throw new NotFoundException(className);
}
CtClass superClass = ct.getSuperclass();
if (superClass != null) {
loadFromOtherClassLoader(pool, classLoader, superClass.getName());
}
CtClass[] interfaces = ct.getInterfaces();
for (CtClass i : interfaces) {
loadFromOtherClassLoader(pool, classLoader, i.getName());
}
Collection<String> refs = ct.getRefClasses();
for (String ref : refs) {
try {
loadFromOtherClassLoader(pool, classLoader, ref);
} catch (NotFoundException e) {
logger.warn("Skip a referenced class because of NotFoundException : ", e);
}
}
byte[] bytes = ct.toBytecode();
return (Class<?>)DEFINE_CLASS.invoke(classLoader, ct.getName(), bytes, 0, bytes.length);
}
@Override
public InputStream getResourceAsStream(ClassLoader classLoader, String classPath) {
classLoader = getClassLoader(classLoader);
return classLoader.getResourceAsStream(classPath);
}
private static ClassLoader getClassLoader(ClassLoader classLoader) {
if (classLoader == null) {
return ClassLoader.getSystemClassLoader();
}
return classLoader;
}
}