Hello world, but with asynchronous reply by email

Prev Next

Goal

We want to add asynchronous email sending to an Hello World Application already hosted on Poja that exposes the /hello?to=email@address.com endpoint, for which it answers ... world! and sends an email to the specified email address.

How-to in 2 steps

Step 1: Add email sending to your endpoint

package com.my.company.endpoint.rest.controller;

import com.my.company.mail.Email;
import com.my.company.mail.Mailer;
import jakarta.mail.internet.InternetAddress;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@AllArgsConstructor
public class HelloWorldController {
  private final Mailer mailer;

  @GetMapping("/hello")
  @SneakyThrows
  public String helloWorld(@RequestParam String to) {
    var email =
        new Email(new InternetAddress(to), List.of(), List.of(), "Hello world", "... world!", List.of());

    mailer.accept(email);
    return "... world!";
  }
}

Step 2: Make it asynchronous

Update the environment’s configuration

Select the preprod environment to update, click one Edit button on the Poja Configuration section and then:

  • Set Queues NB to 2

  • Click on the Save button at the bottom

Updating environment configuration

When the Poja Configuration of an environment is modified:

  • Changes will be pushed on the environment’s branch (prod or preprod)

  • A new deployment will be triggered for the specified environment

Write the asynchronous code

In the context of Poja, asychronous code refers to an Event driven code executing in Workers. Follow the instructions below to write the asynchronous email sending:

  • Create the event class SendEmailRequested with the property to which refers to the recipient address of the email inside the package com.my.company.endpoint.event.model

  • Create the service SendEmailRequestedService which will consume the event object and send the email inside the package com.my.company.service.event

  • Produce the event when the user sends a request to the /hello endpoint and provides an email address

Important !

  • Event classes must be located inside the your.package.name.endpoint.event.model package

  • Event processing services must be located inside the your.package.name.service.event

  • Event processing services should be named must be named as follows {event_name}Service
    Example: SendEmailRequested and SendEmailRequestedService

package com.my.company.endpoint.event.model;

import java.time.Duration;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;

@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@Data
@EqualsAndHashCode(callSuper = false)
@ToString
public class SendEmailRequested extends PojaEvent {
  private String to;

  @Override
  public Duration maxConsumerDuration() {
    return Duration.ofSeconds(45);
  }

  @Override
  public Duration maxConsumerBackoffBetweenRetries() {
    return Duration.ofSeconds(30);
  }
}
package com.my.company.service.event;

import com.my.company.endpoint.event.model.SendEmailRequested;
import com.my.company.mail.Email;
import com.my.company.mail.Mailer;
import jakarta.mail.internet.InternetAddress;
import java.util.List;
import java.util.function.Consumer;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;

@Service
@AllArgsConstructor
public class SendEmailRequestedService implements Consumer<SendEmailRequested> {
  private final Mailer mailer;

  @SneakyThrows
  @Override
  public void accept(SendEmailRequested sendEmailRequested) {
    InternetAddress recipientAddress = new InternetAddress(sendEmailRequested.getTo());
    mailer.accept(new Email(recipientAddress, List.of(), List.of(), "", "... world!", List.of()));
  }
}

Now, update the previously created HelloWorldController to produce an event instead of directly sending the email:

package com.my.company.endpoint.rest.controller;

import com.my.company.endpoint.event.EventProducer;
import com.my.company.endpoint.event.model.SendEmailRequested;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@AllArgsConstructor
public class HelloWorldController {
  private final EventProducer<SendEmailRequested> eventProducer;

  @GetMapping("/hello")
  @SneakyThrows
  public String helloWorld(@RequestParam String to) {
    var event = SendEmailRequested.builder().to(to).build();
    eventProducer.accept(List.of(event));
    return "... world!";
  }
}

To trigger a deployment, just commit and push the code to the preprod branch of the repository. Wait. And voilà!