Reputation: 797
I have an JSF 2.2 application using Wildfly 10 as server.
Users are logged in and added to a list of online users:
HttpSession session = request.getSession();
funcionario.setSessao(session);
funcionario.setIp(request.getRemoteAddr());
session.setAttribute("usuarioLogado", funcionario);
I remove users that logout from online users list.
public class ActiveUserListener implements HttpSessionAttributeListene {
@Inject
Fixo fixo;
@Override
public void attributeAdded(HttpSessionBindingEvent event) {
if (event.getValue() instanceof Funcionario) {
fixo.getLogins().add((Funcionario) event.getValue());
}
}
@Override
public void attributeRemoved(HttpSessionBindingEvent event) {
if (event.getValue() instanceof Funcionario) {
if (event.getValue() instanceof Funcionario) {
fixo.getLogins().remove((Funcionario) event.getValue());
}
}
}
@Override
public void attributeReplaced(HttpSessionBindingEvent event) {
if (event.getValue() instanceof Funcionario) {
fixo.getLogins().add((Funcionario) event.getValue());
}
} }
public class SessionCounter implements HttpSessionListener {
@Inject
Fixo fixo;
public void sessionDestroyed(HttpSessionEvent se) {
if (se.getSession().getAttribute("usuarioLogado") != null) {
Funcionario f = fixo.getLogins().stream().filter(o -> o.getSessaoId().equals(se.getSession().getId()))
.findAny().get();
fixo.getLogins().remove(f);
}
}
@Override
public void sessionCreated(HttpSessionEvent arg0) {
// TODO Auto-generated method stub
}}´
And when I try to print some user/session details:
public void usuConsole() {
System.out.println("nº " + fixo.getLogins().size());
for (Funcionario f : fixo.getLogins()) {
System.out.println(f.getMatricula());
if (f.getSessao() != null) {
System.out.println(f.getSessao().getId());
System.out.println(f.getSessao().getCreationTime());
System.out.println(f.getSessao().getLastAccessedTime());
}
}
}
Print some lines and at some point:
Session is invalid R5EB6hKAzanm50PSRYqxcv361UMD6nGjZWxJVc5P
If few users login it is ok, but after a lot of users login and logout, the error appears
Upvotes: 0
Views: 1715
Reputation: 26512
Option 1:
Try to use java.util.concurrent.CopyOnWriteArrayList implementation for your logins collection in the Fixo class.
Option 2
Synchronize the logins collection manually:
a) Make the collection final (good practice when using an object for synchronizing.
b) Synchronize the logins collection inserts and removals in the event listener methods:
synchronized(fixo.getLogins()){
fixo.getLogins().remove((Funcionario) event.getValue());
}
synchronized(fixo.getLogins()){
fixo.getLogins().add((Funcionario) event.getValue());
}
c) Synchronize the for each loop:
synchronized(fixo.getLogins()){
for (Funcionario f : fixo.getLogins()) {
System.out.println(f.getMatricula());
if (f.getSessao() != null) {
System.out.println(f.getSessao().getId());
System.out.println(f.getSessao().getCreationTime());
System.out.println(f.getSessao().getLastAccessedTime());
}
}
}
Now while in any of those synchronized blocks, any public method calls to the logins collection will be blocked until the application get out of the block. This means that while the for each loop is running, any modifications of the collection in the event listener methods will be held until the looping is over. According to the listener spec, the session will not be invalidated until the sessionDestroyed method has finished processing.
Thats at least the theory, try it out and lets see how it went.
Upvotes: 1