All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glowroot.agent.weaving.AdviceMatcher Maven / Gradle / Ivy

There is a newer version: 0.14.0-beta.3
Show newest version
/*
 * Copyright 2012-2018 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.reflect.Modifier;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;

import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.collect.ImmutableList;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.collect.Lists;
import org.glowroot.agent.shaded.org.checkerframework.checker.nullness.qual.Nullable;
import org.immutables.value.Value;
import org.glowroot.agent.shaded.org.objectweb.asm.Type;
import org.glowroot.agent.shaded.org.glowroot.agent.shaded.org.slf4j.Logger;
import org.glowroot.agent.shaded.org.glowroot.agent.shaded.org.slf4j.LoggerFactory;

import org.glowroot.agent.plugin.api.weaving.MethodModifier;
import org.glowroot.agent.shaded.org.glowroot.common.util.Styles;

@Value.Immutable
@Styles.AllParameters
abstract class AdviceMatcher {

    private static final Logger logger = LoggerFactory.getLogger(AdviceMatcher.class);

    static ImmutableList getAdviceMatchers(String className,
            List classAnnotations, Collection superClassNames,
            List advisors) {
        List adviceMatchers = Lists.newArrayList();
        for (Advice advice : advisors) {
            if (isClassMatch(className, classAnnotations, superClassNames, advice)) {
                adviceMatchers.add(ImmutableAdviceMatcher.of(advice));
            }
        }
        return ImmutableList.copyOf(adviceMatchers);
    }

    abstract Advice advice();

    boolean isMethodLevelMatch(String methodName, List methodAnnotations,
            List parameterTypes, Type returnType, int modifiers) {
        if (!isMethodNameMatch(methodName) || !isAnnotationMatch(methodAnnotations,
                advice().pointcutMethodAnnotationPattern(), advice().pointcut().methodAnnotation())
                || !isMethodParameterTypesMatch(parameterTypes)) {
            return false;
        }
        return isMethodReturnMatch(returnType) && isMethodModifiersMatch(modifiers);
    }

    private boolean isMethodNameMatch(String methodName) {
        if (methodName.equals("")) {
            // static initializers are not supported
            return false;
        }
        Pattern pointcutMethodNamePattern = advice().pointcutMethodNamePattern();
        if (pointcutMethodNamePattern != null) {
            // don't want patterns to match constructors
            return !methodName.equals("")
                    && pointcutMethodNamePattern.matcher(methodName).matches();
        }
        String pointcutMethodName = advice().pointcut().methodName();
        return pointcutMethodName.isEmpty() || pointcutMethodName.equals(methodName);
    }

    private boolean isMethodParameterTypesMatch(List parameterTypes) {
        List pointcutMethodParameterTypes = advice().pointcutMethodParameterTypes();
        for (int i = 0; i < pointcutMethodParameterTypes.size(); i++) {
            Object pointcutMethodParameterType = pointcutMethodParameterTypes.get(i);
            if (pointcutMethodParameterType.equals("..")) {
                if (i != pointcutMethodParameterTypes.size() - 1) {
                    logger.warn("'..' can only be used at the end of methodParameterTypes");
                    return false;
                } else {
                    // ".." matches everything after this
                    return true;
                }
            }
            if (parameterTypes.size() == i) {
                // have run out of argument types to match
                return false;
            }
            if (!isMethodParameterTypeMatch(pointcutMethodParameterType, parameterTypes.get(i))) {
                return false;
            }
        }
        // need this final test since argumentTypes may still have unmatched elements
        return parameterTypes.size() == pointcutMethodParameterTypes.size();
    }

    private boolean isMethodReturnMatch(Type returnType) {
        String pointcutMethodReturn = advice().pointcut().methodReturnType();
        return pointcutMethodReturn.isEmpty()
                || pointcutMethodReturn.equals(returnType.getClassName());
    }

    private boolean isMethodModifiersMatch(int modifiers) {
        for (MethodModifier methodModifier : advice().pointcut().methodModifiers()) {
            if (!isMethodModifierMatch(methodModifier, modifiers)) {
                return false;
            }
        }
        return true;
    }

    private static boolean isMethodParameterTypeMatch(Object pointcutMethodParameterType,
            Type parameterType) {
        String className = parameterType.getClassName();
        if (pointcutMethodParameterType instanceof String) {
            return pointcutMethodParameterType.equals(className);
        } else {
            return ((Pattern) pointcutMethodParameterType).matcher(className).matches();
        }
    }

    private static boolean isMethodModifierMatch(MethodModifier methodModifier, int modifiers) {
        switch (methodModifier) {
            case PUBLIC:
                return Modifier.isPublic(modifiers);
            case STATIC:
                return Modifier.isStatic(modifiers);
            case NOT_STATIC:
                return !Modifier.isStatic(modifiers);
            default:
                return false;
        }
    }

    private static boolean isClassMatch(String className, List classAnnotations,
            Collection superClassNames, Advice advice) {
        if (!isAnnotationMatch(classAnnotations, advice.pointcutClassAnnotationPattern(),
                advice.pointcut().classAnnotation())) {
            return false;
        }
        if (!isClassNameMatch(className, advice)) {
            return false;
        }
        Pattern pointcutSuperTypeRestrictionPattern = advice.pointcutSuperTypeRestrictionPattern();
        if (pointcutSuperTypeRestrictionPattern != null) {
            for (String superClassName : superClassNames) {
                if (pointcutSuperTypeRestrictionPattern.matcher(superClassName).matches()) {
                    return true;
                }
            }
            return false;
        } else {
            String superTypeRestriction = advice.pointcut().superTypeRestriction();
            return superTypeRestriction.isEmpty() || superClassNames.contains(superTypeRestriction);
        }
    }

    private static boolean isClassNameMatch(String className, Advice advice) {
        Pattern classNamePattern = advice.pointcutClassNamePattern();
        if (classNamePattern != null) {
            return classNamePattern.matcher(className).matches();
        }
        String pointcutClassName = advice.pointcut().className();
        return pointcutClassName.isEmpty() || pointcutClassName.equals(className);
    }

    private static boolean isAnnotationMatch(List annotations, @Nullable Pattern pattern,
            String strictMatch) {
        for (String annotation : annotations) {
            annotation = annotation.replace('/', '.').substring(1, annotation.length() - 1);
            if (pattern != null && pattern.matcher(annotation).matches()) {
                return true;
            }
            if (annotation.equals(strictMatch)) {
                return true;
            }
        }
        return strictMatch.isEmpty();
    }
}