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

io.opentelemetry.sdk.autoconfigure.internal.SpiHelper Maven / Gradle / Ivy

There is a newer version: 1.44.1
Show newest version
/*
 * Copyright The OpenTelemetry Authors
 * SPDX-License-Identifier: Apache-2.0
 */

package io.opentelemetry.sdk.autoconfigure.internal;

import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.Ordered;
import io.opentelemetry.sdk.autoconfigure.spi.internal.AutoConfigureListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * This class is internal and is hence not for public use. Its APIs are unstable and can change at
 * any time.
 */
public final class SpiHelper {

  private final ClassLoader classLoader;
  private final SpiFinder spiFinder;
  private final Set listeners =
      Collections.newSetFromMap(new IdentityHashMap<>());

  // Visible for testing
  SpiHelper(ClassLoader classLoader, SpiFinder spiFinder) {
    this.classLoader = classLoader;
    this.spiFinder = spiFinder;
  }

  /** Create a {@link SpiHelper} which loads SPIs using the {@code classLoader}. */
  public static SpiHelper create(ClassLoader classLoader) {
    return new SpiHelper(classLoader, ServiceLoader::load);
  }

  /**
   * Load implementations of an SPI which are configurable (i.e. they accept {@link
   * ConfigProperties}.
   *
   * @param spiClass the SPI class
   * @param getName function returning the name of an SPI implementation
   * @param getConfigurable function returning a configured instance
   * @param config the configuration to pass to invocations of {@code #getConfigurable}
   * @param  the configurable type
   * @param  the SPI type
   * @return a {@link NamedSpiManager} used to access configured instances of the SPI by name
   */
  public  NamedSpiManager loadConfigurable(
      Class spiClass,
      Function getName,
      BiFunction getConfigurable,
      ConfigProperties config) {
    Map> nameToProvider = new HashMap<>();
    for (S provider : load(spiClass)) {
      String name = getName.apply(provider);
      nameToProvider.put(
          name,
          () -> {
            T result = getConfigurable.apply(provider, config);
            maybeAddListener(result);
            return result;
          });
    }
    return NamedSpiManager.create(nameToProvider);
  }

  /**
   * Load implementations of an ordered SPI (i.e. implements {@link Ordered}).
   *
   * @param spiClass the SPI class
   * @param  the SPI type
   * @return list of SPI implementations, in order
   */
  public  List loadOrdered(Class spiClass) {
    List result = load(spiClass);
    result.sort(Comparator.comparing(Ordered::order));
    return result;
  }

  /**
   * Load implementations of an SPI.
   *
   * @param spiClass the SPI class
   * @param  the SPI type
   * @return list of SPI implementations
   */
  public  List load(Class spiClass) {
    List result = new ArrayList<>();
    for (T service : spiFinder.load(spiClass, classLoader)) {
      maybeAddListener(service);
      result.add(service);
    }
    return result;
  }

  private void maybeAddListener(Object object) {
    if (object instanceof AutoConfigureListener) {
      listeners.add((AutoConfigureListener) object);
    }
  }

  /** Return the set of SPIs loaded which implement {@link AutoConfigureListener}. */
  public Set getListeners() {
    return Collections.unmodifiableSet(listeners);
  }

  // Visible for testing
  interface SpiFinder {
     Iterable load(Class spiClass, ClassLoader classLoader);
  }
}