content: /*
   * Copyright The OpenTelemetry Authors
   * SPDX-License-Identifier: Apache-2.0
   */
  
  package io.opentelemetry.javaagent.bootstrap.servlet;
  
  import io.opentelemetry.context.Context;
  import io.opentelemetry.context.ContextKey;
  import java.util.function.Function;
  
  /**
   * The context key here is used to propagate the servlet context path throughout the request, so
   * that routing framework instrumentation that updates the span name with a more specific route can
   * prepend the servlet context path in front of that route.
   *
   * <p>This needs to be in the instrumentation-api module, instead of injected as a helper class into
   * the different modules that need it, in order to make sure that there is only a single instance of
   * the context key, since otherwise instrumentation across different class loaders would use
   * different context keys and not be able to share the servlet context path.
   */
  public final class ServletContextPath {
  
    // Keeps track of the servlet context path that needs to be prepended to the route when updating
    // the span name
    private static final ContextKey<ServletContextPath> CONTEXT_KEY =
        ContextKey.named("opentelemetry-servlet-context-path-key");
  
    public static <REQUEST> Context init(
        Context context, Function<REQUEST, String> contextPathExtractor, REQUEST request) {
      ServletContextPath servletContextPath = context.get(CONTEXT_KEY);
      if (servletContextPath != null) {
        return context;
      }
      String contextPath = contextPathExtractor.apply(request);
      if (contextPath == null) {
        // context path isn't know yet
        return context;
      }
      if (contextPath.isEmpty() || contextPath.equals("/")) {
        // normalize empty context path to null
        contextPath = null;
      }
      return context.with(CONTEXT_KEY, new ServletContextPath(contextPath));
    }
  
    private final String contextPath;
  
    private ServletContextPath(String contextPath) {
      this.contextPath = contextPath;
    }
  
    /**
     * Returns a concatenation of a servlet context path stored in the given {@code context} and a
     * given {@code spanName}. If there is no servlet path stored in the context, returns {@code
     * spanName}.
     */
    public static String prepend(Context context, String spanName) {
      ServletContextPath servletContextPath = context.get(CONTEXT_KEY);
      if (servletContextPath != null) {
        String value = servletContextPath.contextPath;
        if (value != null) {
          if (spanName == null || spanName.isEmpty()) {
            return value;
          } else {
            return value + (spanName.startsWith("/") ? spanName : ("/" + spanName));
          }
        }
      }
  
      return spanName;
    }
  }
  