Search in sources :

Example 1 with PubSubClientException

use of io.micronaut.gcp.pubsub.exception.PubSubClientException in project micronaut-gcp by micronaut-projects.

the class PubSubClientIntroductionAdvice method intercept.

@Override
public Object intercept(MethodInvocationContext<Object, Object> context) {
    if (context.hasAnnotation(Topic.class)) {
        PubSubPublisherState publisherState = publisherStateCache.computeIfAbsent(context.getExecutableMethod(), method -> {
            String projectId = method.stringValue(PubSubClient.class).orElse(googleCloudConfiguration.getProjectId());
            Optional<Argument> orderingArgument = Arrays.stream(method.getArguments()).filter(argument -> argument.getAnnotationMetadata().hasAnnotation(OrderingKey.class)).findFirst();
            String topic = method.stringValue(Topic.class).orElse(context.getName());
            String endpoint = method.stringValue(Topic.class, "endpoint").orElse("");
            String configurationName = method.stringValue(Topic.class, "configuration").orElse("");
            String contentType = method.stringValue(Topic.class, "contentType").orElse(MediaType.APPLICATION_JSON);
            ProjectTopicName projectTopicName = PubSubTopicUtils.toProjectTopicName(topic, projectId);
            Map<String, String> staticMessageAttributes = new HashMap<>();
            List<AnnotationValue<MessageHeader>> headerAnnotations = context.getAnnotationValuesByType(MessageHeader.class);
            headerAnnotations.forEach((header) -> {
                String name = header.stringValue("name").orElse(null);
                String value = header.stringValue().orElse(null);
                if (StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value)) {
                    staticMessageAttributes.put(name, value);
                }
            });
            Argument<?> bodyArgument = findBodyArgument(method).orElseThrow(() -> new PubSubClientException("No valid message body argument found for method: " + context.getExecutableMethod()));
            PubSubPublisherState.TopicState topicState = new PubSubPublisherState.TopicState(contentType, projectTopicName, configurationName, endpoint, orderingArgument.isPresent());
            logger.debug("Created a new publisher[{}] for topic: {}", context.getExecutableMethod().getName(), topic);
            PublisherInterface publisher = publisherFactory.createPublisher(new PublisherFactoryConfig(topicState, pubSubConfigurationProperties.getPublishingExecutor()));
            return new PubSubPublisherState(topicState, staticMessageAttributes, bodyArgument, publisher, orderingArgument);
        });
        Map<String, String> messageAttributes = new HashMap<>(publisherState.getStaticMessageAttributes());
        String contentType = publisherState.getTopicState().getContentType();
        Argument<?> bodyArgument = publisherState.getBodyArgument();
        Map<String, Object> parameterValues = context.getParameterValueMap();
        final ReturnType<Object> returnTypeInfo = context.getReturnType();
        ReturnType<Object> returnType = returnTypeInfo;
        Class<?> javaReturnType = returnType.getType();
        Argument[] arguments = context.getArguments();
        for (Argument arg : arguments) {
            AnnotationValue<MessageHeader> headerAnn = arg.getAnnotation(MessageHeader.class);
            if (headerAnn != null) {
                Map.Entry<String, String> entry = getNameAndValue(arg, headerAnn, parameterValues);
                messageAttributes.put(entry.getKey(), entry.getValue());
            }
        }
        PublisherInterface publisher = publisherState.getPublisher();
        Object body = parameterValues.get(bodyArgument.getName());
        PubsubMessage pubsubMessage = null;
        if (body.getClass() == PubsubMessage.class) {
            pubsubMessage = (PubsubMessage) body;
        } else {
            // if target type is byte[] we bypass serdes completely
            byte[] serialized = null;
            if (body.getClass() == byte[].class) {
                serialized = (byte[]) body;
            } else {
                PubSubMessageSerDes serDes = serDesRegistry.find(contentType).orElseThrow(() -> new PubSubClientException("Could not locate a valid SerDes implementation for type: " + contentType));
                serialized = serDes.serialize(body);
            }
            messageAttributes.put("Content-Type", contentType);
            PubsubMessage.Builder messageBuilder = PubsubMessage.newBuilder();
            messageBuilder.setData(ByteString.copyFrom(serialized)).putAllAttributes(messageAttributes);
            if (publisherState.getOrderingArgument().isPresent()) {
                String orderingKey = conversionService.convert(parameterValues.get(publisherState.getOrderingArgument().get().getName()), String.class).orElseThrow(() -> new PubSubClientException("Could not convert argument annotated with @OrderingKey to String type"));
                messageBuilder.setOrderingKey(orderingKey);
            }
            pubsubMessage = messageBuilder.build();
        }
        PubsubMessage finalPubsubMessage = pubsubMessage;
        Mono<String> reactiveResult = Mono.create(sink -> {
            ApiFuture<String> future = publisher.publish(finalPubsubMessage);
            future.addListener(() -> {
                try {
                    final String result = future.get();
                    sink.success(result);
                } catch (Throwable e) {
                    sink.error(e);
                }
            }, executorService);
        });
        if (javaReturnType == void.class || javaReturnType == Void.class) {
            String result = reactiveResult.block();
            return null;
        } else {
            if (returnTypeInfo.isReactive()) {
                return Publishers.convertPublisher(reactiveResult, javaReturnType);
            } else if (returnTypeInfo.isAsync()) {
                return reactiveResult.toFuture();
            } else {
                String result = reactiveResult.block();
                return conversionService.convert(result, javaReturnType).orElseThrow(() -> new PubSubClientException("Could not convert publisher result to method return type: " + javaReturnType));
            }
        }
    } else {
        return context.proceed();
    }
}
Also used : PublisherFactory(io.micronaut.gcp.pubsub.support.PublisherFactory) Arrays(java.util.Arrays) Publishers(io.micronaut.core.async.publisher.Publishers) PubSubPublisherState(io.micronaut.gcp.pubsub.support.PubSubPublisherState) LoggerFactory(org.slf4j.LoggerFactory) MessageHeader(io.micronaut.messaging.annotation.MessageHeader) HashMap(java.util.HashMap) PublisherFactoryConfig(io.micronaut.gcp.pubsub.support.PublisherFactoryConfig) ExecutableMethod(io.micronaut.inject.ExecutableMethod) PubSubMessageSerDesRegistry(io.micronaut.gcp.pubsub.serdes.PubSubMessageSerDesRegistry) PubsubMessage(com.google.pubsub.v1.PubsubMessage) PreDestroy(javax.annotation.PreDestroy) TaskExecutors(io.micronaut.scheduling.TaskExecutors) PublisherInterface(com.google.cloud.pubsub.v1.PublisherInterface) PubSubClientException(io.micronaut.gcp.pubsub.exception.PubSubClientException) MethodInterceptor(io.micronaut.aop.MethodInterceptor) PubSubTopicUtils(io.micronaut.gcp.pubsub.support.PubSubTopicUtils) MediaType(io.micronaut.http.MediaType) Map(java.util.Map) ReturnType(io.micronaut.core.type.ReturnType) PubSubConfigurationProperties(io.micronaut.gcp.pubsub.configuration.PubSubConfigurationProperties) ProjectTopicName(com.google.pubsub.v1.ProjectTopicName) Argument(io.micronaut.core.type.Argument) PubSubClient(io.micronaut.gcp.pubsub.annotation.PubSubClient) ConversionService(io.micronaut.core.convert.ConversionService) PubSubMessageSerDes(io.micronaut.gcp.pubsub.serdes.PubSubMessageSerDes) ExecutorService(java.util.concurrent.ExecutorService) Logger(org.slf4j.Logger) GoogleCloudConfiguration(io.micronaut.gcp.GoogleCloudConfiguration) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Singleton(jakarta.inject.Singleton) Mono(reactor.core.publisher.Mono) ApiFuture(com.google.api.core.ApiFuture) ByteString(com.google.protobuf.ByteString) StringUtils(io.micronaut.core.util.StringUtils) AbstractMap(java.util.AbstractMap) List(java.util.List) MethodInvocationContext(io.micronaut.aop.MethodInvocationContext) AnnotationValue(io.micronaut.core.annotation.AnnotationValue) OrderingKey(io.micronaut.gcp.pubsub.annotation.OrderingKey) Optional(java.util.Optional) Topic(io.micronaut.gcp.pubsub.annotation.Topic) MessageBody(io.micronaut.messaging.annotation.MessageBody) Named(jakarta.inject.Named) PubSubClientException(io.micronaut.gcp.pubsub.exception.PubSubClientException) Argument(io.micronaut.core.type.Argument) HashMap(java.util.HashMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) PubSubPublisherState(io.micronaut.gcp.pubsub.support.PubSubPublisherState) ByteString(com.google.protobuf.ByteString) PublisherInterface(com.google.cloud.pubsub.v1.PublisherInterface) PubsubMessage(com.google.pubsub.v1.PubsubMessage) PubSubClient(io.micronaut.gcp.pubsub.annotation.PubSubClient) Topic(io.micronaut.gcp.pubsub.annotation.Topic) PubSubMessageSerDes(io.micronaut.gcp.pubsub.serdes.PubSubMessageSerDes) PublisherFactoryConfig(io.micronaut.gcp.pubsub.support.PublisherFactoryConfig) AnnotationValue(io.micronaut.core.annotation.AnnotationValue) ProjectTopicName(com.google.pubsub.v1.ProjectTopicName) MessageHeader(io.micronaut.messaging.annotation.MessageHeader) HashMap(java.util.HashMap) Map(java.util.Map) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) AbstractMap(java.util.AbstractMap)

Example 2 with PubSubClientException

use of io.micronaut.gcp.pubsub.exception.PubSubClientException in project micronaut-gcp by micronaut-projects.

the class DefaultPublisherFactory method createPublisher.

/**
 * Creates a publisher for a given topic.
 *
 * @param config {@link PublisherFactoryConfig} object containing all required properties.
 * @return An instance of {@link Publisher} configured using the config and environment properties from `gcp.pubsub.publisher.&gt;config_name&lt;`
 */
@Override
public Publisher createPublisher(@NonNull PublisherFactoryConfig config) {
    try {
        Publisher.Builder publisherBuilder = Publisher.newBuilder(config.getTopicState().getProjectTopicName());
        Optional<PublisherConfigurationProperties> publisherConfiguration = beanContext.findBean(PublisherConfigurationProperties.class, Qualifiers.byName(config.getTopicState().getConfigurationName()));
        String executor = publisherConfiguration.map(p -> p.getExecutor()).orElse(config.getDefaultExecutor());
        ExecutorService executorService = beanContext.getBean(ExecutorService.class, Qualifiers.byName(executor));
        publisherBuilder.setEnableMessageOrdering(config.getTopicState().getOrdered());
        if (!StringUtils.isEmpty(config.getTopicState().getEndpoint())) {
            publisherBuilder.setEndpoint(config.getTopicState().getEndpoint());
        }
        if (publisherConfiguration.isPresent()) {
            publisherBuilder.setRetrySettings(publisherConfiguration.get().getRetrySettings().build());
            // FlowControl had to be flatten in order to be parsed
            BatchingSettings batchSettings = publisherConfiguration.get().getBatchingSettings().build();
            publisherBuilder.setBatchingSettings(BatchingSettings.newBuilder().setDelayThreshold(batchSettings.getDelayThreshold()).setElementCountThreshold(batchSettings.getElementCountThreshold()).setIsEnabled(batchSettings.getIsEnabled()).setRequestByteThreshold(batchSettings.getRequestByteThreshold()).setFlowControlSettings(publisherConfiguration.get().getFlowControlSettings().build()).build());
        }
        if (!(executorService instanceof ScheduledExecutorService)) {
            throw new IllegalStateException("Invalid Executor type provided, please make sure you have a ScheduledExecutorService configured for Publisher: " + config.getTopicState().getProjectTopicName().getTopic());
        }
        publisherBuilder.setExecutorProvider(FixedExecutorProvider.create((ScheduledExecutorService) executorService));
        publisherBuilder.setChannelProvider(this.transportChannelProvider);
        publisherBuilder.setCredentialsProvider(this.credentialsProvider);
        return publisherBuilder.build();
    } catch (Exception ex) {
        throw new PubSubClientException("Failed to create subscriber", ex);
    }
}
Also used : BeanContext(io.micronaut.context.BeanContext) BatchingSettings(com.google.api.gax.batching.BatchingSettings) PublisherConfigurationProperties(io.micronaut.gcp.pubsub.configuration.PublisherConfigurationProperties) Qualifiers(io.micronaut.inject.qualifiers.Qualifiers) Singleton(jakarta.inject.Singleton) StringUtils(io.micronaut.core.util.StringUtils) NonNull(io.micronaut.core.annotation.NonNull) Publisher(com.google.cloud.pubsub.v1.Publisher) PubSubClientException(io.micronaut.gcp.pubsub.exception.PubSubClientException) Modules(io.micronaut.gcp.Modules) TransportChannelProvider(com.google.api.gax.rpc.TransportChannelProvider) CredentialsProvider(com.google.api.gax.core.CredentialsProvider) FixedExecutorProvider(com.google.api.gax.core.FixedExecutorProvider) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) Optional(java.util.Optional) ExecutorService(java.util.concurrent.ExecutorService) Named(jakarta.inject.Named) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) PubSubClientException(io.micronaut.gcp.pubsub.exception.PubSubClientException) PublisherConfigurationProperties(io.micronaut.gcp.pubsub.configuration.PublisherConfigurationProperties) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) ExecutorService(java.util.concurrent.ExecutorService) Publisher(com.google.cloud.pubsub.v1.Publisher) BatchingSettings(com.google.api.gax.batching.BatchingSettings) PubSubClientException(io.micronaut.gcp.pubsub.exception.PubSubClientException)

Aggregations

StringUtils (io.micronaut.core.util.StringUtils)2 PubSubClientException (io.micronaut.gcp.pubsub.exception.PubSubClientException)2 Named (jakarta.inject.Named)2 Singleton (jakarta.inject.Singleton)2 Optional (java.util.Optional)2 ExecutorService (java.util.concurrent.ExecutorService)2 ApiFuture (com.google.api.core.ApiFuture)1 BatchingSettings (com.google.api.gax.batching.BatchingSettings)1 CredentialsProvider (com.google.api.gax.core.CredentialsProvider)1 FixedExecutorProvider (com.google.api.gax.core.FixedExecutorProvider)1 TransportChannelProvider (com.google.api.gax.rpc.TransportChannelProvider)1 Publisher (com.google.cloud.pubsub.v1.Publisher)1 PublisherInterface (com.google.cloud.pubsub.v1.PublisherInterface)1 ByteString (com.google.protobuf.ByteString)1 ProjectTopicName (com.google.pubsub.v1.ProjectTopicName)1 PubsubMessage (com.google.pubsub.v1.PubsubMessage)1 MethodInterceptor (io.micronaut.aop.MethodInterceptor)1 MethodInvocationContext (io.micronaut.aop.MethodInvocationContext)1 BeanContext (io.micronaut.context.BeanContext)1 AnnotationValue (io.micronaut.core.annotation.AnnotationValue)1