Enterprise Java

Java EE Observer Design Pattern Example

Photo of Joel Patrick LlosaJoel Patrick LlosaJanuary 7th, 2019Last Updated: February 26th, 2021
1 368 8 minutes read

This article is about a Java EE Observer Design Pattern Example. The observer pattern is one of the most widely used design patterns in programming. It is implemented in thejava.util package of Java SE 8 asObserver andObservable.

1. Introduction

By extending these classes, we can easily implement the observer pattern. But this article is not about that. We will focus on the Java EE implementation of the observer pattern.

In the observer pattern, an object that changes its state can inform other objects that a change in state has happened. The object that changes its state is the subject. The objects that receive the notification of the change of state are the observers. The observer pattern does this in a decoupled manner so that the subject does not know about the observers.

2. Tools and Requirements

  1. Java 8
  2. WildFly 14.0.1
  3. Eclipse Oxygen
  4. Java EE Kickoff App
  5. Eclipse with WildFly and JBoss Tools Example

The source in this is example is based on the Java EE Kickoff App. We will not go through the details of setting up the project, so it is recommended thatEclipse with WildFly and JBoss Tools Example be read before trying out the example. We’ll be using WildFly 14.x because it is a Java EE 8 full platform compatible implementation.

3. Java EE Observer Design Pattern Implementaiton

In Java EE, the subject and observer are decoupled. Many observers can receive notification about a change in the state of the subject. It relies on theObserves annotation. This annotation marks the observers and the subject uses theEvent class to create and fire events that observers listen for.

4. @Observes and Event

After downloading WildFly 14.x, we add it as one of our servers in Eclipse. We should have something like below:

Observer Pattern java - WildFly 14.x Servers tab
WildFly 14.x Servers tab

Import thejava-ee-observer-pattern project in Eclipse. We will add some code in theAuthBacking class.AuthBacking.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
packageorg.example.kickoff.view.auth;
 
...snipped...
 
publicabstractclassAuthBacking {
 
    ...snipped...
     
    @Inject
    privateEvent<User> userLoginEvent;
 
    ...snipped...
 
    protectedvoidauthenticate(AuthenticationParameters parameters)throwsIOException {
        AuthenticationStatus status = securityContext.authenticate(getRequest(), getResponse(), parameters);
 
        if(status == SEND_FAILURE) {
            addGlobalError("auth.message.error.failure");
            validationFailed();
        }
        elseif(status == SEND_CONTINUE) {
            responseComplete();// Prevent JSF from rendering a response so authentication mechanism can continue.
        }
        elseif(activeUser.hasGroup(ADMIN)) {
            userLoginEvent.fire(user);
            redirect("admin/users");
        }
        elseif(activeUser.hasGroup(USER)) {
            userLoginEvent.fire(user);
            redirect("user/profile");
        }
        else{
            redirect("");
        }
    }
 
    ...snipped...
 
}

Parts of the code have been snipped. Download the project to get the full contents of the code. We will only be discussing what is relevant to the observer pattern. We will not discuss the other parts of the Java EE Kickoff App.

AnEvent instance of typeUser is created by the container and is injected into the subject class. When the fire method is invoked, a notification is fired and any observer listening forUser events will receive the user instance. Any method parameter annotated with@Observes and of typeUser will receive this instance of the user object. This is the observer pattern in its simplest form.

Create the observer classes.

LoginService.java

01
02
03
04
05
06
07
08
09
10
packageorg.example.kickoff.business.service;
 
importjavax.enterprise.event.Observes;
importorg.example.kickoff.model.User;
 
publicclassLoginService {
    publicvoidsaveLoginAttempt(@ObservesUser user) {
        System.out.println("Logged in: "+ user.getEmail());
    }
}

EmailLoginService.java

01
02
03
04
05
06
07
08
09
10
packageorg.example.kickoff.business.service;
 
importjavax.enterprise.event.Observes;
importorg.example.kickoff.model.User;
 
publicclassEmailLoginService {
    publicvoidsendLoginAttemptEmail(@ObservesUser user) {
        System.out.println(user.getEmail() +" has been used to log into Java EE Observer Design Pattern Example.");
    }
}

The above methods with parameters annotated with@Observes will receive a notification when a user logs in. When we log in, we should have the statements above printed.

Observer Pattern java
Java EE Kickoff App Login Page

Try logging as anadmin and then as auser. We should see the output below.

Console Output

1
2
3
4
5
6
11:18:31,908 INFO  [org.omnifaces.eventlistener.FacesRequestLogger] (default task-1) GET={url=/login, user={ip=127.0.0.1, login=null, session=zMpJHJSEodlYr0tY8-giDAciJIDcMdEStJT_6lcQ, viewState=null}, action={source=null, event=null, methods=[], validationFailed=false}, params={}, messages={}, timer={0=220ms, 1=3ms, 2=-1ms, 3=-1ms, 4=-1ms, 5=-1ms, 6=217ms}}
11:18:49,943 INFO  [stdout] (default task-1) Loggedin: admin@kickoff.example.org
 
11:18:49,943 INFO  [stdout] (default task-1) admin@kickoff.example.org has been used to log into Java EE Observer Design Pattern Example.
 
11:18:49,946 INFO  [org.omnifaces.eventlistener.FacesRequestLogger] (default task-1) POST={url=/login, user={ip=127.0.0.1, login=admin@kickoff.example.org, session=pnrQwJj3ao-mJoPd3RmEc_I-ompITHeimrs8XvDw, viewState=stateless}, action={source=loginForm:login,

Those were the basics related to the J2EE observer pattern.

5. @Priority

The order in which observers are invoked is not specified. We can use thePriority annotation to specify the invocation order of the observers. This is a new feature in Java EE 8. The lowest value priority is called first. If the priorities are the same then they are invoked in an uncertain order.

LoginService.java

01
02
03
04
05
06
07
08
09
10
packageorg.example.kickoff.business.service;
 
importjavax.enterprise.event.Observes;
importorg.example.kickoff.model.User;
 
publicclassLoginService {
    publicvoidsaveLoginAttempt(@Observes@Priority(10) User user) {
        System.out.println("Logged in: "+ user.getEmail());
    }
}

EmailLoginService.java

01
02
03
04
05
06
07
08
09
10
packageorg.example.kickoff.business.service;
 
importjavax.enterprise.event.Observes;
importorg.example.kickoff.model.User;
 
publicclassEmailLoginService {
    publicvoidsendLoginAttemptEmail(@Observes@Priority(100) User user) {
        System.out.println(user.getEmail() +" has been used to log into Java EE Observer Design Pattern Example.");
    }
}

With the above priority configuration, the “Logging in…” is printed first followed by “Java EE Observer Design Pattern…”. Try swapping the number so that “Java EE Observer Design Pattern…” is printed first.

6. @Qualifier

So far our observers are listening forUser type events. What if we want to distinguish between different types of users? Theadmin and theuser type? At the moment both methods get invoked when a user logs in. What if we only want to save a login attempt if it’s of typeuser? What if we only want to send an email if the login attempt is of typeadmin? In order for the observers to distinguish between events, we will use aQualifier.

UserEvent.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
packageorg.example.kickoff.business.service;
 
importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;
 
importjavax.inject.Qualifier;
 
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
public@interfaceUserEvent {
    Type value();
    enumType {ADMIN, USER}
}

We will then use theUserEvent annotation to distinguish which observer to invoke like so.

LoginService.java

01
02
03
04
05
06
07
08
09
10
packageorg.example.kickoff.business.service;
 
importjavax.enterprise.event.Observes;
importorg.example.kickoff.model.User;
 
publicclassLoginService {
    publicvoidsaveLoginAttempt(@Observes@Priority(10)@UserEvent(UserEvent.Type.USER) User user) {
        System.out.println("Logged in: "+ user.getEmail());
    }
}

EmailLoginService.java

01
02
03
04
05
06
07
08
09
10
packageorg.example.kickoff.business.service;
 
importjavax.enterprise.event.Observes;
importorg.example.kickoff.model.User;
 
publicclassEmailLoginService {
    publicvoidsendLoginAttemptEmail(@Observes@Priority(100)@UserEvent(UserEvent.Type.ADMIN) User user) {
        System.out.println(user.getEmail() +" has been used to log into Java EE Observer Design Pattern Example.");
    }
}

And then editAuthBacking, adding a new event like so.

AuthBacking.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
packageorg.example.kickoff.view.auth;
 
...snipped...
importorg.example.kickoff.business.service.UserEvent;
...snipped...
 
publicabstractclassAuthBacking {
 
    ...snipped...
     
    @Inject@UserEvent(UserEvent.Type.USER)
    privateEvent<User> userLoginEvent;
         
        @Inject@UserEvent(UserEvent.Type.ADMIN)
    privateEvent<User> adminLoginEvent;
 
    ...snipped...
 
    protectedvoidauthenticate(AuthenticationParameters parameters)throwsIOException {
        AuthenticationStatus status = securityContext.authenticate(getRequest(), getResponse(), parameters);
 
        if(status == SEND_FAILURE) {
            addGlobalError("auth.message.error.failure");
            validationFailed();
        }
        elseif(status == SEND_CONTINUE) {
            responseComplete();// Prevent JSF from rendering a response so authentication mechanism can continue.
        }
        elseif(activeUser.hasGroup(ADMIN)) {
            adminLoginEvent.fire(user);
            redirect("admin/users");
        }
        elseif(activeUser.hasGroup(USER)) {
            userLoginEvent.fire(user);
            redirect("user/profile");
        }
        else{
            redirect("");
        }
    }
 
    ...snipped...
 
}

With the above code, whenever an admin logs in, an email is sent. Whenever a user logs in, it is saved. Our observers can now distinguish which subject has changed its states.

Want to be a Java Master ?
Subscribe to our newsletter and download the JavaDesignPatternsright now!
In order to help you master the Java programming language, we have compiled a kick-ass guide with all the must-know Design Patterns for Java! Besides studying them online you may download the eBook in PDF format!

Thank you!

We will contact you soon.

7. Asynchronous Observer

By default events are synchronous. In CDI 2.0, a new firing method calledfireAsync and a corresponding observer annotationObservesAsync handles asynchronous processing of events. We cannot set a priority because events are observed asynchronously in separate threads. Asynchronous and synchronous observers operate independently from each other. This means the synchronous firing of events are not observed by asynchronous observers and vice versa. We’ll need to changeLoginService, EmailLoginService, andAuthBacking like so.

LoginService.java

01
02
03
04
05
06
07
08
09
10
packageorg.example.kickoff.business.service;
 
importjavax.enterprise.event.ObservesAsync;
importorg.example.kickoff.model.User;
 
publicclassLoginService {
    publicvoidsaveLoginAttempt(@ObservesAsync@UserEvent(UserEvent.Type.USER) User user) {
        System.out.println("Logged in: "+ user.getEmail());
    }
}

EmailLoginService.java

01
02
03
04
05
06
07
08
09
10
packageorg.example.kickoff.business.service;
 
importjavax.enterprise.event.ObservesAsync;
importorg.example.kickoff.model.User;
 
publicclassEmailLoginService {
    publicvoidsendLoginAttemptEmail(@ObservesAsync@UserEvent(UserEvent.Type.ADMIN) User user) {
        System.out.println(user.getEmail() +" has been used to log into Java EE Observer Design Pattern Example.");
    }
}

AuthBacking.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
packageorg.example.kickoff.view.auth;
 
...snipped...
importjava.util.concurrent.CompletionStage;
importorg.example.kickoff.business.service.UserEvent;
...snipped...
 
publicabstractclassAuthBacking {
 
    ...snipped...
     
    @Inject@UserEvent(UserEvent.Type.USER)
    privateEvent<User> userLoginEvent;
         
        @Inject@UserEvent(UserEvent.Type.ADMIN)
    privateEvent<User> adminLoginEvent;
 
    ...snipped...
 
    protectedvoidauthenticate(AuthenticationParameters parameters)throwsIOException {
        AuthenticationStatus status = securityContext.authenticate(getRequest(), getResponse(), parameters);
 
        if(status == SEND_FAILURE) {
            addGlobalError("auth.message.error.failure");
            validationFailed();
        }
        elseif(status == SEND_CONTINUE) {
            responseComplete();// Prevent JSF from rendering a response so authentication mechanism can continue.
        }
        elseif(activeUser.hasGroup(ADMIN)) {
            CompletionStage stage = adminLoginEvent.fireAsync(user);
            stage.handle((User event, Throwable e) -> {
                for(Throwable t : e.getSuppressed()) {
                    System.out.println(t.getMessage());
                }
                returnevent;
            });
            redirect("admin/users");
        }
        elseif(activeUser.hasGroup(USER)) {
            userLoginEvent.fireAsync(user, NotificationOptions.ofExecutor(newForkJoinPool(10)));
            redirect("user/profile");
        }
        else{
            redirect("");
        }
    }
 
    ...snipped...
 
}

We have changed the methodfire tofireAsync. We have added notification options to our firing event and specified a thread pool. OurForkJoinPool allows 10 threads. This means if there are 10 or fewer observers, they will get executed asynchronously. If there are more, the other observers must wait until a thread becomes available.

ThefireAsync the method returns an instance ofCompletionStage. This instance holds a reference to all the exceptions thrown during observer invocation and can be handled in the same way that you would handle a completion state instance.

8. Java EE Observer Design Pattern Summary

That’s all there is to it. We started with a simple implementation of the observer pattern then moved on to use many of the Java EE features. Reaching to the advanced topic of asynchronous observers. Let us now look at the pros and cons of the J2EE Observer Design pattern.

8.1 Pros and cons of the Observer Design Pattern

In Java, the Observer pattern was introduced in Java 1.0. J2EE Observer design helps in development by using annotations and conventions over configuration. Resources also can be injected by type and using annotations@Inject and@Producer. This design pattern helps in startup behavioral characteristics. The developer has control over concurrency and access timeout. This design results in lesser boilerplate code. Any java object can be injected easily. Loosely coupled design and dependency injection can be achieved easily. Business logic is decoupled from the observer through events.

The disadvantage of this pattern is performance issues related to lazy loading. Eager loading creates memory issues. The overuse of this design pattern results in problems. An observer design pattern is suggested to be used in special cases. Type safety is an issue with named annotations. The creation of objects is not transparent. The execution flow is not visible to the developer due to events triggered by other events.

9. Download the Source Code

This is an example about Java EE Observer Design Pattern.

Download
You can download the source code of this example here:Java EE Observer Design Pattern Example

Last updated on Jun. 22nd, 2020

Do you want to know how to develop your skillset to become aJava Rockstar?
Subscribe to our newsletter to start Rockingright now!
To get you started we give you our best selling eBooks forFREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to theTerms andPrivacy Policy

Thank you!

We will contact you soon.

Photo of Joel Patrick LlosaJoel Patrick LlosaJanuary 7th, 2019Last Updated: February 26th, 2021
1 368 8 minutes read
Photo of Joel Patrick Llosa

Joel Patrick Llosa

I graduated from Silliman University in Dumaguete City with a degree in Bachelor of Science in Business Computer Application. I have contributed to many Java related projects at Neural Technologies Ltd., University of Southampton (iSolutions), Predictive Technologies, LLC., Confluence Service, North Concepts, Inc., NEC Telecom Software Philippines, Inc., and NEC Technologies Philippines, Inc. You can also find me in Upwork freelancing as a Java Developer.
Subscribe
Notify of
guest
I agree to theTerms andPrivacy Policy
The comment form collects your name, email and content to allow us keep track of the comments placed on the website. Please read and accept our website Terms and Privacy Policy to post a comment.

I agree to theTerms andPrivacy Policy
The comment form collects your name, email and content to allow us keep track of the comments placed on the website. Please read and accept our website Terms and Privacy Policy to post a comment.