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

org.glowroot.agent.init.AgentModule Maven / Gradle / Ivy

There is a newer version: 0.9.28
Show newest version
/*
 * Copyright 2011-2015 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.init;

import java.io.File;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.jar.JarFile;

import javax.annotation.Nullable;

import org.glowroot.agent.shaded.google.common.base.Joiner;
import org.glowroot.agent.shaded.google.common.base.Ticker;
import org.glowroot.agent.shaded.google.common.collect.Lists;
import org.glowroot.agent.shaded.google.common.util.concurrent.ThreadFactoryBuilder;
import org.glowroot.agent.shaded.slf4j.Logger;
import org.glowroot.agent.shaded.slf4j.LoggerFactory;

import org.glowroot.agent.api.internal.GlowrootService;
import org.glowroot.agent.config.ConfigService;
import org.glowroot.agent.config.PluginCache;
import org.glowroot.agent.impl.AdviceCache;
import org.glowroot.agent.impl.Aggregator;
import org.glowroot.agent.impl.ConfigServiceImpl;
import org.glowroot.agent.impl.GlowrootServiceImpl;
import org.glowroot.agent.impl.ServiceRegistryImpl;
import org.glowroot.agent.impl.ServiceRegistryImpl.ConfigServiceFactory;
import org.glowroot.agent.impl.TimerNameCache;
import org.glowroot.agent.impl.TransactionCollector;
import org.glowroot.agent.impl.TransactionRegistry;
import org.glowroot.agent.impl.TransactionServiceImpl;
import org.glowroot.agent.impl.UserProfileScheduler;
import org.glowroot.agent.impl.WeavingTimerServiceImpl;
import org.glowroot.agent.live.LiveAggregateRepositoryImpl;
import org.glowroot.agent.live.LiveJvmServiceImpl;
import org.glowroot.agent.live.LiveTraceRepositoryImpl;
import org.glowroot.agent.live.LiveWeavingServiceImpl;
import org.glowroot.agent.plugin.api.transaction.TransactionService;
import org.glowroot.agent.util.LazyPlatformMBeanServer;
import org.glowroot.agent.util.OptionalService;
import org.glowroot.agent.util.ThreadAllocatedBytes;
import org.glowroot.agent.util.Tickers;
import org.glowroot.agent.weaving.AnalyzedWorld;
import org.glowroot.agent.weaving.ExtraBootResourceFinder;
import org.glowroot.agent.weaving.WeavingClassFileTransformer;
import org.glowroot.agent.weaving.WeavingTimerService;
import org.glowroot.common.config.PluginDescriptor;
import org.glowroot.common.live.LiveAggregateRepository;
import org.glowroot.common.live.LiveJvmService;
import org.glowroot.common.live.LiveTraceRepository;
import org.glowroot.common.live.LiveWeavingService;
import org.glowroot.common.util.Clock;
import org.glowroot.common.util.OnlyUsedByTests;
import org.glowroot.wire.api.Collector;

import static org.glowroot.agent.shaded.google.common.base.Preconditions.checkNotNull;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;

public class AgentModule {

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

    // log startup messages using logger name "org.glowroot"
    private static final Logger startupLogger = LoggerFactory.getLogger("org.glowroot");

    // 1 minute
    private static final long ROLLUP_0_INTERVAL_MILLIS =
            Long.getLong("glowroot.internal.rollup.0.intervalMillis", 60 * 1000);

    private final PluginCache pluginCache;
    private final ConfigService configService;
    private final AnalyzedWorld analyzedWorld;
    private final TransactionRegistry transactionRegistry;
    private final AdviceCache adviceCache;
    private final WeavingTimerService weavingTimerService;

    private final TransactionCollector transactionCollector;
    private final Aggregator aggregator;

    private final ImmediateTraceStoreWatcher immedateTraceStoreWatcher;

    private final ScheduledExecutorService scheduledExecutor;
    private final Collector collector;
    private final GaugeCollector gaugeCollector;
    private final StackTraceCollector stackTraceCollector;

    private final boolean timerWrapperMethods;
    private final boolean jvmRetransformClassesSupported;

    private final ServiceRegistryImpl serviceRegistry;

    private final LiveTraceRepository liveTraceRepository;
    private final LiveAggregateRepository liveAggregateRepository;
    private final LiveWeavingService liveWeavingService;
    private final LiveJvmService liveJvmService;

    private final LazyPlatformMBeanServer lazyPlatformMBeanServer;

    // accepts @Nullable Ticker to deal with shading issues when called from GlowrootModule
    public AgentModule(Clock clock, @Nullable Ticker nullableTicker, final PluginCache pluginCache,
            final ConfigService configService, Collector collector,
            @Nullable Instrumentation instrumentation, File baseDir,
            boolean jbossModules) throws Exception {

        Ticker ticker = nullableTicker == null ? Tickers.getTicker() : nullableTicker;
        this.pluginCache = pluginCache;
        this.configService = configService;
        transactionRegistry = new TransactionRegistry();

        ExtraBootResourceFinder extraBootResourceFinder =
                createExtraBootResourceFinder(instrumentation, pluginCache.pluginJars());

        adviceCache = new AdviceCache(pluginCache.pluginDescriptors(), pluginCache.pluginJars(),
                configService.getInstrumentationConfigs(), instrumentation, baseDir);
        analyzedWorld = new AnalyzedWorld(adviceCache.getAdvisorsSupplier(),
                adviceCache.getShimTypes(), adviceCache.getMixinTypes(), extraBootResourceFinder);
        final TimerNameCache timerNameCache = new TimerNameCache();
        weavingTimerService =
                new WeavingTimerServiceImpl(transactionRegistry, configService, timerNameCache);

        ThreadFactory threadFactory = new ThreadFactoryBuilder().setDaemon(true)
                .setNameFormat("Glowroot-Background-%d").build();
        scheduledExecutor = Executors.newScheduledThreadPool(2, threadFactory);

        this.collector = collector;
        aggregator = new Aggregator(scheduledExecutor, collector, configService,
                ROLLUP_0_INTERVAL_MILLIS, clock);
        transactionCollector = new TransactionCollector(scheduledExecutor, configService, collector,
                aggregator, clock, ticker);

        timerWrapperMethods = configService.getAdvancedConfig().timerWrapperMethods();
        // instrumentation is null when debugging with IsolatedWeavingClassLoader
        // instead of javaagent
        if (instrumentation != null) {
            ClassFileTransformer transformer =
                    new WeavingClassFileTransformer(adviceCache.getShimTypes(),
                            adviceCache.getMixinTypes(), adviceCache.getAdvisorsSupplier(),
                            analyzedWorld, weavingTimerService, timerWrapperMethods);
            if (instrumentation.isRetransformClassesSupported()) {
                instrumentation.addTransformer(transformer, true);
                jvmRetransformClassesSupported = true;
            } else {
                instrumentation.addTransformer(transformer);
                jvmRetransformClassesSupported = false;
            }
        } else {
            jvmRetransformClassesSupported = false;
        }

        OptionalService threadAllocatedBytes = ThreadAllocatedBytes.create();

        immedateTraceStoreWatcher = new ImmediateTraceStoreWatcher(scheduledExecutor,
                transactionRegistry, transactionCollector, configService, ticker);
        immedateTraceStoreWatcher.scheduleWithFixedDelay(scheduledExecutor, 0,
                ImmediateTraceStoreWatcher.PERIOD_MILLIS, MILLISECONDS);
        UserProfileScheduler userProfileScheduler =
                new UserProfileScheduler(scheduledExecutor, configService);
        GlowrootService glowrootService =
                new GlowrootServiceImpl(transactionRegistry, userProfileScheduler);
        TransactionService transactionService = TransactionServiceImpl.create(transactionRegistry,
                transactionCollector, configService, timerNameCache,
                threadAllocatedBytes.getService(), userProfileScheduler, ticker, clock);
        ConfigServiceFactory configServiceFactory = new ConfigServiceFactory() {
            @Override
            public org.glowroot.agent.plugin.api.config.ConfigService create(String pluginId) {
                checkNotNull(configService);
                checkNotNull(pluginCache);
                return ConfigServiceImpl.create(configService, pluginCache.pluginDescriptors(),
                        pluginId);
            }
        };
        serviceRegistry =
                ServiceRegistryImpl.init(glowrootService, transactionService, configServiceFactory);

        lazyPlatformMBeanServer = new LazyPlatformMBeanServer(jbossModules);
        gaugeCollector = new GaugeCollector(configService, collector, lazyPlatformMBeanServer,
                clock, ticker);
        // using fixed rate to keep gauge collections close to on the second mark
        long gaugeCollectionIntervalMillis = configService.getGaugeCollectionIntervalMillis();
        long initialDelay = gaugeCollectionIntervalMillis
                - (clock.currentTimeMillis() % gaugeCollectionIntervalMillis);
        gaugeCollector.scheduleWithFixedDelay(initialDelay, gaugeCollectionIntervalMillis,
                MILLISECONDS);
        stackTraceCollector =
                StackTraceCollector.create(transactionRegistry, configService, scheduledExecutor);

        liveTraceRepository = new LiveTraceRepositoryImpl(transactionRegistry, transactionCollector,
                clock, ticker);
        liveAggregateRepository = new LiveAggregateRepositoryImpl(aggregator);
        liveWeavingService = new LiveWeavingServiceImpl(analyzedWorld, instrumentation,
                configService, adviceCache, jvmRetransformClassesSupported);
        liveJvmService = new LiveJvmServiceImpl(lazyPlatformMBeanServer, transactionRegistry,
                transactionCollector, threadAllocatedBytes.getAvailability());

        // using context class loader in LocalContainer tests so that the plugins are instantiated
        // inside org.glowroot.agent.it.harness.impl.IsolatedClassLoader
        ClassLoader initPluginClassLoader =
                instrumentation == null ? Thread.currentThread().getContextClassLoader()
                        : AgentModule.class.getClassLoader();
        initPlugins(pluginCache.pluginDescriptors(), initPluginClassLoader);

        List pluginDescriptors = pluginCache.pluginDescriptors();
        List pluginNames = Lists.newArrayList();
        for (PluginDescriptor pluginDescriptor : pluginDescriptors) {
            pluginNames.add(pluginDescriptor.name());
        }
        if (!pluginNames.isEmpty()) {
            startupLogger.info("Glowroot plugins loaded: {}", Joiner.on(", ").join(pluginNames));
        }
    }

    public ConfigService getConfigService() {
        return configService;
    }

    public AdviceCache getAdviceCache() {
        return adviceCache;
    }

    public LazyPlatformMBeanServer getLazyPlatformMBeanServer() {
        return lazyPlatformMBeanServer;
    }

    public LiveTraceRepository getLiveTraceRepository() {
        return liveTraceRepository;
    }

    public LiveAggregateRepository getLiveAggregateRepository() {
        return liveAggregateRepository;
    }

    public LiveWeavingService getLiveWeavingService() {
        return liveWeavingService;
    }

    public LiveJvmService getLiveJvmService() {
        return liveJvmService;
    }

    public WeavingTimerService getWeavingTimerService() {
        return weavingTimerService;
    }

    public List getPluginDescriptors() {
        return pluginCache.pluginDescriptors();
    }

    private static @Nullable ExtraBootResourceFinder createExtraBootResourceFinder(
            @Nullable Instrumentation instrumentation, List pluginJars) throws IOException {
        if (instrumentation == null) {
            return null;
        }
        for (File pluginJar : pluginJars) {
            instrumentation.appendToBootstrapClassLoaderSearch(new JarFile(pluginJar));
        }
        return new ExtraBootResourceFinder(pluginJars);
    }

    // now init plugins to give them a chance to do something in their static initializer
    // e.g. append their package to jboss.modules.system.pkgs
    private static void initPlugins(List pluginDescriptors,
            @Nullable ClassLoader loader) {
        for (PluginDescriptor pluginDescriptor : pluginDescriptors) {
            for (String aspect : pluginDescriptor.aspects()) {
                try {
                    Class.forName(aspect, true, loader);
                } catch (ClassNotFoundException e) {
                    // this would have already been logged as a warning during advice construction
                    logger.debug(e.getMessage(), e);
                }
            }
        }
    }

    @OnlyUsedByTests
    public void reopen() throws Exception {
        ServiceRegistryImpl.reopen(serviceRegistry);
    }

    @OnlyUsedByTests
    public void close() throws InterruptedException {
        immedateTraceStoreWatcher.cancel();
        aggregator.close();
        gaugeCollector.close();
        stackTraceCollector.close();
        scheduledExecutor.shutdown();
        if (!scheduledExecutor.awaitTermination(10, SECONDS)) {
            throw new IllegalStateException("Could not terminate agent scheduled executor");
        }
        // shut down collector last since above threads can try to use it
        collector.close();
        // finally, close logger
        LoggingInit.close();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy