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

brave.internal.propagation.InjectorFactory Maven / Gradle / Ivy

There is a newer version: 6.3.0
Show newest version
/*
 * Copyright The OpenZipkin Authors
 * SPDX-License-Identifier: Apache-2.0
 */
package brave.internal.propagation;

import brave.Request;
import brave.Span;
import brave.propagation.Propagation;
import brave.propagation.Propagation.RemoteSetter;
import brave.propagation.Propagation.Setter;
import brave.propagation.TraceContext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * This is an internal type used to implement possibly multiple trace formats based on {@link
 * Span.Kind}. It isn't intended to be shared widely as it may change in practice.
 *
 * 

When {@link RemoteSetter} is implemented, {@link #newInjector(Setter)} will create an * injector that is pre-configured for {@link RemoteSetter#spanKind()}. Otherwise, it will create an * injector that defers until {@link RemoteInjector#inject(TraceContext, Object)} is called. * *

A deferred injector checks if the {@code request} parameter is an instance of {@link * Request}, it considers {@link Request#spanKind()}. If so, it uses this for injection formats * accordingly. If not, a {@linkplain Builder#injectorFunction(InjectorFunction, * InjectorFunction...) default function} is used. * *

Note: Instrumentation that have not been recently updated may be remote, but neither * implement {@link RemoteSetter} nor {@link Request}. In other words, lack of these types do not * mean the input is for a local span. However, this can be assumed for code in this repository, as * it is all up-to-date. * *

In Brave 6, we might make the assumption that local injection is when the inputs neither * implement {@link RemoteSetter} nor {@link Request}. */ public final class InjectorFactory { /** Like {@link TraceContext.Injector}, except the {@link Setter} is a parameter. */ public interface InjectorFunction { InjectorFunction NOOP = new InjectorFunction() { @Override public List keyNames() { return Collections.emptyList(); } @Override public void inject(Setter setter, TraceContext context, R request) { } }; /** * The distinct list of key names this can inject. * * @see Propagation#keys() */ List keyNames(); /** Like {@see TraceContext.Injector#inject} except the {@code setter} is explicit. */ void inject(Setter setter, TraceContext context, R request); } /** Defaults all injector functions to {@code injectorFunction}. */ public static Builder newBuilder(InjectorFunction injectorFunction) { if (injectorFunction == null) throw new NullPointerException("injectorFunction == null"); return new Builder(injectorFunction); } public static final class Builder { InjectorFunction injectorFunction; InjectorFunction clientInjectorFunction; InjectorFunction producerInjectorFunction; InjectorFunction consumerInjectorFunction; Builder(InjectorFunction defaultInjectorFunction) { injectorFunction = defaultInjectorFunction; clientInjectorFunction = defaultInjectorFunction; producerInjectorFunction = defaultInjectorFunction; consumerInjectorFunction = defaultInjectorFunction; } /** * The {@linkplain InjectorFunction injectors} to use when the {@link Setter} is not {@link * RemoteSetter} and the request parameter is not a {@link Request}. * * @param injectorFunctions ignores an empty array or {@link InjectorFunction#NOOP} elements. */ public Builder injectorFunctions(InjectorFunction... injectorFunctions) { injectorFunction = injectorFunction(injectorFunction, injectorFunctions); return this; } /** * The {@linkplain InjectorFunction injectors} to use for {@link Span.Kind#CLIENT} contexts, * determined by either {@link RemoteSetter#spanKind()} or {@link Request#spanKind()}. * * @param injectorFunctions ignores an empty array or {@link InjectorFunction#NOOP} elements. */ public Builder clientInjectorFunctions(InjectorFunction... injectorFunctions) { clientInjectorFunction = injectorFunction(clientInjectorFunction, injectorFunctions); return this; } /** * The {@linkplain InjectorFunction injectors} to use for {@link Span.Kind#PRODUCER} contexts, * determined by either {@link RemoteSetter#spanKind()} or {@link Request#spanKind()}. * * @param injectorFunctions ignores an empty array or {@link InjectorFunction#NOOP} elements. */ public Builder producerInjectorFunctions(InjectorFunction... injectorFunctions) { this.producerInjectorFunction = injectorFunction(producerInjectorFunction, injectorFunctions); return this; } /** * The {@linkplain InjectorFunction injectors} to use for {@link Span.Kind#CONSUMER} contexts, * determined by either {@link RemoteSetter#spanKind()} or {@link Request#spanKind()}. * *

It may seem unusual at first to inject consumers, but this is how the trace context is * serialized for message processors that happen on other threads, or distributed stages (like * kafka-streams). * * @param injectorFunctions ignores an empty array or {@link InjectorFunction#NOOP} elements. */ public Builder consumerInjectorFunctions(InjectorFunction... injectorFunctions) { consumerInjectorFunction = injectorFunction(consumerInjectorFunction, injectorFunctions); return this; } /** @throws IllegalArgumentException if all builder methods weren't called. */ public InjectorFactory build() { return new InjectorFactory(this); } } final InjectorFunction injectorFunction; final InjectorFunction clientInjectorFunction, producerInjectorFunction, consumerInjectorFunction; final List keyNames; InjectorFactory(Builder builder) { injectorFunction = builder.injectorFunction; clientInjectorFunction = builder.clientInjectorFunction; producerInjectorFunction = builder.producerInjectorFunction; consumerInjectorFunction = builder.consumerInjectorFunction; Set keyNames = new LinkedHashSet(); // Add messaging first as their formats are likely the cheapest to extract keyNames.addAll(builder.consumerInjectorFunction.keyNames()); keyNames.addAll(builder.producerInjectorFunction.keyNames()); keyNames.addAll(builder.clientInjectorFunction.keyNames()); keyNames.addAll(builder.injectorFunction.keyNames()); this.keyNames = Collections.unmodifiableList(new ArrayList(keyNames)); } /** * The distinct list of key names this can inject. * * @see Propagation#keys() */ public List keyNames() { return keyNames; } /** * Creates a potentially composite injector if the input is an instance of {@link RemoteSetter}. * Otherwise, a deferred injector is return that examples the request parameter to decide if it is * remote or not. */ public TraceContext.Injector newInjector(Setter setter) { if (setter == null) throw new NullPointerException("setter == null"); if (setter instanceof RemoteSetter) { RemoteSetter remoteSetter = (RemoteSetter) setter; switch (remoteSetter.spanKind()) { case CLIENT: return new RemoteInjector(setter, clientInjectorFunction); case PRODUCER: return new RemoteInjector(setter, producerInjectorFunction); case CONSUMER: return new RemoteInjector(setter, consumerInjectorFunction); default: // SERVER is nonsense as it cannot be injected } } return new DeferredInjector(setter, this); } @Override public int hashCode() { int h = 1000003; h ^= injectorFunction.hashCode(); h *= 1000003; h ^= clientInjectorFunction.hashCode(); h *= 1000003; h ^= producerInjectorFunction.hashCode(); h *= 1000003; h ^= consumerInjectorFunction.hashCode(); return h; } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof InjectorFactory)) return false; InjectorFactory that = (InjectorFactory) o; return injectorFunction.equals(that.injectorFunction) && clientInjectorFunction.equals(that.clientInjectorFunction) && producerInjectorFunction.equals(that.producerInjectorFunction) && consumerInjectorFunction.equals(that.consumerInjectorFunction); } @Override public String toString() { return "InjectorFactory{injectorFunction=" + injectorFunction + ", clientInjectorFunction=" + clientInjectorFunction + ", producerInjectorFunction=" + producerInjectorFunction + ", consumerInjectorFunction=" + consumerInjectorFunction + "}"; } static final class DeferredInjector implements TraceContext.Injector { final Setter setter; final InjectorFactory injectorFactory; DeferredInjector(Setter setter, InjectorFactory injectorFactory) { this.setter = setter; this.injectorFactory = injectorFactory; } @Override public void inject(TraceContext context, R request) { if (request instanceof Request) { switch (((Request) request).spanKind()) { case CLIENT: injectorFactory.clientInjectorFunction.inject(setter, context, request); return; case PRODUCER: injectorFactory.producerInjectorFunction.inject(setter, context, request); return; case CONSUMER: injectorFactory.consumerInjectorFunction.inject(setter, context, request); return; default: // SERVER is nonsense as it cannot be injected } } injectorFactory.injectorFunction.inject(setter, context, request); } @Override public int hashCode() { int h = 1000003; h ^= setter.hashCode(); h *= 1000003; h ^= injectorFactory.hashCode(); return h; } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof DeferredInjector)) return false; DeferredInjector that = (DeferredInjector) o; return setter.equals(that.setter) && injectorFactory.equals(that.injectorFactory); } @Override public String toString() { return "DeferredInjector{setter=" + setter + ", injectorFactory=" + injectorFactory + "}"; } } static final class RemoteInjector implements TraceContext.Injector { final InjectorFunction injectorFunction; final Setter setter; RemoteInjector(Setter setter, InjectorFunction injectorFunction) { this.injectorFunction = injectorFunction; this.setter = setter; } @Override public void inject(TraceContext context, R request) { injectorFunction.inject(setter, context, request); } @Override public int hashCode() { int h = 1000003; h ^= setter.hashCode(); h *= 1000003; h ^= injectorFunction.hashCode(); return h; } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof RemoteInjector)) return false; RemoteInjector that = (RemoteInjector) o; return setter.equals(that.setter) && injectorFunction.equals(that.injectorFunction); } @Override public String toString() { return "Injector{setter=" + setter + ", injectorFunction=" + injectorFunction + "}"; } } static InjectorFunction injectorFunction(InjectorFunction existing, InjectorFunction... update) { if (update == null) throw new NullPointerException("injectorFunctions == null"); LinkedHashSet injectorFunctionSet = new LinkedHashSet(Arrays.asList(update)); if (injectorFunctionSet.contains(null)) { throw new NullPointerException("injectorFunction == null"); } injectorFunctionSet.remove(InjectorFunction.NOOP); if (injectorFunctionSet.isEmpty()) return existing; if (injectorFunctionSet.size() == 1) return injectorFunctionSet.iterator().next(); return new CompositeInjectorFunction(injectorFunctionSet.toArray(new InjectorFunction[0])); } static final class CompositeInjectorFunction implements InjectorFunction { final InjectorFunction[] injectorFunctions; // Array ensures no iterators are created at runtime final List keyNames; CompositeInjectorFunction(InjectorFunction[] injectorFunctions) { this.injectorFunctions = injectorFunctions; Set keyNames = new LinkedHashSet(); for (InjectorFunction injectorFunction : injectorFunctions) { keyNames.addAll(injectorFunction.keyNames()); } this.keyNames = Collections.unmodifiableList(new ArrayList(keyNames)); } @Override public List keyNames() { return keyNames; } @Override public void inject(Setter setter, TraceContext context, R request) { for (InjectorFunction injectorFunction : injectorFunctions) { injectorFunction.inject(setter, context, request); } } @Override public int hashCode() { int h = 1000003; h ^= Arrays.hashCode(injectorFunctions); return h; } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof CompositeInjectorFunction)) return false; return Arrays.equals(injectorFunctions, ((CompositeInjectorFunction) o).injectorFunctions); } @Override public String toString() { return Arrays.toString(injectorFunctions); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy