/* * Copyright 2016, Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package io.grpc.internal; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.VisibleForTesting; import io.grpc.CallOptions; import io.grpc.ClientStreamTracer; import io.grpc.Context; import io.grpc.Metadata; import io.grpc.ServerCall; import io.grpc.ServerStreamTracer; import io.grpc.Status; import io.grpc.StreamTracer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.concurrent.ThreadSafe; /** * The stats and tracing information for a stream. */ @ThreadSafe public final class StatsTraceContext { public static final StatsTraceContext NOOP = new StatsTraceContext(new StreamTracer[0]); private final StreamTracer[] tracers; private final AtomicBoolean closed = new AtomicBoolean(false); /** * Factory method for the client-side. */ public static StatsTraceContext newClientContext(CallOptions callOptions, Metadata headers) { List<ClientStreamTracer.Factory> factories = callOptions.getStreamTracerFactories(); if (factories.isEmpty()) { return NOOP; } // This array will be iterated multiple times per RPC. Use primitive array instead of Collection // so that for-each doesn't create an Iterator every time. StreamTracer[] tracers = new StreamTracer[factories.size()]; for (int i = 0; i < tracers.length; i++) { tracers[i] = factories.get(i).newClientStreamTracer(headers); } return new StatsTraceContext(tracers); } /** * Factory method for the server-side. */ public static StatsTraceContext newServerContext( List<ServerStreamTracer.Factory> factories, String fullMethodName, Metadata headers) { if (factories.isEmpty()) { return NOOP; } StreamTracer[] tracers = new StreamTracer[factories.size()]; for (int i = 0; i < tracers.length; i++) { tracers[i] = factories.get(i).newServerStreamTracer(fullMethodName, headers); } return new StatsTraceContext(tracers); } @VisibleForTesting StatsTraceContext(StreamTracer[] tracers) { this.tracers = tracers; } /** * Returns a copy of the tracer list. */ @VisibleForTesting public List<StreamTracer> getTracersForTest() { return new ArrayList<StreamTracer>(Arrays.asList(tracers)); } /** * See {@link ClientStreamTracer#outboundHeaders}. For client-side only. * * <p>Transport-specific, thus should be called by transport implementations. */ public void clientOutboundHeaders() { for (StreamTracer tracer : tracers) { ((ClientStreamTracer) tracer).outboundHeaders(); } } /** * See {@link ClientStreamTracer#inboundHeaders}. For client-side only. * * <p>Called from abstract stream implementations. */ public void clientInboundHeaders() { for (StreamTracer tracer : tracers) { ((ClientStreamTracer) tracer).inboundHeaders(); } } /** * See {@link ServerStreamTracer#filterContext}. For server-side only. * * <p>Called from {@link io.grpc.internal.ServerImpl}. */ public <ReqT, RespT> Context serverFilterContext(Context context) { Context ctx = checkNotNull(context, "context"); for (StreamTracer tracer : tracers) { ctx = ((ServerStreamTracer) tracer).filterContext(ctx); checkNotNull(ctx, "%s returns null context", tracer); } return ctx; } /** * See {@link ServerStreamTracer#serverCallStarted}. For server-side only. * * <p>Called from {@link io.grpc.internal.ServerImpl}. */ public void serverCallStarted(ServerCall<?, ?> call) { for (StreamTracer tracer : tracers) { ((ServerStreamTracer) tracer).serverCallStarted(call); } } /** * See {@link StreamTracer#streamClosed}. This may be called multiple times, and only the first * value will be taken. * * <p>Called from abstract stream implementations. */ public void streamClosed(Status status) { if (closed.compareAndSet(false, true)) { for (StreamTracer tracer : tracers) { tracer.streamClosed(status); } } } /** * See {@link StreamTracer#outboundMessage}. * * <p>Called from {@link io.grpc.internal.Framer}. */ public void outboundMessage() { for (StreamTracer tracer : tracers) { tracer.outboundMessage(); } } /** * See {@link StreamTracer#inboundMessage}. * * <p>Called from {@link io.grpc.internal.MessageDeframer}. */ public void inboundMessage() { for (StreamTracer tracer : tracers) { tracer.inboundMessage(); } } /** * See {@link StreamTracer#outboundUncompressedSize}. * * <p>Called from {@link io.grpc.internal.Framer}. */ public void outboundUncompressedSize(long bytes) { for (StreamTracer tracer : tracers) { tracer.outboundUncompressedSize(bytes); } } /** * See {@link StreamTracer#outboundWireSize}. * * <p>Called from {@link io.grpc.internal.Framer}. */ public void outboundWireSize(long bytes) { for (StreamTracer tracer : tracers) { tracer.outboundWireSize(bytes); } } /** * See {@link StreamTracer#inboundUncompressedSize}. * * <p>Called from {@link io.grpc.internal.MessageDeframer}. */ public void inboundUncompressedSize(long bytes) { for (StreamTracer tracer : tracers) { tracer.inboundUncompressedSize(bytes); } } /** * See {@link StreamTracer#inboundWireSize}. * * <p>Called from {@link io.grpc.internal.MessageDeframer}. */ public void inboundWireSize(long bytes) { for (StreamTracer tracer : tracers) { tracer.inboundWireSize(bytes); } } }