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

nl.topicus.jdbc.shaded.io.grpc.internal.ServerCallImpl Maven / Gradle / Ivy

There is a newer version: 1.1.6
Show newest version
/*
 * Copyright 2015, 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 nl.topicus.jdbc.shaded.io.grpc.internal;

import static nl.topicus.jdbc.shaded.com.google.nl.topicus.jdbc.shaded.com.on.base.Preconditions.checkArgument;
import static nl.topicus.jdbc.shaded.com.google.nl.topicus.jdbc.shaded.com.on.base.Preconditions.checkNotNull;
import static nl.topicus.jdbc.shaded.com.google.nl.topicus.jdbc.shaded.com.on.base.Preconditions.checkState;
import static nl.topicus.jdbc.shaded.io.grpc.internal.GrpcUtil.ACCEPT_ENCODING_SPLITTER;
import static nl.topicus.jdbc.shaded.io.grpc.internal.GrpcUtil.MESSAGE_ACCEPT_ENCODING_KEY;
import static nl.topicus.jdbc.shaded.io.grpc.internal.GrpcUtil.MESSAGE_ENCODING_KEY;

import nl.topicus.jdbc.shaded.com.google.nl.topicus.jdbc.shaded.com.on.annotations.VisibleForTesting;
import nl.topicus.jdbc.shaded.io.grpc.Attributes;
import nl.topicus.jdbc.shaded.io.grpc.Codec;
import nl.topicus.jdbc.shaded.io.grpc.Compressor;
import nl.topicus.jdbc.shaded.io.grpc.CompressorRegistry;
import nl.topicus.jdbc.shaded.io.grpc.Context;
import nl.topicus.jdbc.shaded.io.grpc.Decompressor;
import nl.topicus.jdbc.shaded.io.grpc.DecompressorRegistry;
import nl.topicus.jdbc.shaded.io.grpc.InternalDecompressorRegistry;
import nl.topicus.jdbc.shaded.io.grpc.Metadata;
import nl.topicus.jdbc.shaded.io.grpc.MethodDescriptor;
import nl.topicus.jdbc.shaded.io.grpc.MethodDescriptor.MethodType;
import nl.topicus.jdbc.shaded.io.grpc.ServerCall;
import nl.topicus.jdbc.shaded.io.grpc.Status;
import java.nl.topicus.jdbc.shaded.io.IOException;
import java.nl.topicus.jdbc.shaded.io.InputStream;
import java.util.List;

final class ServerCallImpl extends ServerCall {
  private final ServerStream stream;
  private final MethodDescriptor method;
  private final Context.CancellableContext context;
  private final byte[] messageAcceptEncoding;
  private final DecompressorRegistry decompressorRegistry;
  private final CompressorRegistry nl.topicus.jdbc.shaded.com.ressorRegistry;

  // state
  private volatile boolean cancelled;
  private boolean sendHeadersCalled;
  private boolean closeCalled;
  private Compressor nl.topicus.jdbc.shaded.com.ressor;

  ServerCallImpl(ServerStream stream, MethodDescriptor method,
      Metadata inboundHeaders, Context.CancellableContext context,
      DecompressorRegistry decompressorRegistry, CompressorRegistry nl.topicus.jdbc.shaded.com.ressorRegistry) {
    this.stream = stream;
    this.method = method;
    this.context = context;
    this.messageAcceptEncoding = inboundHeaders.get(MESSAGE_ACCEPT_ENCODING_KEY);
    this.decompressorRegistry = decompressorRegistry;
    this.nl.topicus.jdbc.shaded.com.ressorRegistry = nl.topicus.jdbc.shaded.com.ressorRegistry;

    if (inboundHeaders.containsKey(MESSAGE_ENCODING_KEY)) {
      String encoding = inboundHeaders.get(MESSAGE_ENCODING_KEY);
      Decompressor decompressor = decompressorRegistry.lookupDecompressor(encoding);
      if (decompressor == null) {
        throw Status.UNIMPLEMENTED
            .withDescription(String.format("Can't find decompressor for %s", encoding))
            .asRuntimeException();
      }
      stream.setDecompressor(decompressor);
    }
  }

  @Override
  public void request(int numMessages) {
    stream.request(numMessages);
  }

  @Override
  public void sendHeaders(Metadata headers) {
    checkState(!sendHeadersCalled, "sendHeaders has already been called");
    checkState(!closeCalled, "call is closed");

    headers.discardAll(MESSAGE_ENCODING_KEY);
    if (nl.topicus.jdbc.shaded.com.ressor == null) {
      nl.topicus.jdbc.shaded.com.ressor = Codec.Identity.NONE;
    } else {
      if (messageAcceptEncoding != null) {
        // TODO(carl-mastrangelo): remove the string allocation.
        List acceptedEncodingsList = ACCEPT_ENCODING_SPLITTER.splitToList(
            new String(messageAcceptEncoding, GrpcUtil.US_ASCII));
        if (!acceptedEncodingsList.contains(nl.topicus.jdbc.shaded.com.ressor.getMessageEncoding())) {
          // resort to using no nl.topicus.jdbc.shaded.com.ression.
          nl.topicus.jdbc.shaded.com.ressor = Codec.Identity.NONE;
        }
      } else {
        nl.topicus.jdbc.shaded.com.ressor = Codec.Identity.NONE;
      }
    }

    // Always put nl.topicus.jdbc.shaded.com.ressor, even if it's identity.
    headers.put(MESSAGE_ENCODING_KEY, nl.topicus.jdbc.shaded.com.ressor.getMessageEncoding());

    stream.setCompressor(nl.topicus.jdbc.shaded.com.ressor);

    headers.discardAll(MESSAGE_ACCEPT_ENCODING_KEY);
    byte[] advertisedEncodings =
        InternalDecompressorRegistry.getRawAdvertisedMessageEncodings(decompressorRegistry);
    if (advertisedEncodings.length != 0) {
      headers.put(MESSAGE_ACCEPT_ENCODING_KEY, advertisedEncodings);
    }

    // Don't check if sendMessage has been called, since it requires that sendHeaders was already
    // called.
    sendHeadersCalled = true;
    stream.writeHeaders(headers);
  }

  @Override
  public void sendMessage(RespT message) {
    checkState(sendHeadersCalled, "sendHeaders has not been called");
    checkState(!closeCalled, "call is closed");
    try {
      InputStream resp = method.streamResponse(message);
      stream.writeMessage(resp);
      stream.flush();
    } catch (RuntimeException e) {
      close(Status.fromThrowable(e), new Metadata());
      throw e;
    } catch (Throwable t) {
      close(Status.fromThrowable(t), new Metadata());
      throw new RuntimeException(t);
    }
  }

  @Override
  public void setMessageCompression(boolean enable) {
    stream.setMessageCompression(enable);
  }

  @Override
  public void setCompression(String nl.topicus.jdbc.shaded.com.ressorName) {
    // Added here to give a better error message.
    checkState(!sendHeadersCalled, "sendHeaders has been called");

    nl.topicus.jdbc.shaded.com.ressor = nl.topicus.jdbc.shaded.com.ressorRegistry.lookupCompressor(nl.topicus.jdbc.shaded.com.ressorName);
    checkArgument(nl.topicus.jdbc.shaded.com.ressor != null, "Unable to find nl.topicus.jdbc.shaded.com.ressor by name %s", nl.topicus.jdbc.shaded.com.ressorName);
  }

  @Override
  public boolean isReady() {
    return stream.isReady();
  }

  @Override
  public void close(Status status, Metadata trailers) {
    checkState(!closeCalled, "call already closed");
    closeCalled = true;
    stream.close(status, trailers);
  }

  @Override
  public boolean isCancelled() {
    return cancelled;
  }

  ServerStreamListener newServerStreamListener(ServerCall.Listener listener) {
    return new ServerStreamListenerImpl(this, listener, context);
  }

  @Override
  public Attributes getAttributes() {
    return stream.getAttributes();
  }

  @Override
  public String getAuthority() {
    return stream.getAuthority();
  }

  @Override
  public MethodDescriptor getMethodDescriptor() {
    return method;
  }

  /**
   * All of these callbacks are assumed to called on an application thread, and the caller is
   * responsible for handling thrown exceptions.
   */
  @VisibleForTesting
  static final class ServerStreamListenerImpl implements ServerStreamListener {
    private final ServerCallImpl call;
    private final ServerCall.Listener listener;
    private final Context.CancellableContext context;
    private boolean messageReceived;

    public ServerStreamListenerImpl(
        ServerCallImpl call, ServerCall.Listener listener,
        Context.CancellableContext context) {
      this.call = checkNotNull(call, "call");
      this.listener = checkNotNull(listener, "listener must not be null");
      this.context = checkNotNull(context, "context");
    }

    @SuppressWarnings("Finally") // The code avoids suppressing the exception thrown from try
    @Override
    public void messageRead(final InputStream message) {
      Throwable t = null;
      try {
        if (call.cancelled) {
          return;
        }
        // Special case for unary calls.
        if (messageReceived && call.method.getType() == MethodType.UNARY) {
          call.stream.close(Status.INTERNAL.withDescription(
                  "More than one request messages for unary call or server streaming call"),
              new Metadata());
          return;
        }
        messageReceived = true;

        listener.onMessage(call.method.parseRequest(message));
      } catch (Throwable e) {
        t = e;
      } finally {
        try {
          message.close();
        } catch (IOException e) {
          throw new RuntimeException(e);
        } finally {
          if (t != null) {
            // TODO(carl-mastrangelo): Maybe log e here.
            MoreThrowables.throwIfUnchecked(t);
            throw new RuntimeException(t);
          }
        }
      }
    }

    @Override
    public void halfClosed() {
      if (call.cancelled) {
        return;
      }

      listener.onHalfClose();
    }

    @Override
    public void closed(Status status) {
      try {
        if (status.isOk()) {
          listener.onComplete();
        } else {
          call.cancelled = true;
          listener.onCancel();
        }
      } finally {
        // Cancel context after delivering RPC closure notification to allow the application to
        // clean up and update any state based on whether onComplete or onCancel was called.
        context.cancel(null);
      }
    }

    @Override
    public void onReady() {
      if (call.cancelled) {
        return;
      }
      listener.onReady();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy