Reputation: 25
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
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
Reputation: 44715
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.
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.
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
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