alexey-ignatiev
alexey-ignatiev

Reputation: 25

Is there a beautiful solution for disable all email notification?

I have a mail service which simply send notification to recipients.

@Service
public class MailService {

        @Autowired
        private JavaMailSender mailSender;

        public void send(String subject, String text, String... emails) {
                // MailMessage object configuration
                mailSender.send(mail);
        }
}

And then I have a rest controller that calls this method after some business logic.

@RestController
public class MyRestController {

        @Autowired
        private MailService mailService;

        @PostMapping("/orders")
        public void createOrders(@RequestBody List<Order> orders) {
                // manipulations with orders
                mailService.send("Order notification", "New orders", "[email protected]");
        }
}

How should I design an application to turn off notifications for different spring profiles? Are there best practices for configuring email distribution depending on the profile?

Upvotes: 2

Views: 3452

Answers (3)

HappyPanda
HappyPanda

Reputation: 774

I'd suggest to add a variable (for instance sendEmails=false) to application-[profile-name].properties files with different settings for each profile.

Then in your code you can use this property to decide if the emails should be sent.

@Service
public class MailService {

    @Value("sendEmails}")
    private boolean sendEmails;

    @Autowired
    private JavaMailSender mailSender;

    public void send(String subject, String text, String... emails) {
        if (sendEmails) {
            // MailMessage object configuration
            mailSender.send(mail);
        }
    }
}

This approach enables you to use different (and easily changable) settings for each profile.

Upvotes: 0

Dimitri Mestdagh
Dimitri Mestdagh

Reputation: 44715

Using multiple implementations

One possibility is by creating a MailService interface with two implementations. By combining these implementations with the @Profile annotation, you can properly inject one or the other implementation depending on which profile you use. For example:

public interface MailService {
    void send(String subject, String text, String... emails);
}

And then you can add the @Profile("mail") annotation to one implementation:

@Service
@Profile("mail") // Only create this bean when the 'mail' profile is used
public class JavaMailServiceImpl implements MailService {

    @Autowired
    private JavaMailSender mailSender;

    public void send(String subject, String text, String... emails) {
        // MailMessage object configuration
        mailSender.send(mail);
    }
}

Additionally, you can add another implementation for when the mail profile isn't active:

@Service
@Profile("!mail") // By adding the exclamination mark, this implementation will be used when the mail profile isn't active
public class NoopMailServiceImpl implements MailService {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public void send(String subject, String text, String... emails) {
        logger.debug("Dummy implementation, no e-mail is being sent");
    }
}

If you now autowire MailService into your controller, it will properly implement the correct implementation depending on which profile you're using.


Using aspects

Another alternative is to decouple sending mails entirely from the order creation logic by using aspects.

This allows you to remove the MailService dependency from the MyRestController controller entirely:

@RestController
public class MyRestController {

    @PostMapping("/orders")
    public void createOrders(@RequestBody List<Order> orders) {
        // manipulations with orders
    }
}

In stead of that, you add the mailService logic to a separate aspect:

@Aspect
@Component
@Profile("mail")
public class OrderNotificationAspect {
    private final MailService mailService;

    public OrderNotificationAspect(MailService mailService) {
        this.mailService = mailService;
    }

    @AfterReturning("execution(* com.example.MyRestController.createOrders(..))")
    public void sendNotification() {
        mailService.send("Order notification", "New orders", "[email protected]");
    }
}

Like before, we use the @Profile annotation to only register the aspect bean when the mail profile is active. However, since we're no longer directly tying the controller to the MailService we no longer need the "dummy implementation".

To use aspects within Spring boot, you should add the spring-boot-starter-aop dependency.


Checking profiles programmatically

Another option is to write the check within your code, for example:

@RestController
public class MyRestController {

    @Autowired
    private MailService mailService;
    @Autowired
    private Environment environment;  

    @PostMapping("/orders")
    public void createOrders(@RequestBody List<Order> orders) {
        // manipulations with orders
        if (environment.acceptsProfiles(Profiles.of("mail"))) {
            mailService.send("Order notification", "New orders", "[email protected]");
        }
    }
}

Depending on how fine-grained you want this logic to be, you can either put it in the controller, or in the service (if you want to disable all emails being sent for a specific profile).


Which option you choose depends on your needs. Using multiple implementations is the easiest way to disable all mails from being sent.

However, if you only want to disable certain mails from being sent, you'll have to choose another way. The downside of using aspects is that it makes the flow sometimes a bit more difficult to follow, though IDE's have pretty good support for aspects.

Upvotes: 2

Strelok
Strelok

Reputation: 51461

Just have a dummy implementation for profiles that need it

@Bean
@Profile("prod")
public JavaMailSender getRealJavaMailSender() {
    JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
    mailSender.setHost("smtp.gmail.com");
    mailSender.setPort(587);
     
    mailSender.setUsername("[email protected]");
    mailSender.setPassword("password");
     
    Properties props = mailSender.getJavaMailProperties();
    props.put("mail.transport.protocol", "smtp");
    props.put("mail.smtp.auth", "true");
    props.put("mail.smtp.starttls.enable", "true");
    props.put("mail.debug", "true");
     
    return mailSender;
}

@Bean
@Profile("test")
public JavaMailSender getDummyJavaMailSender() {
    return new JavaMailSender() {
        ... dummy method implementations ...
    };
}

Upvotes: 2

Related Questions