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

com.turbospaces.gcp.pubsub.config.PubsubSpringDiModule Maven / Gradle / Ivy

There is a newer version: 2.0.33
Show newest version
package com.turbospaces.gcp.pubsub.config;

import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.Executor;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.DynamicCloud;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.threeten.bp.Duration;

import com.google.api.gax.batching.BatchingSettings;
import com.google.api.gax.core.CredentialsProvider;
import com.google.api.gax.core.ExecutorProvider;
import com.google.api.gax.core.FixedExecutorProvider;
import com.google.api.gax.core.NoCredentialsProvider;
import com.google.api.gax.grpc.GrpcTransportChannel;
import com.google.api.gax.rpc.FixedTransportChannelProvider;
import com.google.api.gax.rpc.TransportChannelProvider;
import com.google.auth.Credentials;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.pubsub.v1.SubscriptionAdminClient;
import com.google.cloud.pubsub.v1.SubscriptionAdminSettings;
import com.google.cloud.pubsub.v1.TopicAdminClient;
import com.google.cloud.pubsub.v1.TopicAdminSettings;
import com.google.cloud.pubsub.v1.stub.PublisherStubSettings;
import com.google.cloud.pubsub.v1.stub.SubscriberStubSettings;
import com.google.cloud.spring.core.DefaultGcpProjectIdProvider;
import com.google.cloud.spring.core.GcpProjectIdProvider;
import com.google.cloud.spring.pubsub.PubSubAdmin;
import com.google.cloud.spring.pubsub.core.PubSubConfiguration;
import com.google.cloud.spring.pubsub.core.PubSubException;
import com.google.cloud.spring.pubsub.core.subscriber.PubSubSubscriberTemplate;
import com.google.cloud.spring.pubsub.support.CachingPublisherFactory;
import com.google.cloud.spring.pubsub.support.DefaultPublisherFactory;
import com.google.cloud.spring.pubsub.support.DefaultSubscriberFactory;
import com.google.cloud.spring.pubsub.support.PublisherFactory;
import com.google.cloud.spring.pubsub.support.SubscriberFactory;
import com.turbospaces.cfg.ApplicationProperties;
import com.turbospaces.common.PlatformUtil;
import com.turbospaces.ups.RawServiceInfo;
import com.turbospaces.ups.UPSs;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;

@Configuration
public class PubsubSpringDiModule implements DisposableBean {
    protected String url(DynamicCloud cloud) throws IOException {
        RawServiceInfo si = UPSs.findRequiredServiceInfoByName(cloud, UPSs.PUBSUB);
        return si.read();
    }
    @Bean
    public GcpProjectIdProvider gcpProjectIdProvider(ApplicationProperties props) {
        if (props.isDevMode()) {
            return () -> "local-project-id";
        }
        return new DefaultGcpProjectIdProvider();
    }

    @Bean
    public CredentialsProvider credentialsProvider(DynamicCloud cloud) throws IOException {
        Optional si = UPSs.findServiceInfoByName(cloud, UPSs.UPS_GCV);
        if (si.isPresent()) {
            Credentials credentials = GoogleCredentials.fromStream(si.get().toByteSource().openBufferedStream());
            return () -> credentials;
        }
        return NoCredentialsProvider.create();
    }

    @Bean
    public PubSubConfiguration pubSubConfiguration(ApplicationProperties props, GcpProjectIdProvider projectIdProvider) {
        var config = new PubSubConfiguration();
        config.initialize(projectIdProvider.getProjectId());
        config.getSubscriber().setParallelPullCount(props.PUBSUB_PARALLEL_PULL_COUNT.get());
        if (props.PUBSUB_OVERRIDE_SUBSCRIBER_FACTORY_CONFIG.get()) {
            config.getSubscriber().setMaxDurationPerAckExtension(props.PUBSUB_MOD_ACK_DEADLINE_MAX_EXTENSION_DURATION.get().toSeconds());
        }
        return config;
    }

    //
    // ~ publisher
    //

    @Bean
    public ThreadPoolTaskScheduler pubsubPublisherThreadPool(PubSubConfiguration config) {
        var scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(config.getPublisher().getExecutorThreads());
        scheduler.setAcceptTasksAfterContextClose(config.getPublisher().getExecutorAcceptTasksAfterContextClose());
        scheduler.setWaitForTasksToCompleteOnShutdown(config.getPublisher().getExecutorWaitForTasksToCompleteOnShutdown());
        scheduler.setAwaitTerminationMillis(config.getPublisher().getExecutorAwaitTerminationMillis());
        scheduler.setThreadNamePrefix("gcp-pubsub-publisher");
        scheduler.setDaemon(true);
        return scheduler;
    }

    @Bean
    public ExecutorProvider publisherExecutorProvider(@Qualifier("pubsubPublisherThreadPool") ThreadPoolTaskScheduler scheduler) {
        return FixedExecutorProvider.create(scheduler.getScheduledExecutor());
    }

    @Bean
    public TransportChannelProvider publisherTransportChannelProvider(
            ApplicationProperties props,
            DynamicCloud cloud,
            @Qualifier("emulatorTransportChannelProvider") TransportChannelProvider emulatorChannelProvider) throws IOException {
        if (isLocalHost(cloud)) {
            return emulatorChannelProvider;
        }
        return PublisherStubSettings.defaultGrpcTransportProviderBuilder()
                .setEndpoint(url(cloud))
                .setKeepAliveTime(Duration.ofMinutes(props.PUBSUB_KEEP_ALIVE_DURATION.get().toMinutes()))
                .build();
    }

    @Bean
    public PublisherFactory publisherFactory(
            ApplicationProperties props,
            DynamicCloud cloud,
            GcpProjectIdProvider projectIdProvider,
            @Qualifier("publisherExecutorProvider") ExecutorProvider executorProvider,
            CredentialsProvider credentialsProvider,
            @Qualifier("publisherTransportChannelProvider") TransportChannelProvider channelProvider) throws IOException {
        var factory = new DefaultPublisherFactory(projectIdProvider);
        factory.setExecutorProvider(executorProvider);
        factory.setCredentialsProvider(credentialsProvider);
        factory.setChannelProvider(channelProvider);
        factory.setEnableMessageOrdering(props.PUBSUB_MESSAGE_ORDERING_ENABLED.get());
        factory.setEndpoint(url(cloud));
        if (props.PUBSUB_PRODUCER_BATCHING_ENABLED.get()) {
            factory.setBatchingSettings(BatchingSettings.newBuilder()
                    .setElementCountThreshold((long) props.PUBSUB_PRODUCER_BATCHING_MESSAGE_COUNT.get())
                    .setRequestByteThreshold((long) props.PUBSUB_PRODUCER_BATCHING_BYTES_COUNT.get())
                    .setDelayThreshold(org.threeten.bp.Duration.ofMillis(props.PUBSUB_PRODUCER_BATCHING_DELAY.get().toMillis()))
                    .build());
        }
        return new CachingPublisherFactory(factory);
    }

    //
    // ~ subscriber
    //

    @Bean
    public TransportChannelProvider subscriberTransportChannelProvider(
            ApplicationProperties props,
            DynamicCloud cloud,
            @Qualifier("emulatorTransportChannelProvider") TransportChannelProvider emulatorChannelProvider) throws IOException {
        if (isLocalHost(cloud)) {
            return emulatorChannelProvider;
        }
        return SubscriberStubSettings.defaultGrpcTransportProviderBuilder()
                .setEndpoint(url(cloud))
                .setKeepAliveTime(Duration.ofMinutes(props.PUBSUB_KEEP_ALIVE_DURATION.get().toMinutes()))
                .build();
    }

    @Bean
    public SubscriberFactory subscriberFactory(
            DynamicCloud cloud,
            GcpProjectIdProvider projectIdProvider,
            CredentialsProvider credentialsProvider,
            PubSubConfiguration pubSubConfiguration,
            @Qualifier("subscriberTransportChannelProvider") TransportChannelProvider channelProvider) throws IOException {
        var factory = new DefaultSubscriberFactory(projectIdProvider, pubSubConfiguration);
        factory.setCredentialsProvider(credentialsProvider);
        factory.setChannelProvider(channelProvider);
        factory.setPullEndpoint(url(cloud));
        return factory;

    }

    @Bean
    public Executor pubSubAcknowledgementExecutor(PubSubConfiguration config) {
        var ackExecutor = new ThreadPoolTaskExecutor();
        ackExecutor.setMaxPoolSize(config.getSubscriber().getMaxAcknowledgementThreads());
        ackExecutor.setThreadNamePrefix("gcp-pubsub-ack-executor");
        ackExecutor.setDaemon(true);
        return ackExecutor;
    }

    @Bean
    public PubSubSubscriberTemplate pubSubSubscriberTemplate(SubscriberFactory subscriberFactory,
            @Qualifier("pubSubAcknowledgementExecutor") Executor ackExecutor) {
        var pubSubSubscriberTemplate = new PubSubSubscriberTemplate(subscriberFactory);
        pubSubSubscriberTemplate.setAckExecutor(ackExecutor);
        return pubSubSubscriberTemplate;
    }

    //
    // ~ admin
    //

    @Bean
    public PubSubAdmin pubSubAdmin(GcpProjectIdProvider projectIdProvider, TopicAdminClient topicAdminClient,
            SubscriptionAdminClient subscriptionAdminClient) {
        return new PubSubAdmin(projectIdProvider, topicAdminClient, subscriptionAdminClient);
    }

    @Bean
    public TopicAdminClient topicAdminClient(TopicAdminSettings topicAdminSettings) {
        try {
            return TopicAdminClient.create(topicAdminSettings);
        } catch (IOException ioe) {
            throw new PubSubException("An error occurred while creating TopicAdminClient.", ioe);
        }
    }

    @Bean
    public TopicAdminSettings topicAdminSettings(CredentialsProvider credentialsProvider,
            @Qualifier("publisherTransportChannelProvider") TransportChannelProvider channelProvider) {
        try {
            return TopicAdminSettings.newBuilder()
                    .setCredentialsProvider(credentialsProvider)
                    .setTransportChannelProvider(channelProvider)
                    .build();
        } catch (IOException ioe) {
            throw new PubSubException("An error occurred while creating TopicAdminSettings.", ioe);
        }
    }

    @Bean
    public SubscriptionAdminClient subscriptionAdminClient(
            CredentialsProvider credentialsProvider,
            @Qualifier("subscriberTransportChannelProvider") TransportChannelProvider channelProvider) throws IOException {
            return SubscriptionAdminClient.create(
                    SubscriptionAdminSettings.newBuilder()
                            .setCredentialsProvider(credentialsProvider)
                            .setTransportChannelProvider(channelProvider)
                            .build());
    }

    //
    // ~ emulator
    //

    private ManagedChannel channel;

    @Bean
    public ManagedChannel channel(DynamicCloud cloud) throws IOException {
        this.channel = ManagedChannelBuilder.forTarget(url(cloud)).usePlaintext().build();
        return channel;
    }

    @Bean
    public TransportChannelProvider emulatorTransportChannelProvider(ManagedChannel ch) {
        return FixedTransportChannelProvider.create(GrpcTransportChannel.create(ch));
    }

    @Override
    public void destroy() throws Exception {
        if (this.channel != null) {
            this.channel.shutdown();
        }
    }

    private boolean isLocalHost(DynamicCloud cloud) throws IOException {
        String url = url(cloud);
        return PlatformUtil.isLocalHost(url) || url.contains("localhost");
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy