How it works...

First, let's understand our bean declaration:

@Singleton
@Startup
public class UserCacheBean {

...

@PostConstruct
protected void init() {
cache = new ConcurrentLinkedQueue<>();
loadCache();
}
}

We are using a Singleton because it has one and only one instance in the application context. This is the way we want a data cache to be we don't want different data being shared.

Also, note that we used the @Startup annotation. This tells the server that this bean should be executed once it is loaded and that the method annotated with @PostConstruct is used for it.

So, we use the startup time to load our cache:

protected void loadCache() {
List<User> list = em.createQuery("SELECT u FROM USER
as u").getResultList();

list.forEach((user) -> {
cache.add(user);
});
}

Now, let's check the object holding our cache:

protected Queue<User> cache = null;

...

cache = new ConcurrentLinkedQueue<>();

ConcurrentLinkedQueue is a list that's built with one main purpose to be accessed by multiple processes in a thread-safe environment. That's exactly what we need. This also offers great performance when we need to access its members.

Finally, let's check the access to our data cache:

Lock(LockType.READ)
public List<User> get() {
return cache.stream().collect(Collectors.toList());
}

We annotated the get() method with LockType.READ, which is telling the concurrency manager that it can be accessed by multiple processes at once in a thread-safe way.