
org.glowroot.agent.weaving.AdviceBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of glowroot-agent-it-harness Show documentation
Show all versions of glowroot-agent-it-harness Show documentation
Glowroot Agent Integration Test Harness
/*
* Copyright 2012-2016 the original author or authors.
*
* 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 org.glowroot.agent.weaving;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.List;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.glowroot.agent.shaded.google.common.base.Joiner;
import org.glowroot.agent.shaded.google.common.collect.ImmutableList;
import org.glowroot.agent.shaded.google.common.collect.ImmutableMap;
import org.glowroot.agent.shaded.google.common.collect.Lists;
import org.glowroot.agent.shaded.objectweb.asm.Type;
import org.glowroot.agent.shaded.objectweb.asm.commons.Method;
import org.glowroot.agent.plugin.api.OptionalThreadContext;
import org.glowroot.agent.plugin.api.ThreadContext;
import org.glowroot.agent.plugin.api.weaving.BindClassMeta;
import org.glowroot.agent.plugin.api.weaving.BindMethodMeta;
import org.glowroot.agent.plugin.api.weaving.BindMethodName;
import org.glowroot.agent.plugin.api.weaving.BindOptionalReturn;
import org.glowroot.agent.plugin.api.weaving.BindParameter;
import org.glowroot.agent.plugin.api.weaving.BindParameterArray;
import org.glowroot.agent.plugin.api.weaving.BindReceiver;
import org.glowroot.agent.plugin.api.weaving.BindReturn;
import org.glowroot.agent.plugin.api.weaving.BindThrowable;
import org.glowroot.agent.plugin.api.weaving.BindTraveler;
import org.glowroot.agent.plugin.api.weaving.IsEnabled;
import org.glowroot.agent.plugin.api.weaving.OnAfter;
import org.glowroot.agent.plugin.api.weaving.OnBefore;
import org.glowroot.agent.plugin.api.weaving.OnReturn;
import org.glowroot.agent.plugin.api.weaving.OnThrow;
import org.glowroot.agent.plugin.api.weaving.Pointcut;
import org.glowroot.agent.weaving.Advice.AdviceParameter;
import org.glowroot.agent.weaving.ClassLoaders.LazyDefinedClass;
import org.glowroot.agent.shaded.glowroot.common.util.Patterns;
import static org.glowroot.agent.shaded.google.common.base.Preconditions.checkNotNull;
public class AdviceBuilder {
private static final ImmutableList> isEnabledBindAnnotationTypes =
ImmutableList.of(BindReceiver.class, BindParameter.class, BindParameterArray.class,
BindMethodName.class, BindClassMeta.class, BindMethodMeta.class);
private static final ImmutableList> onBeforeBindAnnotationTypes =
ImmutableList.of(BindReceiver.class, BindParameter.class, BindParameterArray.class,
BindMethodName.class, BindClassMeta.class, BindMethodMeta.class);
private static final ImmutableList> onReturnBindAnnotationTypes =
ImmutableList.of(BindReceiver.class, BindParameter.class, BindParameterArray.class,
BindMethodName.class, BindReturn.class, BindOptionalReturn.class,
BindTraveler.class, BindClassMeta.class, BindMethodMeta.class);
private static final ImmutableList> onThrowBindAnnotationTypes =
ImmutableList.of(BindReceiver.class, BindParameter.class, BindParameterArray.class,
BindMethodName.class, BindThrowable.class, BindTraveler.class,
BindClassMeta.class, BindMethodMeta.class);
private static final ImmutableList> onAfterBindAnnotationTypes =
ImmutableList.of(BindReceiver.class, BindParameter.class, BindParameterArray.class,
BindMethodName.class, BindTraveler.class, BindClassMeta.class,
BindMethodMeta.class);
private static final ImmutableMap, ParameterKind> parameterKindMap =
new ImmutableMap.Builder, ParameterKind>()
.put(BindReceiver.class, ParameterKind.RECEIVER)
.put(BindParameter.class, ParameterKind.METHOD_ARG)
.put(BindParameterArray.class, ParameterKind.METHOD_ARG_ARRAY)
.put(BindMethodName.class, ParameterKind.METHOD_NAME)
.put(BindReturn.class, ParameterKind.RETURN)
.put(BindOptionalReturn.class, ParameterKind.OPTIONAL_RETURN)
.put(BindThrowable.class, ParameterKind.THROWABLE)
.put(BindTraveler.class, ParameterKind.TRAVELER)
.put(BindClassMeta.class, ParameterKind.CLASS_META)
.put(BindMethodMeta.class, ParameterKind.METHOD_META)
.build();
private final ImmutableAdvice.Builder builder = ImmutableAdvice.builder();
private final @Nullable Class> adviceClass;
private final @Nullable LazyDefinedClass lazyAdviceClass;
private boolean hasIsEnabledAdvice;
private boolean hasOnBeforeAdvice;
private boolean hasOnReturnAdvice;
private boolean hasOnThrowAdvice;
private boolean hasOnAfterAdvice;
public AdviceBuilder(Class> adviceClass) {
this.adviceClass = adviceClass;
this.lazyAdviceClass = null;
builder.reweavable(false);
}
public AdviceBuilder(LazyDefinedClass lazyAdviceClass, boolean reweavable) {
this.adviceClass = null;
this.lazyAdviceClass = lazyAdviceClass;
builder.reweavable(reweavable);
}
public Advice build() throws Exception {
Class> adviceClass = this.adviceClass;
if (adviceClass == null) {
// safe check, if adviceClass is null then lazyAdviceClass is non-null
checkNotNull(lazyAdviceClass);
ClassLoader tempClassLoader =
AccessController.doPrivileged(new PrivilegedAction() {
@Override
public ClassLoader run() {
return new URLClassLoader(new URL[0],
AdviceBuilder.class.getClassLoader());
}
});
adviceClass = ClassLoaders.defineClass(lazyAdviceClass, tempClassLoader);
}
Pointcut pointcut = adviceClass.getAnnotation(Pointcut.class);
checkState(pointcut != null, "Class has no @Pointcut annotation");
checkNotNull(pointcut);
builder.pointcut(pointcut);
builder.adviceType(Type.getType(adviceClass));
String pointcutClassName = pointcut.className();
builder.pointcutClassName(pointcutClassName);
builder.pointcutClassNamePattern(buildPattern(pointcutClassName));
builder.pointcutClassNameAnnotationPattern(buildPattern(pointcut.classAnnotation()));
if (pointcut.methodDeclaringClassName().equals("")) {
builder.pointcutMethodDeclaringClassName(pointcutClassName);
builder.pointcutMethodDeclaringClassNamePattern(buildPattern(pointcutClassName));
} else {
builder.pointcutMethodDeclaringClassName(pointcut.methodDeclaringClassName());
builder.pointcutMethodDeclaringClassNamePattern(
buildPattern(pointcut.methodDeclaringClassName()));
}
builder.pointcutMethodNamePattern(buildPattern(pointcut.methodName()));
builder.pointcutMethodAnnotationPattern(buildPattern(pointcut.methodAnnotation()));
builder.pointcutMethodParameterTypes(buildPatterns(pointcut.methodParameterTypes()));
// hasBindThreadContext will be overridden below if needed
builder.hasBindThreadContext(false);
// hasBindOptionalThreadContext will be overridden below if needed
builder.hasBindOptionalThreadContext(false);
for (java.lang.reflect.Method method : adviceClass.getMethods()) {
if (method.isAnnotationPresent(IsEnabled.class)) {
initIsEnabledAdvice(adviceClass, method);
} else if (method.isAnnotationPresent(OnBefore.class)) {
initOnBeforeAdvice(adviceClass, method);
} else if (method.isAnnotationPresent(OnReturn.class)) {
initOnReturnAdvice(adviceClass, method);
} else if (method.isAnnotationPresent(OnThrow.class)) {
initOnThrowAdvice(adviceClass, method);
} else if (method.isAnnotationPresent(OnAfter.class)) {
initOnAfterAdvice(adviceClass, method);
}
}
return builder.build();
}
private void initIsEnabledAdvice(Class> adviceClass, java.lang.reflect.Method method)
throws AdviceConstructionException {
checkState(!hasIsEnabledAdvice,
"@Pointcut '" + adviceClass.getName() + "' has more than one @IsEnabled method");
Method asmMethod = Method.getMethod(method);
checkState(asmMethod.getReturnType().getSort() == Type.BOOLEAN,
"@IsEnabled method must return boolean");
builder.isEnabledAdvice(asmMethod);
List parameters = getAdviceParameters(method.getParameterAnnotations(),
method.getParameterTypes(), isEnabledBindAnnotationTypes, IsEnabled.class);
builder.addAllIsEnabledParameters(parameters);
hasIsEnabledAdvice = true;
}
private void initOnBeforeAdvice(Class> adviceClass, java.lang.reflect.Method method)
throws AdviceConstructionException {
checkState(!hasOnBeforeAdvice,
"@Pointcut '" + adviceClass.getName() + "' has more than one @OnBefore method");
Method onBeforeAdvice = Method.getMethod(method);
builder.onBeforeAdvice(onBeforeAdvice);
List parameters = getAdviceParameters(method.getParameterAnnotations(),
method.getParameterTypes(), onBeforeBindAnnotationTypes, OnBefore.class);
builder.addAllOnBeforeParameters(parameters);
if (onBeforeAdvice.getReturnType().getSort() != Type.VOID) {
builder.travelerType(onBeforeAdvice.getReturnType());
}
checkForBindThreadContext(parameters);
checkForBindOptionalThreadContext(parameters);
hasOnBeforeAdvice = true;
}
private void initOnReturnAdvice(Class> adviceClass, java.lang.reflect.Method method)
throws AdviceConstructionException {
checkState(!hasOnReturnAdvice,
"@Pointcut '" + adviceClass.getName() + "' has more than one @OnReturn method");
List parameters = getAdviceParameters(method.getParameterAnnotations(),
method.getParameterTypes(), onReturnBindAnnotationTypes, OnReturn.class);
for (int i = 1; i < parameters.size(); i++) {
checkState(parameters.get(i).kind() != ParameterKind.RETURN,
"@BindReturn must be the first argument to @OnReturn");
checkState(parameters.get(i).kind() != ParameterKind.OPTIONAL_RETURN,
"@BindOptionalReturn must be the first argument to @OnReturn");
}
builder.onReturnAdvice(Method.getMethod(method));
builder.addAllOnReturnParameters(parameters);
checkForBindThreadContext(parameters);
checkForBindOptionalThreadContext(parameters);
hasOnReturnAdvice = true;
}
private void initOnThrowAdvice(Class> adviceClass, java.lang.reflect.Method method)
throws AdviceConstructionException {
checkState(!hasOnThrowAdvice,
"@Pointcut '" + adviceClass.getName() + "' has more than one @OnThrow method");
List parameters = getAdviceParameters(method.getParameterAnnotations(),
method.getParameterTypes(), onThrowBindAnnotationTypes, OnThrow.class);
for (int i = 1; i < parameters.size(); i++) {
checkState(parameters.get(i).kind() != ParameterKind.THROWABLE,
"@BindThrowable must be the first argument to @OnThrow");
}
Method asmMethod = Method.getMethod(method);
checkState(asmMethod.getReturnType().getSort() == Type.VOID,
"@OnThrow method must return void (for now)");
builder.onThrowAdvice(asmMethod);
builder.addAllOnThrowParameters(parameters);
checkForBindThreadContext(parameters);
checkForBindOptionalThreadContext(parameters);
hasOnThrowAdvice = true;
}
private void initOnAfterAdvice(Class> adviceClass, java.lang.reflect.Method method)
throws AdviceConstructionException {
checkState(!hasOnAfterAdvice,
"@Pointcut '" + adviceClass.getName() + "' has more than one @OnAfter method");
Method asmMethod = Method.getMethod(method);
checkState(asmMethod.getReturnType().getSort() == Type.VOID,
"@OnAfter method must return void");
builder.onAfterAdvice(asmMethod);
List parameters = getAdviceParameters(method.getParameterAnnotations(),
method.getParameterTypes(), onAfterBindAnnotationTypes, OnAfter.class);
builder.addAllOnAfterParameters(parameters);
checkForBindThreadContext(parameters);
checkForBindOptionalThreadContext(parameters);
hasOnAfterAdvice = true;
}
private void checkForBindThreadContext(List parameters) {
for (AdviceParameter parameter : parameters) {
if (parameter.kind() == ParameterKind.THREAD_CONTEXT) {
builder.hasBindThreadContext(true);
return;
}
}
}
private void checkForBindOptionalThreadContext(List parameters) {
for (AdviceParameter parameter : parameters) {
if (parameter.kind() == ParameterKind.OPTIONAL_THREAD_CONTEXT) {
builder.hasBindOptionalThreadContext(true);
break;
}
}
}
private static void checkState(boolean condition, String message)
throws AdviceConstructionException {
if (!condition) {
throw new AdviceConstructionException(message);
}
}
private List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy