Reputation: 9018
Looking for a best design for the following use case.
Use case: In my application, I have a scheduler running every one hour and exposing a service.
The scheduler runs every one hour and retrieves records from a particular table and sends notification if any record processing fails. The notification could be of email or SMS.
Also, this service could be invoked by other services for record processing failure notification( Email or SMS).
The failure type will be of batchFailure or serviceFailure.
Also, there are some prop settings class to enable or disable SMS & Email notification.
public class Settings {
private boolean emailEnabled;
private boolean smsEnabled;
}
The email subject should contain Subject as "Batch failure" or "Service failure" based on the type. The other content remains same for both batch job and service failure.
I created the following classes:-
Subject - I created a common subject for both.
public class Notification {
private int recordId;
private String recordName;
private String email; // Related to email
private String smsNumber; // Related to sms
}
Listener class
public interface RecordFailureListener {
void sendNotification(Notification notification);
}
EmailListener
public class EmailListener implements RecordFailureListener {
void sendNotification(Notification notification) {
// Code to send email here
}
}
SMSListener
public class SMSListener implements RecordFailureListener {
void sendNotification(Notification notification) {
// Code to send SMS here
}
}
Service class
public class NotificationService {
List<RecordFailureListener> listeners = new ArrayList(); // This list has emailListener and smsListener
void sendNotification(Notification notification) {
listeners.forEach(listener -> listener.sendNotification(notification));
}
}
This notification service is being invoked from scheduler for any failure. Also, from the failure service being exposed.
Question:
1) Here, the subject seems to be having the properties required for email and sms. Is there any other better way so that email notificaiton will have properties required for email and sms notification will have properties required for sms? and there are some common properties also.
2) Check if email is been enabled or not in the email listener before calling send email and same in sms listener. Is this the right place?
Any other better design?
Thanks
Upvotes: 3
Views: 3084
Reputation: 143
Just sharing my design pattern,little old question though.
Settings can be validated in NotificationService itself, since this is where we are starting notification flow. Assuming NotificationService is being invoked from Scheduler for failure notification
public class NotificationService {
Notification notification;
static List<RecordFailureListener> listenerList = new ArrayList<>(); // holds email & sms listeners
public NotificationService (Notification notification){
this.notification=notification;
}
public void sendNotification(){
if(Settings.emailEnabled){
listenerList.stream().filter(i-> i instanceof EmailListener).forEach(i->i.sendNotification(notification));
}
if(Settings.smsEnabled){
listenerList.stream().filter(i-> i instanceof SmsListener).forEach(i->i.sendNotification(notification));
}
}
}
RecordeFailureListener
public interface RecordFailureListener {
void sendNotification(Notification notification);
}
EmailListener (FailureType can validated here to assign respective subject line for email)
public class EmailListener implements RecordFailureListener {
private String emailSubject;//emailistener specific property
@Override
public void sendNotification(Notification notification) {
if(Stream.of("BatchFailure","ServiceFailure").anyMatch(notification.getFailureType()::equalsIgnoreCase)){
emailSubject= notification.getFailureType();
}
else{
emailSubject="customSubject";
}
//code to send email notification
}
}
SMSListener
public class SmsListener implements RecordFailureListener {
@Override
public void sendNotification(Notification notification) {
//code to sms notification
}
}
Settings
public class Settings {
public static boolean emailEnabled;
public static boolean smsEnabled;
}
Notification model(have added failureType as Notification property)
public class Notification {
private int recordId;
private String recordName;
private String email;
private String smsnumber;
private String failureType;
}
Upvotes: 0
Reputation: 4090
You problem is the classic notifier/listener problem. I would suggest using an event bus as well, as a means of decoupling the lisetner and the notifier. The idea is that the notifier will notify the event bus, and the event bus will go over the listeners and update them on notification. The design is as follows : (This is a bare skeleton, with some convenient short cuts, such as using an enum singleton for the EventBus. the fine details are up to you).
public interface Event {
// general event information - date, message, meta data, etc....
}
public interface Listener {
public void listen(Event event);
}
public enum EventBus {
INSTANCE;
private List<Listener> listeners = new ArrayList<>;
public void addListener(Listener newListener) {
listeners.add(newListener);
}
public void notify(Event event) {
for (Listener listener : listeners) {
listener.listen(event);
}
}
}
public class Notifier {
public void handlePointOfInterest(.....) {
// do something interesting with the parameters, etc...
Event event = // create a new event based on your needs
EventBus.INSTANCE.notify(event);
...
}
}
Your event interface can also use generics in order to support specific meta data types - such as your settings. You Listener implementations can use generics in order to handle specific types of events.
Hope this helps, feel free to ask for clarifications.
Upvotes: 2