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

com.ibm.cp4waiops.connectors.sdk.GRPCCloudEventConsumeChannel Maven / Gradle / Ivy

There is a newer version: 2.2.8
Show newest version
package com.ibm.cp4waiops.connectors.sdk;

import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.ibm.aiops.connectors.bridge.ConnectorBridgeGrpc.ConnectorBridgeStub;

import io.cloudevents.CloudEvent;
import io.cloudevents.core.builder.CloudEventBuilder;
import io.grpc.Status;
import io.grpc.stub.StreamObserver;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;

/**
 * Consumes cloud events from the target channel
 */
class GRPCCloudEventConsumeChannel implements StreamObserver, AutoCloseable {
    private static final Logger logger = Logger.getLogger(GRPCCloudEventConsumeChannel.class.getName());

    private String _name;
    private ConnectorBridgeStub _stub;

    private CloudEvent _template;
    private BlockingQueue _output;

    private AtomicBoolean _streamActive;
    private AtomicBoolean _shouldClose;

    private Counter _consumedCount;
    private Counter _droppedCount;
    private Counter _streamStartCount;

    /**
     * Constructs an instance
     * 
     * @param channelName
     *            the name of the channel that will be sent to the server, most likely the name of the kafka topic
     * @param stub
     *            the interface used to communicate with the bridge server
     * @param requestTemplate
     *            the template used for the request that initializes the channel
     * @param output
     *            a queue where incoming cloud events will be placed
     * @param meterRegistry
     *            a registry for recording channel metrics
     */
    GRPCCloudEventConsumeChannel(String channelName, ConnectorBridgeStub stub, CloudEvent requestTemplate,
            BlockingQueue output, MeterRegistry meterRegistry) {
        _name = channelName;
        _stub = stub;
        _template = requestTemplate;
        _output = output;
        _streamActive = new AtomicBoolean(false);
        _shouldClose = new AtomicBoolean(false);
        _consumedCount = meterRegistry.counter("connector.sdk.consume.received", Constant.CHANNEL_NAME_TAG,
                channelName);
        _droppedCount = meterRegistry.counter("connector.sdk.consume.dropped", Constant.CHANNEL_NAME_TAG, channelName);
        _streamStartCount = meterRegistry.counter("connector.sdk.consume.starts", Constant.CHANNEL_NAME_TAG,
                channelName);
    }

    double getConsumedCount() {
        return _consumedCount.count();
    }

    double getDroppedCount() {
        return _droppedCount.count();
    }

    double getStreamRestartCount() {
        return _streamStartCount.count() - 1;
    }

    /**
     * Starts the consume channel
     */
    void startChannel() {
        // Ignore if already active
        if (_streamActive.get()) {
            return;
        }
        // setup stream
        logger.log(Level.INFO, "starting consume stream: channel=" + _name);
        _streamStartCount.increment();
        CloudEvent request = CloudEventBuilder.from(_template).withId(UUID.randomUUID().toString())
                .withExtension(Connector.REQUEST_ID_CE_EXTENSION_NAME, UUID.randomUUID().toString()).build();
        io.cloudevents.v1.proto.CloudEvent convertedRequest;
        try {
            convertedRequest = Util.convertCloudEventToProto(request);
        } catch (Exception error) {
            logger.log(Level.SEVERE, "failed to convert request, consume stream will not start: name=" + _name, error);
            return;
        }
        _stub.consume(convertedRequest, this);
        _streamActive.set(true);
    }

    /**
     * Restarts the consume channel if it is inactive
     */
    void fixChannelIfStopped() {
        startChannel();
    }

    /**
     * Asynchronously stops the consume channel
     */
    @Override
    public void close() {
        logger.log(Level.INFO, "stopping consume stream: channel=" + _name);
        _shouldClose.set(true);
    }

    @Override
    public void onNext(io.cloudevents.v1.proto.CloudEvent proto) {
        // Close stream if requested
        if (_shouldClose.get()) {
            _streamActive.set(false);
            throw Status.CANCELLED.asRuntimeException();
        }
        // Convert
        CloudEvent event;
        try {
            event = Util.convertCloudEventFromProto(proto);
        } catch (Exception error) {
            logger.log(Level.SEVERE, "proto to cloud event conversion failed, dropping: id=" + proto.getId());
            _droppedCount.increment();
            return;
        }
        // Log
        if (logger.isLoggable(Level.FINE)) {
            try {
                logger.log(Level.FINE,
                        "receiving cloud event: channel=" + _name + ",event=" + Util.convertCloudEventToJSON(event));
            } catch (Exception error) {
                logger.log(Level.FINE, "failed to serialize event for logging", error);
            }
        }
        // Put on queue (blocking)
        try {
            CloudEventConsumer.CloudEventEntry entry = new CloudEventConsumer.CloudEventEntry();
            entry.channelName = _name;
            entry.event = event;
            _output.put(entry);
            _consumedCount.increment();
        } catch (InterruptedException error) {
            logger.log(Level.WARNING, "onNext() interrupted, event will be dropped: _channel=" + _name, error);
            Thread.currentThread().interrupt();
        }
    }

    @Override
    public void onError(Throwable t) {
        logger.log(Level.WARNING, "consume stream terminated with an error: channel=" + _name, t);

        String improvedDiagnostic = Util.getDiagnosticMessage(t);
        if (improvedDiagnostic != null) {
            logger.log(Level.WARNING, improvedDiagnostic);
        }
        _streamActive.set(false);
    }

    @Override
    public void onCompleted() {
        logger.log(Level.INFO, "consume stream terminated cleanly: channel=" + _name);
        _streamActive.set(false);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy