Home » Enterprise Java » Java EE Observer Design Pattern Example

About 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 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.

Java EE Observer Design Pattern Example

1. Introduction

Hi Java Code Geeks fan! This article is about a Java EE Observer Design Pattern Example. The observer pattern is one of the most widely used design pattern in programming. It is implemented in the java.util package of Java SE 8 as Observer and Observable. 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 that Eclipse 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 state of the subject. It relies on the Observes annotation. This annotation marks the observers and the subject uses the Event 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:

Java EE Observer Design Pattern - WildFly 14.x Servers tab

WildFly 14.x Servers tab

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

package org.example.kickoff.view.auth;

...snipped...

public abstract class AuthBacking {

	...snipped...
	
	@Inject
	private Event<User> userLoginEvent;

	...snipped...

	protected void authenticate(AuthenticationParameters parameters) throws IOException {
		AuthenticationStatus status = securityContext.authenticate(getRequest(), getResponse(), parameters);

		if (status == SEND_FAILURE) {
			addGlobalError("auth.message.error.failure");
			validationFailed();
		}
		else if (status == SEND_CONTINUE) {
			responseComplete(); // Prevent JSF from rendering a response so authentication mechanism can continue.
        }
		else if (activeUser.hasGroup(ADMIN)) {
			userLoginEvent.fire(user);
			redirect("admin/users");
		}
		else if (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.

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

Create the observer classes.

LoginService.java

package org.example.kickoff.business.service;

import javax.enterprise.event.Observes;
import org.example.kickoff.model.User;

public class LoginService {
	public void saveLoginAttempt(@Observes User user) {
		System.out.println("Logged in: " + user.getEmail());
	}
}

EmailLoginService.java

package org.example.kickoff.business.service;

import javax.enterprise.event.Observes;
import org.example.kickoff.model.User;

public class EmailLoginService {
	public void sendLoginAttemptEmail(@Observes User 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 notification when a user logs in. When we log in, we should the statements above printed.

Java EE Kickoff App Login Page

Try logging as an admin and then as a user. We should see the output below.

Console Output

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) Logged in: 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, 

That’s the observer pattern basics.

5. @Priority

The order in which observers are invoked is not specified. We can use the Priority 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

package org.example.kickoff.business.service;

import javax.enterprise.event.Observes;
import org.example.kickoff.model.User;

public class LoginService {
	public void saveLoginAttempt(@Observes @Priority(10) User user) {
		System.out.println("Logged in: " + user.getEmail());
	}
}

EmailLoginService.java

package org.example.kickoff.business.service;

import javax.enterprise.event.Observes;
import org.example.kickoff.model.User;

public class EmailLoginService {
	public void sendLoginAttemptEmail(@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 for User type events. What if we want to distinguish between different types of users? The admin and the user 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 type user? What if we only want to send an email if the login attempt is of type admin? In order for the observers to distinguish between events, we will use a Qualifier.

UserEvent.java

package org.example.kickoff.business.service;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.inject.Qualifier;

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
public @interface UserEvent {
	Type value();
	enum Type {ADMIN, USER}
}

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

LoginService.java

package org.example.kickoff.business.service;

import javax.enterprise.event.Observes;
import org.example.kickoff.model.User;

public class LoginService {
	public void saveLoginAttempt(@Observes @Priority(10) @UserEvent(UserEvent.Type.USER) User user) {
		System.out.println("Logged in: " + user.getEmail());
	}
}

EmailLoginService.java

package org.example.kickoff.business.service;

import javax.enterprise.event.Observes;
import org.example.kickoff.model.User;

public class EmailLoginService {
	public void sendLoginAttemptEmail(@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 edit AuthBacking, adding a new event like so.

AuthBacking.java

package org.example.kickoff.view.auth;

...snipped...
import org.example.kickoff.business.service.UserEvent;
...snipped...

public abstract class AuthBacking {

	...snipped...
	
	@Inject @UserEvent(UserEvent.Type.USER)
	private Event<User> userLoginEvent;
        
        @Inject @UserEvent(UserEvent.Type.ADMIN)
	private Event<User> adminLoginEvent;

	...snipped...

	protected void authenticate(AuthenticationParameters parameters) throws IOException {
		AuthenticationStatus status = securityContext.authenticate(getRequest(), getResponse(), parameters);

		if (status == SEND_FAILURE) {
			addGlobalError("auth.message.error.failure");
			validationFailed();
		}
		else if (status == SEND_CONTINUE) {
			responseComplete(); // Prevent JSF from rendering a response so authentication mechanism can continue.
        }
		else if (activeUser.hasGroup(ADMIN)) {
			adminLoginEvent.fire(user);
			redirect("admin/users");
		}
		else if (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 change it’s state.

7. Asynchronous Observer

By default events are synchronous. In CDI 2.0, a new firing method called fireAsync and a corresponding observer annotation ObservesAsync 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 synchronous firing of events are not observed by asynchronous observers and vice versa. We’ll need to change LoginService, EmailLoginService, and AuthBacking like so.

LoginService.java

package org.example.kickoff.business.service;

import javax.enterprise.event.ObservesAsync;
import org.example.kickoff.model.User;

public class LoginService {
	public void saveLoginAttempt(@ObservesAsync @UserEvent(UserEvent.Type.USER) User user) {
		System.out.println("Logged in: " + user.getEmail());
	}
}

EmailLoginService.java

package org.example.kickoff.business.service;

import javax.enterprise.event.ObservesAsync;
import org.example.kickoff.model.User;

public class EmailLoginService {
	public void sendLoginAttemptEmail(@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

package org.example.kickoff.view.auth;

...snipped...
import java.util.concurrent.CompletionStage;
import org.example.kickoff.business.service.UserEvent;
...snipped...

public abstract class AuthBacking {

	...snipped...
	
	@Inject @UserEvent(UserEvent.Type.USER)
	private Event<User> userLoginEvent;
        
        @Inject @UserEvent(UserEvent.Type.ADMIN)
	private Event<User> adminLoginEvent;

	...snipped...

	protected void authenticate(AuthenticationParameters parameters) throws IOException {
		AuthenticationStatus status = securityContext.authenticate(getRequest(), getResponse(), parameters);

		if (status == SEND_FAILURE) {
			addGlobalError("auth.message.error.failure");
			validationFailed();
		}
		else if (status == SEND_CONTINUE) {
			responseComplete(); // Prevent JSF from rendering a response so authentication mechanism can continue.
        }
		else if (activeUser.hasGroup(ADMIN)) {
			CompletionStage stage = adminLoginEvent.fireAsync(user);
			stage.handle((User event, Throwable e) -> {
				for (Throwable t : e.getSuppressed()) {
					System.out.println(t.getMessage());
				}
				return event;
			});
			redirect("admin/users");
		}
		else if (activeUser.hasGroup(USER)) {
			userLoginEvent.fireAsync(user, NotificationOptions.ofExecutor(new ForkJoinPool(10)));
			redirect("user/profile");
		}
		else {
			redirect("");
		}
	}

	...snipped...

}

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

The fireAsync method returns an instance of CompletionStage. 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 Jave EE features. Reaching to the advanced topic of asynchronous observers.

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-pattern.zip
(+4 rating, 4 votes)
1 Comment Views Tweet it!

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you our best selling eBooks for FREE!

 

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 ....

 

Receive Java & Developer job alerts in your Area

 

1
Leave a Reply

avatar
1 Comment threads
0 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
1 Comment authors
Terthan Markov Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
Terthan Markov
Guest
Terthan Markov