Java tutorial
/* * 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.agent.central; import java.util.List; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import com.google.common.base.Stopwatch; import io.grpc.stub.StreamObserver; import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.glowroot.agent.collector.Collector.AgentConfigUpdater; import org.glowroot.agent.live.LiveJvmServiceImpl; import org.glowroot.agent.live.LiveTraceRepositoryImpl; import org.glowroot.agent.live.LiveWeavingServiceImpl; import org.glowroot.agent.util.ThreadFactories; import org.glowroot.common.live.LiveJvmService.DirectoryDoesNotExistException; import org.glowroot.common.live.LiveJvmService.UnavailableDueToRunningInJ9JvmException; import org.glowroot.common.live.LiveJvmService.UnavailableDueToRunningInJreException; import org.glowroot.common.live.LiveTraceRepository.Entries; import org.glowroot.common.live.LiveTraceRepository.Queries; import org.glowroot.common.util.OnlyUsedByTests; import org.glowroot.common.util.Throwables; import org.glowroot.wire.api.model.DownstreamServiceGrpc; import org.glowroot.wire.api.model.DownstreamServiceGrpc.DownstreamServiceStub; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.AgentConfigUpdateResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.AgentResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.AuxThreadProfileResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.AvailableDiskSpaceResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.Capabilities; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.CapabilitiesResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.CentralRequest; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.CentralRequest.MessageCase; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.CurrentTimeResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.EntriesResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.ExceptionResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.ExplicitGcDisabledResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.ForceGcResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.FullTraceResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.GlobalMeta; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.GlobalMetaResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.HeaderResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.HeapDumpFileInfo; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.HeapDumpResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.HeapHistogram; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.HeapHistogramResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.Hello; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.JstackResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.MBeanDump; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.MBeanDumpRequest; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.MBeanDumpResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.MBeanMeta; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.MBeanMetaRequest; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.MBeanMetaResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.MainThreadProfileResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.MatchingClassNamesRequest; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.MatchingClassNamesResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.MatchingMBeanObjectNamesRequest; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.MatchingMBeanObjectNamesResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.MatchingMethodNamesRequest; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.MatchingMethodNamesResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.MethodSignature; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.MethodSignaturesRequest; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.MethodSignaturesResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.PreloadClasspathCacheResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.QueriesResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.ReweaveResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.SystemPropertiesResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.ThreadDump; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.ThreadDumpResponse; import org.glowroot.wire.api.model.DownstreamServiceOuterClass.UnknownRequestResponse; import org.glowroot.wire.api.model.ProfileOuterClass.Profile; import org.glowroot.wire.api.model.TraceOuterClass.Trace; import static com.google.common.base.Preconditions.checkState; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; class DownstreamServiceObserver implements StreamObserver<CentralRequest> { private static final Logger logger = LoggerFactory.getLogger(DownstreamServiceObserver.class); private final CentralConnection centralConnection; private final DownstreamServiceStub downstreamServiceStub; private final AgentConfigUpdater agentConfigUpdater; private final boolean configReadOnly; private final LiveJvmServiceImpl liveJvmService; private final LiveWeavingServiceImpl liveWeavingService; private final LiveTraceRepositoryImpl liveTraceRepository; private final String agentId; private volatile @Nullable StreamObserver<AgentResponse> currResponseObserver; // only used by tests private volatile boolean closedByCentralCollector; private final AtomicBoolean inMaybeConnectionFailure = new AtomicBoolean(); private final AtomicBoolean inConnectionFailure; private final SharedQueryTextLimiter sharedQueryTextLimiter; private final ScheduledExecutorService scheduledRetryExecutor; DownstreamServiceObserver(CentralConnection centralConnection, AgentConfigUpdater agentConfigUpdater, boolean configReadOnly, LiveJvmServiceImpl liveJvmService, LiveWeavingServiceImpl liveWeavingService, LiveTraceRepositoryImpl liveTraceRepository, String agentId, AtomicBoolean inConnectionFailure, SharedQueryTextLimiter sharedQueryTextLimiter) { this.centralConnection = centralConnection; downstreamServiceStub = DownstreamServiceGrpc.newStub(centralConnection.getChannel()) .withCompression("gzip"); this.agentConfigUpdater = agentConfigUpdater; this.configReadOnly = configReadOnly; this.liveJvmService = liveJvmService; this.liveWeavingService = liveWeavingService; this.liveTraceRepository = liveTraceRepository; this.agentId = agentId; this.inConnectionFailure = inConnectionFailure; this.sharedQueryTextLimiter = sharedQueryTextLimiter; scheduledRetryExecutor = Executors .newSingleThreadScheduledExecutor(ThreadFactories.create("Glowroot-Downstream-Retry")); } @Override public void onNext(CentralRequest request) { inMaybeConnectionFailure.set(false); boolean errorFixed = inConnectionFailure.getAndSet(false); if (errorFixed) { centralConnection.suppressLogCollector(new Runnable() { @Override public void run() { logger.info("re-established connection to the central collector"); } }); } if (request.getMessageCase() == MessageCase.HELLO_ACK) { return; } try { onNextInternal(request); } catch (Throwable t) { logger.error(t.getMessage(), t); } } @Override @OnlyUsedByTests public void onCompleted() { closedByCentralCollector = true; } @Override public void onError(final Throwable t) { if (!inMaybeConnectionFailure.getAndSet(true)) { // one free pass // try immediate re-connect once in case this is just node of central collector cluster // going down connectAsync(); return; } if (!inConnectionFailure.getAndSet(true)) { centralConnection.suppressLogCollector(new Runnable() { @Override public void run() { logger.warn("lost connection to the central collector (will keep" + " trying to re-establish...): {}", Throwables.getBestMessage(t)); logger.debug(t.getMessage(), t); } }); } currResponseObserver = null; // TODO revisit retry/backoff after next grpc version scheduledRetryExecutor.schedule(new RetryAfterError(), 1, SECONDS); } void connectAsync() { // these are async so never fail, onError() will be called on failure StreamObserver<AgentResponse> responseObserver = downstreamServiceStub.connect(this); currResponseObserver = responseObserver; responseObserver .onNext(AgentResponse.newBuilder().setHello(Hello.newBuilder().setAgentId(agentId)).build()); } private void onNextInternal(CentralRequest request) throws InterruptedException { StreamObserver<AgentResponse> responseObserver = currResponseObserver; while (responseObserver == null) { MILLISECONDS.sleep(10); responseObserver = currResponseObserver; } switch (request.getMessageCase()) { case AGENT_CONFIG_UPDATE_REQUEST: updateConfigAndRespond(request, responseObserver); return; case THREAD_DUMP_REQUEST: threadDumpAndRespond(request, responseObserver); return; case JSTACK_REQUEST: jstackAndRespond(request, responseObserver); return; case AVAILABLE_DISK_SPACE_REQUEST: availableDiskSpaceAndRespond(request, responseObserver); return; case HEAP_DUMP_REQUEST: heapDumpAndRespond(request, responseObserver); return; case HEAP_HISTOGRAM_REQUEST: heapHistogramAndRespond(request, responseObserver); return; case EXPLICIT_GC_DISABLED_REQUEST: explicitGcDisabledAndRespond(request, responseObserver); return; case FORCE_GC_REQUEST: forceGcAndRespond(request, responseObserver); return; case MBEAN_DUMP_REQUEST: mbeanDumpAndRespond(request, responseObserver); return; case MATCHING_MBEAN_OBJECT_NAMES_REQUEST: matchingMBeanObjectNamesAndRespond(request, responseObserver); return; case MBEAN_META_REQUEST: mbeanMetaAndRespond(request, responseObserver); return; case SYSTEM_PROPERTIES_REQUEST: systemPropertiesAndRespond(request, responseObserver); return; case CURRENT_TIME_REQUEST: currentTimeAndRespond(request, responseObserver); return; case CAPABILITIES_REQUEST: capabilitiesAndRespond(request, responseObserver); return; case GLOBAL_META_REQUEST: globalMetaAndRespond(request, responseObserver); return; case PRELOAD_CLASSPATH_CACHE_REQUEST: preloadClasspathCacheAndRespond(request, responseObserver); return; case MATCHING_CLASS_NAMES_REQUEST: matchingClassNamesAndRespond(request, responseObserver); return; case MATCHING_METHOD_NAMES_REQUEST: matchingMethodNamesAndRespond(request, responseObserver); return; case METHOD_SIGNATURES_REQUEST: methodSignaturesAndRespond(request, responseObserver); return; case REWEAVE_REQUEST: reweaveAndRespond(request, responseObserver); return; case HEADER_REQUEST: getHeaderAndRespond(request, responseObserver); return; case ENTRIES_REQUEST: getEntriesAndRespond(request, responseObserver); return; case QUERIES_REQUEST: getQueriesAndRespond(request, responseObserver); return; case MAIN_THREAD_PROFILE_REQUEST: getMainThreadProfileAndRespond(request, responseObserver); return; case AUX_THREAD_PROFILE_REQUEST: getAuxThreadProfileAndRespond(request, responseObserver); return; case FULL_TRACE_REQUEST: getFullTraceAndRespond(request, responseObserver); return; default: responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setUnknownRequestResponse(UnknownRequestResponse.getDefaultInstance()).build()); return; } } private void updateConfigAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { if (configReadOnly) { // the central collector should observe the InitMessage AgentConfig's config_read_only // and not even send this request logger.error("central collector attempted to update agent configuration, but the agent" + " is running with config.readOnly=true"); sendExceptionResponse(request, responseObserver); return; } try { agentConfigUpdater.update(request.getAgentConfigUpdateRequest().getAgentConfig()); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setAgentConfigUpdateResponse(AgentConfigUpdateResponse.getDefaultInstance()).build()); } private void threadDumpAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { ThreadDump threadDump; try { threadDump = liveJvmService.getThreadDump(""); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setThreadDumpResponse(ThreadDumpResponse.newBuilder().setThreadDump(threadDump)).build()); } private void jstackAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { String jstack; try { jstack = liveJvmService.getJstack(""); } catch (UnavailableDueToRunningInJreException e) { logger.debug(e.getMessage(), e); responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setJstackResponse(JstackResponse.newBuilder().setUnavailableDueToRunningInJre(true)).build()); return; } catch (UnavailableDueToRunningInJ9JvmException e) { // Eclipse OpenJ9 VM or IBM J9 VM logger.debug(e.getMessage(), e); responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setJstackResponse(JstackResponse.newBuilder().setUnavailableDueToRunningInJ9Jvm(true)) .build()); return; } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setJstackResponse(JstackResponse.newBuilder().setJstack(jstack)).build()); } private void availableDiskSpaceAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { long availableDiskSpaceBytes; try { availableDiskSpaceBytes = liveJvmService.getAvailableDiskSpace("", request.getAvailableDiskSpaceRequest().getDirectory()); } catch (DirectoryDoesNotExistException e) { logger.debug(e.getMessage(), e); responseObserver .onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setAvailableDiskSpaceResponse( AvailableDiskSpaceResponse.newBuilder().setDirectoryDoesNotExist(true)) .build()); return; } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setAvailableDiskSpaceResponse( AvailableDiskSpaceResponse.newBuilder().setAvailableBytes(availableDiskSpaceBytes)) .build()); } private void heapDumpAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { HeapDumpFileInfo heapDumpFileInfo; try { heapDumpFileInfo = liveJvmService.heapDump("", request.getHeapDumpRequest().getDirectory()); } catch (DirectoryDoesNotExistException e) { logger.debug(e.getMessage(), e); responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setHeapDumpResponse(HeapDumpResponse.newBuilder().setDirectoryDoesNotExist(true)).build()); return; } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setHeapDumpResponse(HeapDumpResponse.newBuilder().setHeapDumpFileInfo(heapDumpFileInfo)).build()); } private void heapHistogramAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { HeapHistogram heapHistogram; try { heapHistogram = liveJvmService.heapHistogram(""); } catch (UnavailableDueToRunningInJreException e) { logger.debug(e.getMessage(), e); responseObserver .onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setHeapHistogramResponse( HeapHistogramResponse.newBuilder().setUnavailableDueToRunningInJre(true)) .build()); return; } catch (UnavailableDueToRunningInJ9JvmException e) { // Eclipse OpenJ9 VM or IBM J9 VM logger.debug(e.getMessage(), e); responseObserver .onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setHeapHistogramResponse( HeapHistogramResponse.newBuilder().setUnavailableDueToRunningInJ9Jvm(true)) .build()); return; } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setHeapHistogramResponse(HeapHistogramResponse.newBuilder().setHeapHistogram(heapHistogram)) .build()); } private void explicitGcDisabledAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { boolean disabled; try { disabled = liveJvmService.isExplicitGcDisabled(""); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setExplicitGcDisabledResponse(ExplicitGcDisabledResponse.newBuilder().setDisabled(disabled)) .build()); } private void forceGcAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { try { liveJvmService.forceGC(""); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setForceGcResponse(ForceGcResponse.getDefaultInstance()).build()); } private void mbeanDumpAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { MBeanDumpRequest req = request.getMbeanDumpRequest(); MBeanDump mbeanDump; try { mbeanDump = liveJvmService.getMBeanDump("", req.getKind(), req.getObjectNameList()); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setMbeanDumpResponse(MBeanDumpResponse.newBuilder().setMbeanDump(mbeanDump)).build()); } private void matchingMBeanObjectNamesAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { MatchingMBeanObjectNamesRequest req = request.getMatchingMbeanObjectNamesRequest(); List<String> objectNames; try { objectNames = liveJvmService.getMatchingMBeanObjectNames("", req.getPartialObjectName(), req.getLimit()); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver .onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setMatchingMbeanObjectNamesResponse( MatchingMBeanObjectNamesResponse.newBuilder().addAllObjectName(objectNames)) .build()); } private void mbeanMetaAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { MBeanMetaRequest req = request.getMbeanMetaRequest(); MBeanMeta mbeanMeta; try { mbeanMeta = liveJvmService.getMBeanMeta("", req.getObjectName()); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setMbeanMetaResponse(MBeanMetaResponse.newBuilder().setMbeanMeta(mbeanMeta)).build()); } private void systemPropertiesAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { Map<String, String> systemProperties; try { systemProperties = liveJvmService.getSystemProperties(""); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver .onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setSystemPropertiesResponse( SystemPropertiesResponse.newBuilder().putAllSystemProperties(systemProperties)) .build()); } private void currentTimeAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { long currentTime; try { currentTime = liveJvmService.getCurrentTime(""); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setCurrentTimeResponse(CurrentTimeResponse.newBuilder().setCurrentTimeMillis(currentTime)) .build()); } private void capabilitiesAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { Capabilities capabilities; try { capabilities = liveJvmService.getCapabilities(""); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setCapabilitiesResponse(CapabilitiesResponse.newBuilder().setCapabilities(capabilities)).build()); } private void globalMetaAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { GlobalMeta globalMeta; try { globalMeta = liveWeavingService.getGlobalMeta(""); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setGlobalMetaResponse(GlobalMetaResponse.newBuilder().setGlobalMeta(globalMeta)).build()); } private void preloadClasspathCacheAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { try { liveWeavingService.preloadClasspathCache(""); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setPreloadClasspathCacheResponse(PreloadClasspathCacheResponse.getDefaultInstance()).build()); } private void matchingClassNamesAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { MatchingClassNamesRequest req = request.getMatchingClassNamesRequest(); List<String> classNames; try { classNames = liveWeavingService.getMatchingClassNames("", req.getPartialClassName(), req.getLimit()); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setMatchingClassNamesResponse(MatchingClassNamesResponse.newBuilder().addAllClassName(classNames)) .build()); } private void matchingMethodNamesAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { MatchingMethodNamesRequest req = request.getMatchingMethodNamesRequest(); List<String> methodNames; try { methodNames = liveWeavingService.getMatchingMethodNames("", req.getClassName(), req.getPartialMethodName(), req.getLimit()); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver.onNext( AgentResponse.newBuilder().setRequestId(request.getRequestId()).setMatchingMethodNamesResponse( MatchingMethodNamesResponse.newBuilder().addAllMethodName(methodNames)).build()); } private void methodSignaturesAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { MethodSignaturesRequest req = request.getMethodSignaturesRequest(); List<MethodSignature> methodSignatures; try { methodSignatures = liveWeavingService.getMethodSignatures("", req.getClassName(), req.getMethodName()); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver .onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setMethodSignaturesResponse( MethodSignaturesResponse.newBuilder().addAllMethodSignature(methodSignatures)) .build()); } private void reweaveAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { int classUpdateCount; try { classUpdateCount = liveWeavingService.reweave(""); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setReweaveResponse(ReweaveResponse.newBuilder().setClassUpdateCount(classUpdateCount)).build()); } private void getHeaderAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { Trace.Header header; try { header = liveTraceRepository.getHeader("", request.getHeaderRequest().getTraceId()); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } HeaderResponse response; if (header == null) { response = HeaderResponse.getDefaultInstance(); } else { response = HeaderResponse.newBuilder().setHeader(header).build(); } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setHeaderResponse(response).build()); } private void getEntriesAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { Entries entries; try { entries = liveTraceRepository.getEntries("", request.getEntriesRequest().getTraceId()); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } EntriesResponse.Builder response = EntriesResponse.newBuilder(); if (entries != null) { response.addAllEntry(entries.entries()); response.addAllSharedQueryText( sharedQueryTextLimiter.reduceTracePayloadWherePossible(entries.sharedQueryTexts())); } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setEntriesResponse(response).build()); } private void getQueriesAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { Queries queries; try { queries = liveTraceRepository.getQueries("", request.getQueriesRequest().getTraceId()); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } QueriesResponse.Builder response = QueriesResponse.newBuilder(); if (queries != null) { response.addAllQuery(queries.queries()); response.addAllSharedQueryText( sharedQueryTextLimiter.reduceTracePayloadWherePossible(queries.sharedQueryTexts())); } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setQueriesResponse(response).build()); } private void getMainThreadProfileAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { Profile profile; try { profile = liveTraceRepository.getMainThreadProfile("", request.getMainThreadProfileRequest().getTraceId()); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } MainThreadProfileResponse response; if (profile == null) { response = MainThreadProfileResponse.getDefaultInstance(); } else { response = MainThreadProfileResponse.newBuilder().setProfile(profile).build(); } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setMainThreadProfileResponse(response).build()); } private void getAuxThreadProfileAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { Profile profile; try { profile = liveTraceRepository.getAuxThreadProfile("", request.getAuxThreadProfileRequest().getTraceId()); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } AuxThreadProfileResponse response; if (profile == null) { response = AuxThreadProfileResponse.getDefaultInstance(); } else { response = AuxThreadProfileResponse.newBuilder().setProfile(profile).build(); } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setAuxThreadProfileResponse(response).build()); } private void getFullTraceAndRespond(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { Trace trace; try { trace = liveTraceRepository.getFullTrace("", request.getFullTraceRequest().getTraceId()); } catch (Exception e) { logger.error(e.getMessage(), e); sendExceptionResponse(request, responseObserver); return; } FullTraceResponse response; if (trace == null) { response = FullTraceResponse.getDefaultInstance(); } else { response = FullTraceResponse.newBuilder().setTrace(trace).build(); } responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setFullTraceResponse(response).build()); } @OnlyUsedByTests void close() throws InterruptedException { StreamObserver<AgentResponse> responseObserver = currResponseObserver; while (responseObserver == null) { MILLISECONDS.sleep(10); responseObserver = currResponseObserver; } responseObserver.onCompleted(); Stopwatch stopwatch = Stopwatch.createStarted(); while (stopwatch.elapsed(SECONDS) < 10 && !closedByCentralCollector) { MILLISECONDS.sleep(10); } checkState(closedByCentralCollector); } private static void sendExceptionResponse(CentralRequest request, StreamObserver<AgentResponse> responseObserver) { responseObserver.onNext(AgentResponse.newBuilder().setRequestId(request.getRequestId()) .setExceptionResponse(ExceptionResponse.getDefaultInstance()).build()); } private class RetryAfterError implements Runnable { @Override public void run() { try { connectAsync(); } catch (Throwable t) { logger.error(t.getMessage(), t); } } } }