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

org.glowroot.ui.InstrumentationConfigJsonService Maven / Gradle / Ivy

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

import java.io.IOException;
import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;

import org.glowroot.agent.shaded.fasterxml.jackson.annotation.JsonInclude;
import org.glowroot.agent.shaded.fasterxml.jackson.annotation.JsonInclude.Include;
import org.glowroot.agent.shaded.fasterxml.jackson.core.JsonProcessingException;
import org.glowroot.agent.shaded.fasterxml.jackson.databind.ObjectMapper;
import org.glowroot.agent.shaded.google.common.annotations.VisibleForTesting;
import org.glowroot.agent.shaded.google.common.base.Optional;
import org.glowroot.agent.shaded.google.common.collect.ImmutableList;
import org.glowroot.agent.shaded.google.common.collect.Lists;
import org.glowroot.agent.shaded.google.common.collect.Ordering;
import org.glowroot.agent.shaded.google.common.primitives.Ints;
import org.glowroot.agent.shaded.netty.handler.codec.http.HttpResponseStatus;
import org.immutables.value.Value;

import org.glowroot.common.config.ImmutableInstrumentationConfig;
import org.glowroot.common.config.InstrumentationConfig;
import org.glowroot.common.config.InstrumentationConfig.CaptureKind;
import org.glowroot.common.config.InstrumentationConfig.MethodModifier;
import org.glowroot.common.live.LiveWeavingService;
import org.glowroot.common.live.LiveWeavingService.GlobalMeta;
import org.glowroot.common.live.LiveWeavingService.MethodSignature;
import org.glowroot.common.util.ObjectMappers;
import org.glowroot.storage.repo.ConfigRepository;

import static org.glowroot.agent.shaded.google.common.base.Preconditions.checkNotNull;

@JsonService
class InstrumentationConfigJsonService {

    private static final String SERVER_ID = "";

    private static final ObjectMapper mapper = ObjectMappers.create();

    private static final Ordering ordering =
            new InstrumentationConfigOrdering();

    private final ConfigRepository configRepository;
    private final LiveWeavingService liveWeavingService;

    InstrumentationConfigJsonService(ConfigRepository configRepository,
            LiveWeavingService liveWeavingService) {
        this.configRepository = configRepository;
        this.liveWeavingService = liveWeavingService;
    }

    @GET("/backend/config/instrumentation")
    String getInstrumentationConfig(String queryString) throws Exception {
        InstrumentationConfigRequest request =
                QueryStrings.decode(queryString, InstrumentationConfigRequest.class);
        Optional version = request.version();
        if (version.isPresent()) {
            return getInstrumentationConfigInternal(version.get());
        } else {
            List configs =
                    configRepository.getInstrumentationConfigs(SERVER_ID);
            configs = ordering.immutableSortedCopy(configs);
            List dtos = Lists.newArrayList();
            for (InstrumentationConfig config : configs) {
                dtos.add(InstrumentationConfigDto.fromConfig(config));
            }
            GlobalMeta globalMeta = liveWeavingService.getGlobalMeta(SERVER_ID);
            return mapper.writeValueAsString(ImmutableInstrumentationListResponse.builder()
                    .addAllConfigs(dtos)
                    .jvmOutOfSync(globalMeta.jvmOutOfSync())
                    .jvmRetransformClassesSupported(globalMeta.jvmRetransformClassesSupported())
                    .build());
        }
    }

    // this is marked as @GET so it can be used without update rights (e.g. demo instance)
    @GET("/backend/config/preload-classpath-cache")
    void preloadClasspathCache() throws Exception {
        // HttpServer is configured with a very small thread pool to keep number of threads down
        // (currently only a single thread), so spawn a background thread to perform the preloading
        // so it doesn't block other http requests
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                liveWeavingService.preloadClasspathCache(SERVER_ID);
            }
        });
        thread.setDaemon(true);
        thread.setName("Glowroot-Temporary-Thread");
        thread.start();
    }

    @GET("/backend/config/matching-class-names")
    String getMatchingClassNames(String queryString) throws Exception {
        ClassNamesRequest request = QueryStrings.decode(queryString, ClassNamesRequest.class);
        return mapper.writeValueAsString(liveWeavingService.getMatchingClassNames(SERVER_ID,
                request.partialClassName(), request.limit()));
    }

    @GET("/backend/config/matching-method-names")
    String getMatchingMethodNames(String queryString) throws Exception {
        MethodNamesRequest request = QueryStrings.decode(queryString, MethodNamesRequest.class);
        List matchingMethodNames = liveWeavingService.getMatchingMethodNames(SERVER_ID,
                request.className(), request.partialMethodName(), request.limit());
        return mapper.writeValueAsString(matchingMethodNames);
    }

    @GET("/backend/config/method-signatures")
    String getMethodSignatures(String queryString) throws Exception {
        MethodSignaturesRequest request =
                QueryStrings.decode(queryString, MethodSignaturesRequest.class);
        List methodSignatures = liveWeavingService.getMethodSignatures(SERVER_ID,
                request.className(), request.methodName());
        return mapper.writeValueAsString(methodSignatures);
    }

    @POST("/backend/config/instrumentation/add")
    String addInstrumentationConfig(String content) throws Exception {
        InstrumentationConfigDto configDto =
                mapper.readValue(content, ImmutableInstrumentationConfigDto.class);
        InstrumentationConfig config = configDto.toConfig();
        ImmutableList errors = config.validationErrors();
        if (!errors.isEmpty()) {
            return mapper.writeValueAsString(
                    ImmutableInstrumentationErrorResponse.builder().addAllErrors(errors).build());
        }
        configRepository.insertInstrumentationConfig(SERVER_ID, config);
        return getInstrumentationConfigInternal(config.version());
    }

    @POST("/backend/config/instrumentation/update")
    String updateInstrumentationConfig(String content) throws Exception {
        InstrumentationConfigDto configDto =
                mapper.readValue(content, ImmutableInstrumentationConfigDto.class);
        InstrumentationConfig config = configDto.toConfig();
        String version = configDto.version();
        checkNotNull(version, "Missing required request property: version");
        configRepository.updateInstrumentationConfig(SERVER_ID, config, version);
        return getInstrumentationConfigInternal(config.version());
    }

    @POST("/backend/config/instrumentation/remove")
    void removeInstrumentationConfig(String content) throws IOException {
        InstrumentationConfigRequest request =
                mapper.readValue(content, ImmutableInstrumentationConfigRequest.class);
        configRepository.deleteInstrumentationConfig(SERVER_ID, request.version().get());
    }

    private String getInstrumentationConfigInternal(String version) throws JsonProcessingException {
        InstrumentationConfig config =
                configRepository.getInstrumentationConfig(SERVER_ID, version);
        if (config == null) {
            throw new JsonServiceException(HttpResponseStatus.NOT_FOUND);
        }
        List methodSignatures = liveWeavingService.getMethodSignatures(SERVER_ID,
                config.className(), config.methodName());
        return mapper.writeValueAsString(ImmutableInstrumentationConfigResponse.builder()
                .config(InstrumentationConfigDto.fromConfig(config))
                .addAllMethodSignatures(methodSignatures)
                .build());
    }

    @Value.Immutable
    interface InstrumentationConfigRequest {
        Optional version();
    }

    @Value.Immutable
    interface ClassNamesRequest {
        String partialClassName();
        int limit();
    }

    @Value.Immutable
    interface MethodNamesRequest {
        String className();
        String partialMethodName();
        int limit();
    }

    @Value.Immutable
    interface MethodSignaturesRequest {
        String className();
        String methodName();
    }

    @Value.Immutable
    interface InstrumentationListResponse {
        ImmutableList configs();
        boolean jvmOutOfSync();
        boolean jvmRetransformClassesSupported();
    }

    @Value.Immutable
    interface InstrumentationConfigResponse {
        InstrumentationConfigDto config();
        ImmutableList methodSignatures();
    }

    @Value.Immutable
    interface InstrumentationErrorResponse {
        abstract ImmutableList errors();
    }

    @Value.Immutable
    @JsonInclude(value = Include.ALWAYS)
    abstract static class InstrumentationConfigDto {

        abstract String className();
        abstract String declaringClassName();
        abstract String methodName();
        abstract ImmutableList methodParameterTypes();
        abstract String methodReturnType();
        abstract ImmutableList methodModifiers();
        abstract CaptureKind captureKind();
        abstract String timerName();
        abstract String traceEntryMessageTemplate();
        abstract @Nullable Integer traceEntryStackThresholdMillis();
        abstract boolean traceEntryCaptureSelfNested();
        abstract String transactionType();
        abstract String transactionNameTemplate();
        abstract String transactionUserTemplate();
        abstract Map transactionAttributeTemplates();
        abstract @Nullable Integer transactionSlowThresholdMillis();
        abstract String enabledProperty();
        abstract String traceEntryEnabledProperty();
        abstract @Nullable String version(); // absent for insert operations

        private static InstrumentationConfigDto fromConfig(InstrumentationConfig config) {
            return ImmutableInstrumentationConfigDto.builder()
                    .className(config.className())
                    .declaringClassName(config.declaringClassName())
                    .methodName(config.methodName())
                    .addAllMethodParameterTypes(config.methodParameterTypes())
                    .methodReturnType(config.methodReturnType())
                    .addAllMethodModifiers(config.methodModifiers())
                    .captureKind(config.captureKind())
                    .timerName(config.timerName())
                    .traceEntryMessageTemplate(config.traceEntryMessageTemplate())
                    .traceEntryStackThresholdMillis(config.traceEntryStackThresholdMillis())
                    .traceEntryCaptureSelfNested(config.traceEntryCaptureSelfNested())
                    .transactionType(config.transactionType())
                    .transactionNameTemplate(config.transactionNameTemplate())
                    .transactionUserTemplate(config.transactionUserTemplate())
                    .putAllTransactionAttributeTemplates(config.transactionAttributeTemplates())
                    .transactionSlowThresholdMillis(config.transactionSlowThresholdMillis())
                    .enabledProperty(config.enabledProperty())
                    .traceEntryEnabledProperty(config.traceEntryEnabledProperty())
                    .version(config.version())
                    .build();
        }

        private InstrumentationConfig toConfig() {
            return ImmutableInstrumentationConfig.builder()
                    .className(className())
                    .declaringClassName(declaringClassName())
                    .methodName(methodName())
                    .addAllMethodParameterTypes(methodParameterTypes())
                    .methodReturnType(methodReturnType())
                    .addAllMethodModifiers(methodModifiers())
                    .captureKind(captureKind())
                    .timerName(timerName())
                    .traceEntryMessageTemplate(traceEntryMessageTemplate())
                    .traceEntryStackThresholdMillis(traceEntryStackThresholdMillis())
                    .traceEntryCaptureSelfNested(traceEntryCaptureSelfNested())
                    .transactionType(transactionType())
                    .transactionNameTemplate(transactionNameTemplate())
                    .transactionUserTemplate(transactionUserTemplate())
                    .putAllTransactionAttributeTemplates(transactionAttributeTemplates())
                    .transactionSlowThresholdMillis(transactionSlowThresholdMillis())
                    .enabledProperty(enabledProperty())
                    .traceEntryEnabledProperty(traceEntryEnabledProperty())
                    .build();
        }
    }

    @VisibleForTesting
    static class InstrumentationConfigOrdering extends Ordering {
        @Override
        public int compare(InstrumentationConfig left, InstrumentationConfig right) {
            int compare = left.className().compareToIgnoreCase(right.className());
            if (compare != 0) {
                return compare;
            }
            compare = left.methodName().compareToIgnoreCase(right.methodName());
            if (compare != 0) {
                return compare;
            }
            compare = Ints.compare(left.methodParameterTypes().size(),
                    right.methodParameterTypes().size());
            if (compare != 0) {
                return compare;
            }
            List leftParameterTypes = left.methodParameterTypes();
            List rightParameterTypes = right.methodParameterTypes();
            for (int i = 0; i < leftParameterTypes.size(); i++) {
                compare = leftParameterTypes.get(i).compareToIgnoreCase(rightParameterTypes.get(i));
                if (compare != 0) {
                    return compare;
                }
            }
            return 0;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy