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

com.newrelic.jfr.daemon.app.RemoteEntityBridge Maven / Gradle / Ivy

There is a newer version: 1.13.0
Show newest version
/*
 *
 *  * Copyright 2020 New Relic Corporation. All rights reserved.
 *  * SPDX-License-Identifier: Apache-2.0
 *
 */

package com.newrelic.jfr.daemon.app;

import static com.newrelic.jfr.daemon.app.MBeanConnectionFactory.waitForeverBackoff;

import com.newrelic.jfr.daemon.AttributeNames;
import com.newrelic.jfr.daemon.SafeSleep;
import com.newrelic.telemetry.Backoff;
import java.time.Duration;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import javax.management.JMX;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Attempts to locate an entity guid string from a remote MBean. This guid can be used to link jfr
 * daemon data to an external entity.
 */
public class RemoteEntityBridge {

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

  public RemoteEntityBridge() {}

  /**
   * Asynchronously fetch the remote entity id using the {@code connection}. If no remote agent is
   * detected, returns empty. If a remote agent is available, it periodically check if the {@code
   * LinkingMetadata} MBean is available. When it becomes available, it will return the {@link
   * AttributeNames#ENTITY_GUID} property.
   *
   * @param connection the connection to the remote server
   * @return a completable future resolving the remote entity id
   */
  public CompletableFuture> fetchRemoteEntityIdAsync(
      MBeanServerConnection connection) {
    return CompletableFuture.supplyAsync(
        () -> awaitRemoteEntityId(connection, waitForeverBackoff()));
  }

  Optional awaitRemoteEntityId(MBeanServerConnection connection, Backoff backoff) {
    while (true) {
      Optional optGuid;
      try {
        optGuid = getRemoteEntityGuid(connection);
      } catch (Exception e) {
        logger.info("Unable to identify remote agent. Not setting entity guid.");
        return Optional.empty();
      }
      if (optGuid.isPresent()) {
        logger.info("Obtained entity guid from remote agent: {}", optGuid.get());
        return optGuid;
      }
      long backoffMillis = backoff.nextWaitMs();
      if (backoffMillis == -1) {
        logger.info(
            "Remote agent identified but unable to obtain guid after backing off. Not setting entity guid.");
        return Optional.empty();
      }
      logger.info(
          "Remote agent identified but entity guid not yet available. Backing off {} millis.",
          backoffMillis);
      SafeSleep.sleep(Duration.ofMillis(backoffMillis));
    }
  }

  Optional getRemoteEntityGuid(MBeanServerConnection connection)
      throws MalformedObjectNameException {
    final Map metadata = getLinkingMetadata(connection);
    return Optional.ofNullable(metadata.get(AttributeNames.ENTITY_GUID));
  }

  /**
   * Get the {@code entity.name} attribute from the remote agent's linking metadata. This method is
   * only called after establishing that the remote agent's linking metadata exists by first
   * retrieving the {@code entity.guid} attribute.
   *
   * @param connection MBeanServerConnection
   * @return an Optional that may contain the remote agent's {@code entity.name} attribute
   */
  public Optional getRemoteEntityName(MBeanServerConnection connection) {
    final Map metadata;
    try {
      metadata = getLinkingMetadata(connection);
    } catch (Exception e) {
      logger.info("Unable to identify remote agent. Not setting entity name from agent.");
      return Optional.empty();
    }
    final Optional entityNameOptional =
        Optional.ofNullable(metadata.get(AttributeNames.ENTITY_NAME));
    entityNameOptional.ifPresent(s -> logger.info("Obtained entity name from remote agent: {}", s));
    return entityNameOptional;
  }

  Map getLinkingMetadata(MBeanServerConnection connection)
      throws MalformedObjectNameException {
    ObjectName name = new ObjectName("com.newrelic.jfr:type=LinkingMetadata");
    LinkingMetadataMBean linkingMetadataMBean =
        JMX.newMBeanProxy(connection, name, LinkingMetadataMBean.class);
    return linkingMetadataMBean.readLinkingMetadata();
  }

  // NOTE: this must be public or NotCompliantMBeanException will be thrown
  public interface LinkingMetadataMBean {
    Map readLinkingMetadata();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy