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

io.fabric8.kubernetes.client.dsl.internal.ExecWatchInputStream Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.fabric8.kubernetes.client.dsl.internal;

import io.fabric8.kubernetes.client.KubernetesClientException;

import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.List;

/**
 * Provides an InputStream that is non-blocking to the producer
 * and that will request more input when needed.
 */
public class ExecWatchInputStream extends InputStream {

  private static final int BUFFER_SIZE = 1 << 15;

  private final LinkedList buffers = new LinkedList<>();
  private boolean complete;
  private boolean closed;
  private Throwable failed;
  private ByteBuffer currentBuffer;

  private final Runnable request;
  private final int bufferSize;

  public ExecWatchInputStream(Runnable request) {
    this(request, BUFFER_SIZE);
  }

  public ExecWatchInputStream(Runnable request, int bufferSize) {
    this.request = request;
    this.bufferSize = bufferSize;
  }

  void onExit(Integer exitCode, Throwable t) {
    synchronized (buffers) {
      if (complete) {
        return;
      }
      complete = true;
      if (t != null) {
        failed = t;
      } else if (exitCode != null && exitCode != 0) {
        failed = new KubernetesClientException("process exited with a non-zero exit code: " + exitCode);
      }
      buffers.notifyAll();
    }
  }

  void consume(List value) {
    synchronized (buffers) {
      if (closed) {
        // even if closed there may be other streams
        // so keep pulling
        request.run();
        return;
      }
      assert !complete || failed == null;
      buffers.addAll(value);
      buffers.notifyAll();
      if ((currentBuffer != null ? currentBuffer.remaining() : 0)
          + buffers.stream().mapToInt(ByteBuffer::remaining).sum() < bufferSize) {
        request.run();
      }
    }
  }

  private ByteBuffer current() throws IOException {
    synchronized (buffers) {
      while (currentBuffer == null || !currentBuffer.hasRemaining()) {
        // Check whether the stream is closed or exhausted
        if (closed) {
          throw new IOException("closed", failed);
        }
        if (buffers.isEmpty()) {
          if (complete) {
            if (failed != null) {
              throw new IOException("closed", failed);
            }
            return null;
          }
          requestMoreIfNeeded();
        }

        currentBuffer = buffers.poll();

        if (currentBuffer == null && !complete) {
          try {
            buffers.wait();
          } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new InterruptedIOException();
          }
        }
      }
      return currentBuffer;
    }
  }

  /**
   * Adapted from HttpResponseInputStream
   */
  @Override
  public int read(byte[] bytes, int off, int len) throws IOException {
    // get the buffer to read from, possibly blocking if
    // none is available
    ByteBuffer buffer = current();
    if (buffer == null) {
      return -1;
    }

    // don't attempt to read more than what is available
    // in the current buffer.
    int read = Math.min(buffer.remaining(), len);
    assert read > 0 && read <= buffer.remaining();

    // buffer.get() will do the boundary check for us.
    buffer.get(bytes, off, read);
    return read;
  }

  @Override
  public int read() throws IOException {
    byte[] single = new byte[1];
    if (read(single) == -1) {
      return -1;
    }
    return single[0] & 0xFF;
  }

  @Override
  public void close() throws IOException {
    synchronized (buffers) {
      if (this.closed) {
        return;
      }
      this.closed = true;
      requestMoreIfNeeded();
      this.buffers.clear();
      buffers.notifyAll();
    }
  }

  private void requestMoreIfNeeded() {
    if (currentBuffer != null) {
      this.currentBuffer = null;
      this.request.run();
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy