Google Cloud Pub/Sub with Spring Boot and Java

In this step by step guide, we will learn how to set up Google Cloud Pub/Sub with Spring Boot Java application and Jackson JSON.
A featured image for category: Spring

1. Introduction

Google Cloud Pub/Sub might be a great tool to integrate decoupled systems hosted on GCP as well as elsewhere on the Internet. In this step-by-step guide, we will learn how to set up Google Cloud Pub/Sub with Spring Boot Java application.

Moreover, I will show you how to configure it for serialization and deserialization of POJOs using Jackson JSON.

2. Setup Google Cloud Project

2.1. Create Topic and Subscription

As the first step, let’s go to the GCP console, and let’s enable the Pub/Sub API for our future Spring Boot project by clicking the Enable button.

Alternatively, we can do the same with a gcloud CLI command:

gcloud services enable pubsub.googleapis.com

As the next step, let’s go to the Topics tab in the left sidebar and create a new topic named example:

The image contains a screenshot from Google Cloud Platform console showing how to create a topic, which we will use to integrate pub/sub with Spring Boot Kotlin application later.
Or again, with a gcloud command:

gcloud pubsub topics create example

Nextly, let’s go to the Subscriptions tab and click the create subscription button.

On this page, we can specify the subscription ID. Let’s call it an example-subscription and choose our previously created topic:

The image contains a screenshot from Google Cloud Platform console showing how to create a subscription for a Cloud Pub/Sub topic

Or, as usual, it can be done using CLI command:

gcloud pubsub subscriptions create example-subscription --topic=example

If we would like to validate whether everything has been created, we can check it with these two commands:

gcloud pubsub subscriptions describe example-subscription
gcloud pubsub topics describe example

2.2. Setup Service Account

In this paragraph, we will learn how to create a service account, which will be used later in our Spring Boot project.

As the first step, let’s go to the GCP IAM & Admin console and select the Service Accounts tab. Then, let’s click the create service account button and specify the name as follows:
The image contains a screenshot from Google Cloud Platform console showing how to create a service account

 

Nextly, we need to click continue, and on the next page, let’s add Pub/Sub Admin role:

The image contains a screenshot from Google Cloud Platform console showing how to add the Pub/Sub admin role to the service account

After the role has been added, we need to click next and finally click done. We will be redirected to the page showing all service accounts in our project. Let’s click the three dots near to the recently created Pub/Sub service account and click Create key:

The image contains a screenshot from Google Cloud Platform console showing how to create a key
As the key type, let’s select the JSON:

The image contains a screenshot from Google Cloud Platform console showing how to choose the JSON key

 

As the last step, please download the file and put it inside the resources directory of the Spring Boot project.

3. Imports

If all the Pub/Sub steps above have been completed, we can switch to the Spring Boot project. Firstly, let’s import two dependencies we will need for our project to work:

  
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-gcp-starter-pubsub'

4. Configure Application Properties

As the next step, let’s define the application.yaml file as follows:

 
pubsub-example:
  subscription:
    name: ${EXAMPLE_PUBSUB_SUBSCRIPTION}
  topic:
    name: ${EXAMPLE_PUBSUB_TOPIC}

spring:
  cloud:
    gcp:
      credentials:
        location: classpath:credentials.json

Please notice, the name of the JSON should match the name of the file that we’ve put inside the resources folder. Also, the environment variables describing subscription and topic should be set to the correct values, for example:

  • EXAMPLE_PUBSUB_SUBSCRIPTION -> projects/[PROJECT-ID]/subscriptions/example-subscription
  • EXAMPLE_PUBSUB_TOPIC -> projects/[PROJECT-ID]/topics/example

6. Create a POJO Class

Our next step is to create a simple POJO class, which will be used for the serialization and deserialization of the messages sent through the Pub/Sub:

  
public class MessageEntity {

    private final LocalDateTime timestamp;
    private final String message;
    
    //constructor, getters, setters
}

As you can see, it is a simple class containing the timestamp and the value of the message.

7. Set Up The Consumer

Let’s start the work on the consumer part by creating the PubSubConsumer interface:

  
public interface PubSubConsumer {

    String subscription();

    Class payloadType();

    Consumer<ConvertedBasicAcknowledgeablePubsubMessage> messageConsumer();
}

As you can see, this interface consists of three methods definitions:

  • subscription -> returns the name of the subscription
  • payloadTyme -> returns the Class object representing the target type of the payload
  • messageConsumer -> returns the callback method triggered when new messages arrive

Technically, we don’t need this interface, but with this approach, we will be able to add easily more consumers in the future.

Secondly, let’s create the ExampleCosumer class implementing the interface:

  
@Component
public class ExampleConsumer implements PubSubConsumer {

    private static final Logger log = LoggerFactory.getLogger(ExampleConsumer.class);
    private final String subscriptionName;

    public ExampleConsumer(
        @Value("${pubsub-example.subscription.name}") String subscriptionName
    ) {
        this.subscriptionName = subscriptionName;
    }
}

As you might have noticed, our class contains two fields: a logger and a subscriptionName which will be bound with the value from our properties.yaml file.

Please remember, that our class implements PubSubConsumer, so as the next step we need to provide the definition for each method:

  
@Override
public String subscription() {
    return subscriptionName;
}

@Override
public Class payloadType() {
    return MessageEntity.class;
}

@Override
public Consumer<ConvertedBasicAcknowledgeablePubsubMessage> messageConsumer() {
    return this::consume;
}

To keep our code more readable, let’s implement our consumer method as a separate function:

  
private void consume(ConvertedBasicAcknowledgeablePubsubMessage message) {
    MessageEntity received = message.getPayload();

    log.info(
        "Received entity: [timestamp= {}, message= {}]",
        received.getTimestamp(),
        received.getMessage()
    );

    message.ack();
}

To put it simply, this method extracts the payload from the ConvertedBasicAcknowledgeablePubsubMessage object and logs the timestamp and the message at the INFO level. In the end, it asynchronously acknowledges the message.

As the next step, let’s create ExampleSubscriberConfig class and put the configuration for our subscriber there:

  
@Configuration
public class ExampleSubscriberConfig {

    private static final Logger log = LoggerFactory.getLogger(ExampleSubscriberConfig.class);

    private final PubSubTemplate pubSubTemplate;
    private final PubSubConsumer exampleConsumer;

   //constructor
}

As you might have noticed, besides the logger and the exampleConsumer, we are injecting pubsubTemplate, which will be used for publishing to topics and consuming messages.

Let’s implement the method responsible for subscription:

  
@EventListener(ApplicationReadyEvent.class)
public void subscribe() {
    log.info("Subscribing to {}", exampleConsumer.subscription());

    pubSubTemplate.subscribeAndConvert(
        exampleConsumer.subscription(),
        exampleConsumer.messageConsumer(),
        exampleConsumer.payloadType()
    );
}

The annotation  EventListener used with ApplicationReadyEvent indicates, that the method will be called when the application is ready to receive requests.

As the last step, we need to provide the bean definition for a converter using Jackson JSON:

  
@Configuration
public class PubSubConfig {

    @Bean
    public JacksonPubSubMessageConverter jacksonPubSubMessageConverter(ObjectMapper objectMapper) {
        return new JacksonPubSubMessageConverter(objectMapper);
    }
}

With that being done, we are ready to receive the messages.

8. Set Up The Publisher

Besides receiving the messages, it would be good to have the possibility to publish some. Let’s start the work on the publisher part by creating the ExamplePublisher class:

  
@Component
public class ExamplePublisher {

    private final String topic;
    private final PubSubTemplate pubSubTemplate;

    public ExamplePublisher(
        @Value("${pubsub-example.topic.name}") String topic,
        PubSubTemplate pubSubTemplate) {
        this.topic = topic;
        this.pubSubTemplate = pubSubTemplate;
    }
}

As we can see, we need to inject the PubSubTemplate bean and the topic, which will be set to the value from the application properties.

Let’s add a publish method then. It will take the MessageEntity as the argument:

  
public void publish(MessageEntity payload) {
    pubSubTemplate.publish(topic, payload);
}

As you might have noticed, this method uses the publish method to push the object to the specified Pub/Sub topic.

9. Create Example Controller

And finally, we can implement the TestController class:

  
@RestController
@RequestMapping("/api")
public class TestController {

    private final ExamplePublisher publisher;

    public TestController(ExamplePublisher publisher) {
        this.publisher = publisher;
    }

    @PostMapping("/topic")
    public void publish(@RequestBody String message) {
        MessageEntity entity = new MessageEntity(LocalDateTime.now(), message);
        publisher.publish(entity);
    }
}

It’s just a simple REST controller, which will be responding to the POST /api/topic calls and forwarding the message to the publisher.

10. Testing Using Curl

Lastly, there is nothing else to do than just test our application with the following command:

  
curl -d 'Some message' localhost:8080/api/topic

After it executes, we should see a new entry in our application logs (and each time we call the endpoint as well).

9. GCP Pub/Sub With Spring Boot Conclusion

And I believe, that’s all for today. In this step-by-step tutorial, we’ve learned how to publish and receive messages through the Google Cloud Pub/Sub using the Spring Boot Java application.

I hope that you’ve really enjoyed this guide. I would be forever grateful if you would like to share some feedback with me. Would you like to see more Java tutorials as well? Please, let me know about it using our page, group, or contact form.

For the source code of the project, please visit our project on GitHub.

Share this:

Related content

Newsletter
Image presents 3 ebooks with Java, Spring and Kotlin interview questions.

Never miss any important updates from the Kotlin world and get 3 ebooks!

You may opt out any time. Terms of Use and Privacy Policy