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

org.glowroot.instrumentation.engine.weaving.Reweaving Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2015-2019 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.instrumentation.engine.weaving;

import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import org.glowroot.instrumentation.test.harness.shaded.com.google.common.collect.ArrayListMultimap;
import org.glowroot.instrumentation.test.harness.shaded.com.google.common.collect.Lists;
import org.glowroot.instrumentation.test.harness.shaded.com.google.common.collect.Multimap;
import org.glowroot.instrumentation.test.harness.shaded.com.google.common.collect.Sets;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.immutables.value.Value;
import org.glowroot.instrumentation.test.harness.shaded.org.slf4j.Logger;
import org.glowroot.instrumentation.test.harness.shaded.org.slf4j.LoggerFactory;

import static org.glowroot.instrumentation.test.harness.shaded.com.google.common.base.Preconditions.checkNotNull;

public class Reweaving {

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

    private Reweaving() {}

    public static void initialReweave(Set pointcutClassNames,
            Class[] initialLoadedClasses, Instrumentation instrumentation,
            List doNotWeavePrefixes) {
        if (!instrumentation.isRetransformClassesSupported()) {
            return;
        }
        Set> classes = getExistingModifiableSubClasses(pointcutClassNames,
                initialLoadedClasses, instrumentation, doNotWeavePrefixes);
        for (Class clazz : classes) {
            if (clazz.isInterface()) {
                continue;
            }
            try {
                instrumentation.retransformClasses(clazz);
            } catch (UnmodifiableClassException e) {
                // IBM J9 VM Java 6 throws UnmodifiableClassException even though call to
                // isModifiableClass() in getExistingModifiableSubClasses() returns true
                logger.debug(e.getMessage(), e);
            }
        }
    }

    public static Set> getExistingModifiableSubClasses(
            Set pointcutClassNames, Class[] classes,
            Instrumentation instrumentation, List doNotWeavePrefixes) {
        List> matchingClasses = Lists.newArrayList();
        Multimap, Class> subClasses = ArrayListMultimap.create();
        for (Class clazz : classes) {
            String className = clazz.getName();
            if (ignoreClass(className, doNotWeavePrefixes)) {
                continue;
            }
            if (!instrumentation.isModifiableClass(clazz)) {
                continue;
            }
            Class superclass = clazz.getSuperclass();
            if (superclass != null) {
                subClasses.put(superclass, clazz);
            }
            for (Class iface : clazz.getInterfaces()) {
                subClasses.put(iface, clazz);
            }
            for (PointcutClassName pointcutClassName : pointcutClassNames) {
                if (pointcutClassName.appliesTo(clazz.getName())) {
                    matchingClasses.add(clazz);
                    break;
                }
            }
        }
        Set> matchingSubClasses = Sets.newHashSet();
        for (Class matchingClass : matchingClasses) {
            addToMatchingSubClasses(matchingClass, matchingSubClasses, subClasses);
        }
        return matchingSubClasses;
    }

    private static void addToMatchingSubClasses(Class clazz, Set> matchingSubClasses,
            Multimap, Class> subClasses) {
        matchingSubClasses.add(clazz);
        for (Class subClass : subClasses.get(clazz)) {
            addToMatchingSubClasses(subClass, matchingSubClasses, subClasses);
        }
    }

    private static boolean ignoreClass(String className, List doNotWeavePrefixes) {
        if (isEngineClass(className)) {
            return true;
        }
        for (String doNotWeavePrefix : doNotWeavePrefixes) {
            if (className.startsWith(doNotWeavePrefix)) {
                return true;
            }
        }
        return false;
    }

    private static boolean isEngineClass(String className) {
        return className.startsWith("org.glowroot.instrumentation.engine.")
                || className.startsWith("org.glowroot.instrumentation.api.");
    }

    @Value.Immutable
    public abstract static class PointcutClassName {

        abstract @Nullable Pattern pattern();

        abstract @Nullable String nonPattern();

        abstract @Nullable PointcutClassName subTypeRestriction();

        abstract boolean doNotMatchSubClasses();

        public static PointcutClassName fromMaybePattern(String maybePattern,
                @Nullable PointcutClassName subTypeRestriction, boolean doNotMatchSubClasses) {
            Pattern pattern = MaybePatterns.buildPattern(maybePattern);
            if (pattern == null) {
                return fromNonPattern(maybePattern, subTypeRestriction, doNotMatchSubClasses);
            } else {
                return fromPattern(pattern, subTypeRestriction, doNotMatchSubClasses);
            }
        }

        public static PointcutClassName fromPattern(Pattern pattern,
                @Nullable PointcutClassName subTypeRestrictionPointcutClassName,
                boolean doNotMatchSubClasses) {
            return ImmutablePointcutClassName.builder()
                    .pattern(pattern)
                    .nonPattern(null)
                    .subTypeRestriction(subTypeRestrictionPointcutClassName)
                    .doNotMatchSubClasses(doNotMatchSubClasses)
                    .build();
        }

        public static PointcutClassName fromNonPattern(String nonPattern,
                @Nullable PointcutClassName subTypeRestrictionPointcutClassName,
                boolean doNotMatchSubClasses) {
            return ImmutablePointcutClassName.builder()
                    .pattern(null)
                    .nonPattern(nonPattern)
                    .subTypeRestriction(subTypeRestrictionPointcutClassName)
                    .doNotMatchSubClasses(doNotMatchSubClasses)
                    .build();
        }

        private boolean appliesTo(String className) {
            PointcutClassName subTypeRestriction = subTypeRestriction();
            if (subTypeRestriction != null && !subTypeRestriction.appliesTo(className)) {
                return false;
            }
            Pattern pattern = pattern();
            if (pattern != null) {
                return pattern.matcher(className).matches();
            } else {
                return checkNotNull(nonPattern()).equals(className);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy