Every programmer who has developed a web or a form application has experienced event based programming practices. When an user clicks a button or types something in a textfield or closes a window, the respective component fires an event and lets other parts of application handle it or simply ignore. Here I want to describe how to fire an event, listen it and take necessary action using Spring Framework. This approach may give us a different perspective and makes us a bit better programmer altogether.
What’s an event?
When I take a look at Cambridge dictionary to describe what an event is, it gives me a definition as follows
anything that happens, especially something important or unusual:
https://dictionary.cambridge.org/dictionary/english/event
I think this quote is an excellent lighthouse for us to understand event. Let’s check part by part.
Anything that happes: We can think of every action of our application as an event like saving a data, having a condition or even each line of code performed. We can classify all the lines as chain of events, however it would be a bit overenginereed. Why? check the second part
Especially something important or unusual: No every line of code is something important. When we say something important, it should be meaningful to everyone. For example from our lives, it is most like marriage, losses or memorable moments. In this occasions we like sending some messages to our beloved ones. We use different mediums like social media which I am not effective at it or may be direct messaging. Here I prefer to use social media, because it reminds me of an event pusblisher in this topic.
I think we are ok about events, something important or unusual. We can start with the easy part. For better understanding I will give you two different java examples.
Listening an event using standard Java
We will start with a small example which is easy to understand. We will create a standalone application, create a form, put a button to the form and write a listener for that button. If you want to develop the application to make hands-on experience it, you can follow the steps.
- I want you to create a simple java application with your favorite IDE/Text editor. We do not need to add any library other then regular JDK/JRE.
- Create a class named Main. That’s it. We are ready.
import javax.swing.JFrame; public class Main extends JFrame { public static void main(String[] args) { Main m = new Main(); m.setBounds(100, 100, 500, 300); m.setDefaultCloseOperation(EXIT_ON_CLOSE); m.setVisible(true); } }
This code will create a form and make it show on the screen. Years later, I am developing a swing application, good old memories. Anyway. I gave some boundaries which is appropriate for me and set form default close operation of EXIT_ON_CLOSE . That means when we click “x” button on top of form the application will be closed directly.
Now we will add a button to the form.
import java.awt.FlowLayout; import javax.swing.JButton; import javax.swing.JFrame; public class Main extends JFrame { public static void main(String[] args) { Main m = new Main(); m.setBounds(100, 100, 500, 300); m.setDefaultCloseOperation(EXIT_ON_CLOSE); JButton btn = new JButton("Click this and see what happens"); m.setLayout(new FlowLayout()); m.add(btn); m.setVisible(true); } }
I set the layout of form as flow layout which is the most basic layout. For further information you can have a look Java Swing Layouts. I have always liked java standard documentation.
What happens when we click this button? The answer to this question varies according to the position you are taking.
- When we run the application and click the button, nothing happens from the user perspective.
- When we click the button, an event is being fired by JRE swing library. We need to listen it to change the behaviour from the application perspective. Since we did not develop any listener for this button, we ignored it simply.
Let’s develop an event listener and change the behaviour.
import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JOptionPane; public class Main extends JFrame { public static void main(String[] args) { Main m = new Main(); m.setBounds(100, 100, 500, 300); m.setDefaultCloseOperation(EXIT_ON_CLOSE); JButton btn = new JButton("Click this and see what happens"); m.setLayout(new FlowLayout()); m.add(btn); btn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("Here some additional operation. Writing this to console."); JOptionPane.showMessageDialog(m, "Now we are listening"); } }); m.setVisible(true); } }
Here we have some important points we should not skip,
- We did not create any event and did not fired it.
- Our code listens the events which are being triggered by the component
- We did not “set” listener, we “added”. Just take a look at “btn.addActionListener” method of button. This means we can raise the number of listeners. All will be triggered when an action is performed by a user.
Please keep in mind that a button in swing library has number of types of listeners. Action listener is not the only listener, for further details you can visit here.
We had understood about listening events, basically we are registering ourselves for a component with a specific type of event. The component is responsible to trigger all the listeners.
Firing an event
We came to a very important question, how do we trigger our own event? When something important happens in our application, we want to trigger an event. The other guys (developers) should develop their particular business needs without touching our core part. From now on, we will jump back to a Spring application.
I have already mentioned event publisher earlier in this post. While we are listening events we do not need to know how the event was published, we only register ourselves to the event publisher.
Event publisher reminds me of social media. Twitter may be. As we know millions of events happening in a day on twitter and we do not know 99.999 percent of them. We only care about important people for us and registering for their events using Add Listener functionaly called “follow”. Once they tweet something (fire an event), the event is being posted to our timeline (event queue) and we take action whatever it is :).
While we are developing our application, our event publisher is provided by Spring Framework. We do not need to create a event queue, listeners interfaces etc. We simply autowire the publisher to our event and it is ready to use, very easy. It is even easier than I expect. In my earlier years in programming, I was thinking how to develop an event mechanism, it was so hard for me to think. Spring framework kind of live saver for us.
It is better to think of a scenario so that we can implement it.
Defining a scenario
Let’s assume that we are developing a bank application’s credit card create functionality.
Creating a card is something important. When application creates a card, application;
- Sends a welcome email
- Assignes a welcome bonus
- Informs customer care department about the newcomer. May be for sending pyhsical card.
Here the developer who develops the core application can fire an event when credit card created. The other developers can develop their particular parts.
Our model classes are Customer, Card and CardCreatedEvent.
package com.enginaar.eventhandling.model; import lombok.Data; @Data public class Card { private String cardNumber; private Customer owner; }
package com.enginaar.eventhandling.model; import lombok.Data; @Data public class Customer { private String name; private String email; }
package com.enginaar.eventhandling.model; import org.springframework.context.ApplicationEvent; import lombok.Data; public class CardCreatedEvent extends ApplicationEvent { public CardCreatedEvent(Card source) { super(source); } @Override public Card getSource() { return (Card) source; } }
Firing CardCreatedEvent
After we create a card, we will fire an event. Since we defined our model classes, we are ready to fire. The only requirement is that we need to autowire ApplicationEventPubliser interface to our code.
package com.enginaar.eventhandling; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.event.EventListener; import com.enginaar.eventhandling.model.Card; import com.enginaar.eventhandling.model.CardCreatedEvent; import com.enginaar.eventhandling.model.Customer; @SpringBootApplication public class EventhandlingApplication { private @Autowired ApplicationEventPublisher publisher; public static void main(String[] args) { SpringApplication.run(EventhandlingApplication.class, args); } @EventListener(ApplicationReadyEvent.class) public void startup() { Card ca = new Card(); ca.setCardNumber(UUID.randomUUID().toString()); Customer cus = new Customer(); cus.setName("Kenan Erarslan"); cus.setEmail("[email protected]"); ca.setOwner(cus); System.out.printf("Customer created, %s\n", cus); publisher.publishEvent(new CardCreatedEvent(ca)); System.out.println("CustomerCreatedEvent fired"); } }
Just a small pharantesis, I want to emphesize a part which is small but important in this topic. We have already experienced how to listen an event in Spring Framework. Here @EventListerner annotation comes to the stage. What we are doing here is that we are listening Spring Event Publisher. When Spring constructs the application and ready to perform our code, it fires ApplicationReadyEvent which we have used in our previous examples.
Once application creates a credit card we fire/publishe an event. Here you can see the screenshot.
What happens now? Nothing since we did not handle the listening part.
Listening CardCreatedEvent
For our scenario, we can implement all requirement within a single class, but I prefer seperated implementations per functionality.
package com.enginaar.eventhandling.listener; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; import com.enginaar.eventhandling.model.Card; import com.enginaar.eventhandling.model.CardCreatedEvent; @Component public class EmailSendingListener { @EventListener(CardCreatedEvent.class) public void send(CardCreatedEvent event) { Card source = event.getSource(); System.out.printf("Sending email to %s\n", source.getOwner().getEmail()); } }
package com.enginaar.eventhandling.listener; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; import com.enginaar.eventhandling.model.Card; import com.enginaar.eventhandling.model.CardCreatedEvent; @Component public class WelcomeBonusListener { @EventListener(CardCreatedEvent.class) public void send(CardCreatedEvent event) { Card source = event.getSource(); System.out.printf("Welcome bonus assigned.\nCard Number:%s\nOwner:%s\n", source.getCardNumber(), source.getOwner().getName()); } }
package com.enginaar.eventhandling.listener; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; import com.enginaar.eventhandling.model.Card; import com.enginaar.eventhandling.model.CardCreatedEvent; @Component public class CustomerCareListener { @EventListener(CardCreatedEvent.class) public void send(CardCreatedEvent event) { Card source = event.getSource(); System.out.println("For customer care department"); System.out.printf("New card created\nCard number:%s\nOwner:%s", source.getCardNumber(), source.getOwner().getEmail()); } }
The first listener is EmailSendingListener which we can develop an email sending code using different libraries, Java Mail is the most known I think.
The second is WelcomeBonusListener which is responsible for assigning some bonus point or money to the card when the card is created.
The third and the last is CustomerCareListener which is responsible for informing customer care department about the card details. It might be another email sending operation or direct API call for their internal application etc.
Finally our result. We see the listeners have been triggered and performed their respective businesses. You may wonder why “CustomerCreatedEvent fired” message is written in the end. It is because handling events are synchronous process by default. When an event is fired application it waits for the listeners to finish their works.Then it continues for its lifecycle. If you want to make them async, you can simply use Spring Framework Async functionaly which is another topic to discuss.
Conclusion
Creating event based applications seems very easy when we are thinking. However, I did not experienced developers create their events and apply this practice into their daily life. It might be related to style how we code or how we think. In any case, it is undoubtedly very useful feature which makes our lives easier.
You may define your own events and reduce the complexity of application one step.