/*
* Copyright 2016 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.test.javasssit;
import com.google.inject.Provider;
import com.google.inject.util.Providers;
import com.navercorp.pinpoint.bootstrap.config.DefaultProfilerConfig;
import com.navercorp.pinpoint.bootstrap.instrument.ClassFilters;
import com.navercorp.pinpoint.bootstrap.instrument.InstrumentClass;
import com.navercorp.pinpoint.bootstrap.instrument.InstrumentContext;
import com.navercorp.pinpoint.common.util.ArrayUtils;
import com.navercorp.pinpoint.profiler.instrument.InstrumentEngine;
import com.navercorp.pinpoint.bootstrap.instrument.InstrumentException;
import com.navercorp.pinpoint.bootstrap.instrument.InstrumentMethod;
import com.navercorp.pinpoint.bootstrap.instrument.Instrumentor;
import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformCallback;
import com.navercorp.pinpoint.bootstrap.interceptor.Interceptor;
import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory;
import com.navercorp.pinpoint.common.trace.ServiceType;
import com.navercorp.pinpoint.profiler.instrument.JavassistEngine;
import com.navercorp.pinpoint.profiler.interceptor.registry.GlobalInterceptorRegistryBinder;
import com.navercorp.pinpoint.profiler.logging.Slf4jLoggerBinder;
import com.navercorp.pinpoint.profiler.metadata.ApiMetaDataService;
import com.navercorp.pinpoint.profiler.objectfactory.ObjectBinderFactory;
import com.navercorp.pinpoint.test.MockApplicationContext;
import com.navercorp.pinpoint.test.classloader.TestClassLoader;
import com.navercorp.pinpoint.test.util.BytecodeUtils;
import javassist.bytecode.Descriptor;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
/**
* @author emeroad
*/
@Deprecated
public class JavassistClassTest {
private Logger logger = LoggerFactory.getLogger(JavassistClassTest.class.getName());
@Before
public void clear() {
TestInterceptors.clear();
}
private byte[] readByteCode(String className) {
final ClassLoader classLoader = ClassLoader.getSystemClassLoader();
return BytecodeUtils.getClassFile(classLoader, className);
}
@Test
public void testClassHierarchy() throws InstrumentException {
InstrumentEngine engine = newJavassistEngine();
InstrumentContext instrumentContext = mock(InstrumentContext.class);
String testObjectName = "com.navercorp.pinpoint.test.javasssit.mock.TestObject";
byte[] testObjectByteCode = readByteCode(testObjectName);
InstrumentClass testObject = engine.getClass(instrumentContext, null, testObjectName, testObjectByteCode);
Assert.assertEquals(testObject.getName(), testObjectName);
String testObjectSuperClass = testObject.getSuperClass();
Assert.assertEquals("java.lang.Object", testObjectSuperClass);
String[] testObjectSuperClassInterfaces = testObject.getInterfaces();
Assert.assertEquals(testObjectSuperClassInterfaces.length, 0);
final String classHierarchyTestMockName = "com.navercorp.pinpoint.test.javasssit.mock.ClassHierarchyTestMock";
byte[] classHierarchyTestMockByteCode = readByteCode(classHierarchyTestMockName);
InstrumentClass classHierarchyObject = engine.getClass(instrumentContext, null, classHierarchyTestMockName, classHierarchyTestMockByteCode);
String hierarchySuperClass = classHierarchyObject.getSuperClass();
Assert.assertEquals("java.util.HashMap", hierarchySuperClass);
String[] hierarchyInterfaces = classHierarchyObject.getInterfaces();
Assert.assertEquals(hierarchyInterfaces.length, 2);
Assert.assertEquals(hierarchyInterfaces[0], "java.lang.Runnable");
Assert.assertEquals(hierarchyInterfaces[1], "java.lang.Comparable");
}
private InstrumentEngine newJavassistEngine() {
Instrumentation instrumentation = mock(Instrumentation.class);
ObjectBinderFactory objectBinderFactory = mock(ObjectBinderFactory.class);
Provider<ApiMetaDataService> apiMetaDataService = Providers.of(mock(ApiMetaDataService.class));
return new JavassistEngine(instrumentation, objectBinderFactory, new GlobalInterceptorRegistryBinder(), apiMetaDataService, null);
}
@Test
public void testDeclaredMethod() throws InstrumentException {
InstrumentEngine engine = newJavassistEngine();
InstrumentContext instrumentContext = mock(InstrumentContext.class);
String testObjectName = "com.navercorp.pinpoint.test.javasssit.mock.TestObject";
byte[] testObjectByteCode = readByteCode(testObjectName);
InstrumentClass testObject = engine.getClass(instrumentContext, null, testObjectName, testObjectByteCode);
Assert.assertEquals(testObject.getName(), testObjectName);
InstrumentMethod declaredMethod = testObject.getDeclaredMethod("callA");
Assert.assertNotNull(declaredMethod);
}
@Test
public void testDeclaredMethods() throws InstrumentException {
InstrumentEngine engine = newJavassistEngine();
InstrumentContext instrumentContext = mock(InstrumentContext.class);
String testObjectName = "com.navercorp.pinpoint.test.javasssit.mock.TestObject";
byte[] testObjectByteCode = readByteCode(testObjectName);
InstrumentClass testObject = engine.getClass(instrumentContext, null, testObjectName, testObjectByteCode);
Assert.assertEquals(testObject.getName(), testObjectName);
int findMethodCount = 0;
for (InstrumentMethod methodInfo : testObject.getDeclaredMethods()) {
if (!methodInfo.getName().equals("callA")) {
continue;
}
String[] parameterTypes = methodInfo.getParameterTypes();
if (ArrayUtils.isEmpty(parameterTypes)) {
findMethodCount++;
}
}
Assert.assertEquals(findMethodCount, 1);
}
@Test
public void testBeforeAddInterceptor() throws Exception {
final TestClassLoader loader = getTestClassLoader();
final String javassistClassName = "com.navercorp.pinpoint.test.javasssit.mock.TestObject";
loader.addTransformer(javassistClassName, new TransformCallback() {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
try {
logger.info("modify className:{} cl:{}", className, classLoader);
InstrumentClass aClass = instrumentor.getInstrumentClass(classLoader, javassistClassName, classfileBuffer);
String methodName = "callA";
InstrumentMethod callaMethod = aClass.getDeclaredMethod(methodName);
callaMethod.addInterceptor("com.navercorp.pinpoint.test.javasssit.TestBeforeInterceptor");
byte[] bytes = aClass.toBytecode();
return bytes;
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage(), e);
}
}
});
Class<?> testObjectClazz = loader.loadClass(javassistClassName);
final String methodName = "callA";
logger.info("class:{}", testObjectClazz.toString());
logger.info("class cl:{}", testObjectClazz.getClassLoader());
final Object testObject = testObjectClazz.newInstance();
Method callA = testObjectClazz.getMethod(methodName);
callA.invoke(testObject);
Interceptor interceptor = getInterceptor(loader, 0);
assertEqualsIntField(interceptor, "call", 1);
assertEqualsObjectField(interceptor, "className", "com.navercorp.pinpoint.test.javasssit.mock.TestObject");
assertEqualsObjectField(interceptor, "methodName", methodName);
assertEqualsObjectField(interceptor, "args", null);
assertEqualsObjectField(interceptor, "target", testObject);
}
private Interceptor getInterceptor(final TestClassLoader loader, int index) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException {
Class<?> interceptorClazz = loader.loadClass("com.navercorp.pinpoint.test.javasssit.TestInterceptors");
Method getMethod = interceptorClazz.getMethod("get", int.class);
Interceptor interceptor = (Interceptor) getMethod.invoke(null, index);
return interceptor;
}
private TestClassLoader getTestClassLoader() {
PLoggerFactory.initialize(new Slf4jLoggerBinder());
DefaultProfilerConfig profilerConfig = new DefaultProfilerConfig();
profilerConfig.setApplicationServerType(ServiceType.TEST_STAND_ALONE.getName());
MockApplicationContext applicationContext = MockApplicationContext.of(profilerConfig);
TestClassLoader testClassLoader = new TestClassLoader(applicationContext);
testClassLoader.initialize();
return testClassLoader;
}
public void assertEqualsIntField(Object target, String fieldName, int value) throws NoSuchFieldException, IllegalAccessException {
Field field = target.getClass().getField(fieldName);
int anInt = field.getInt(target);
Assert.assertEquals(anInt, value);
}
public void assertEqualsObjectField(Object target, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = target.getClass().getField(fieldName);
Object obj = field.get(target);
Assert.assertEquals(value, obj);
}
@Ignore
@Test
public void testAddAfterInterceptor() throws Exception {
final TestClassLoader loader = getTestClassLoader();
final String testClassObject = "com.navercorp.pinpoint.test.javasssit.mock.TestObject2";
loader.addTransformer(testClassObject, new TransformCallback() {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
try {
logger.info("modify cl:{}", classLoader);
InstrumentClass aClass = instrumentor.getInstrumentClass(classLoader, testClassObject, classfileBuffer);
String methodName = "callA";
aClass.getDeclaredMethod(methodName).addInterceptor("com.navercorp.pinpoint.profiler.interceptor.TestAfterInterceptor");
String methodName2 = "callB";
aClass.getDeclaredMethod(methodName2).addInterceptor("com.navercorp.pinpoint.profiler.interceptor.TestAfterInterceptor");
return aClass.toBytecode();
} catch (InstrumentException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
});
Class<?> testObjectClazz = loader.loadClass(testClassObject);
final String methodName = "callA";
logger.info("class:{}", testObjectClazz.toString());
final Object testObject = testObjectClazz.newInstance();
Method callA = testObjectClazz.getMethod(methodName);
Object result = callA.invoke(testObject);
Interceptor interceptor = getInterceptor(loader, 0);
assertEqualsIntField(interceptor, "call", 1);
assertEqualsObjectField(interceptor, "className", testClassObject);
assertEqualsObjectField(interceptor, "methodName", methodName);
assertEqualsObjectField(interceptor, "args", null);
assertEqualsObjectField(interceptor, "target", testObject);
assertEqualsObjectField(interceptor, "result", result);
final String methodName2 = "callB";
Method callBMethod = testObject.getClass().getMethod(methodName2);
callBMethod.invoke(testObject);
Interceptor interceptor2 = getInterceptor(loader, 1);
assertEqualsIntField(interceptor2, "call", 1);
assertEqualsObjectField(interceptor2, "className", testClassObject);
assertEqualsObjectField(interceptor2, "methodName", methodName2);
assertEqualsObjectField(interceptor2, "args", null);
assertEqualsObjectField(interceptor2, "target", testObject);
assertEqualsObjectField(interceptor2, "result", null);
}
@Test
public void nullDescriptor() {
String nullDescriptor = Descriptor.ofParameters(null);
logger.debug("Descriptor null:{}", nullDescriptor);
}
@Test
public void getNestedClasses() throws Exception {
InstrumentEngine engine = newJavassistEngine();
InstrumentContext instrumentContext = mock(InstrumentContext.class);
String testObjectName = "com.navercorp.pinpoint.test.javasssit.mock.TestObjectNestedClass";
byte[] testObjectByteCode = readByteCode(testObjectName);
InstrumentClass testObject = engine.getClass(instrumentContext, null, testObjectName, testObjectByteCode);
Assert.assertEquals(testObject.getName(), testObjectName);
// find class name condition.
final String targetClassName = "com.navercorp.pinpoint.profiler.interceptor.bci.TestObjectNestedClass$InstanceInner";
for (InstrumentClass c : testObject.getNestedClasses(ClassFilters.name(targetClassName))) {
assertEquals(targetClassName, c.getName());
}
// find enclosing method condition.
assertEquals(2, testObject.getNestedClasses(ClassFilters.enclosingMethod("annonymousInnerClass")).size());
// find interface condition.
assertEquals(2, testObject.getNestedClasses(ClassFilters.interfaze("java.util.concurrent.Callable")).size());
// find enclosing method & interface condition.
assertEquals(1, testObject.getNestedClasses(ClassFilters.chain(ClassFilters.enclosingMethod("annonymousInnerClass"), ClassFilters.interfaze("java.util.concurrent.Callable"))).size());
}
@Test
public void hasEnclodingMethod() throws Exception {
InstrumentEngine engine = newJavassistEngine();
InstrumentContext instrumentContext = mock(InstrumentContext.class);
String testObjectName = "com.navercorp.pinpoint.test.javasssit.mock.TestObjectNestedClass";
byte[] testObjectByteCode = readByteCode(testObjectName);
InstrumentClass testObject = engine.getClass(instrumentContext, null, testObjectName, testObjectByteCode);
Assert.assertEquals(testObject.getName(), testObjectName);
assertEquals(1, testObject.getNestedClasses(ClassFilters.enclosingMethod("enclosingMethod", "java.lang.String", "int")).size());
assertEquals(0, testObject.getNestedClasses(ClassFilters.enclosingMethod("enclosingMethod", "int")).size());
}
}