com.ibm.cp4waiops.connectors.sdk.GRPCCloudEventConsumeChannel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of connectors-sdk Show documentation
Show all versions of connectors-sdk Show documentation
A developer SDK for creating connectors for CP4WAIOps.
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