
org.glowroot.ui.InstrumentationConfigJsonService Maven / Gradle / Ivy
/*
* 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