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

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

The newest version!
package com.ibm.cp4waiops.connectors.sdk;

import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
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 GRPCCloudEventConfigurationChannel implements StreamObserver, AutoCloseable {
    private static final Logger logger = Logger.getLogger(GRPCCloudEventConfigurationChannel.class.getName());

    private String _name;
    private ConnectorBridgeStub _stub;

    private CloudEvent _template;
    AtomicReference _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
     */
    GRPCCloudEventConfigurationChannel(String channelName, ConnectorBridgeStub stub, CloudEvent requestTemplate,
            AtomicReference latestReceivedConfiguration, MeterRegistry meterRegistry,
            Counter configStartCounter) {
        _name = channelName;
        _stub = stub;
        _template = requestTemplate;
        _output = latestReceivedConfiguration;
        _streamActive = new AtomicBoolean(false);
        _shouldClose = new AtomicBoolean(false);
        _consumedCount = meterRegistry.counter("connector.sdk.config.received", Constant.CHANNEL_NAME_TAG, channelName);
        _droppedCount = meterRegistry.counter("connector.sdk.config.dropped", Constant.CHANNEL_NAME_TAG, channelName);
        _streamStartCount = configStartCounter;
    }

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

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

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

    /**
     * Starts the configuration channel
     */
    void startConfigrationChannel() {
        // Ignore if already active
        if (_streamActive.get()) {
            logger.log(Level.FINE, "Stream is active" + _name);
            return;
        }
        // setup stream
        logger.log(Level.INFO, "starting configuration 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.configuration(convertedRequest, this);
    }

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

    /**
     * Asynchronously stops the consume channel
     */
    @Override
    public void close() {
        logger.log(Level.INFO, "stopping consume stream: channel=" + _name);
        _streamActive.set(false);
        _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);
            _streamActive.set(true);
        } 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);
            }
        }
        try {
            _output.set(event);
            _consumedCount.increment();
        } catch (Exception 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 - 2025 Weber Informatics LLC | Privacy Policy