how to calculate online user count from HttpSessionEventPublisher - spring

i want to calculate user online or active sessions by HttpSessionEventPublisher my web.xml file as bellow :
<listener>
<listener-class>my.web.application.SessionManager</listener-class>
</listener>
and my class SessionManager is:
import org.springframework.security.web.session.HttpSessionEventPublisher;
import javax.servlet.http.HttpSessionEvent;
public class SessionManager extends HttpSessionEventPublisher {
private static int userCount;
#Override
public void sessionCreated(HttpSessionEvent event) {
userCount++;
super.sessionCreated(event);
}
#Override
public void sessionDestroyed(HttpSessionEvent event) {
userCount--;
super.sessionDestroyed(event);
}
public static int getUserCount() {
return userCount;
}
}
when start application 4 session creating , but should be 1 create session, how to solve my problem?
thanks

Related

Get session events from Hazelcast session repository?

I'm not getting session closed or expired events when using embedded Hazelcast session repository in Spring boot application. I do get session creation events. I have a very short timeout for the sessions (30s). I have verified that the session gets expired after 30s by getting "unauthorized" reply from the server. How do you receive session expiration/destruction events?
This is my session configuration:
#Configuration
#EnableHazelcastHttpSession(maxInactiveIntervalInSeconds = 30)
public class SessionConfiguration{
#Bean
#SpringSessionHazelcastInstance
public HazelcastInstance hazelcastInstance() {
Config config = new Config();
config.setClusterName("spring-session-cluster");
// Add this attribute to be able to query sessions by their PRINCIPAL_NAME_ATTRIBUTE's
AttributeConfig attributeConfig = new AttributeConfig()
.setName(Hazelcast4IndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
.setExtractorClassName(Hazelcast4PrincipalNameExtractor.class.getName());
// Configure the sessions map
config.getMapConfig(Hazelcast4IndexedSessionRepository.DEFAULT_SESSION_MAP_NAME)
.addAttributeConfig(attributeConfig).addIndexConfig(
new IndexConfig(IndexType.HASH, Hazelcast4IndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE));
// Use use custom serializer to de/serialize sessions faster. This is optional.
SerializerConfig serializerConfig = new SerializerConfig();
serializerConfig.setImplementation(new HazelcastSessionSerializer()).setTypeClass(MapSession.class);
config.getSerializationConfig().addSerializerConfig(serializerConfig);
return Hazelcast.newHazelcastInstance(config);
}
#Bean
public SessionRepositoryCustomizer<Hazelcast4IndexedSessionRepository> customize() {
return (sessionRepository) -> {
sessionRepository.setFlushMode(FlushMode.IMMEDIATE);
sessionRepository.setSaveMode(SaveMode.ALWAYS);
sessionRepository.setSessionMapName(Hazelcast4IndexedSessionRepository.DEFAULT_SESSION_MAP_NAME);
sessionRepository.setDefaultMaxInactiveInterval(30); //this is extra; tried with and without
};
}
}
And this is my listener:
#Component
public class SessionListener {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SessionListener.class);
#EventListener
public void sessionCreated(SessionCreatedEvent event) {
log.info("SESSION:CREATE:ID="+event.getSessionId()); //only this gets called but none of the others
}
#EventListener
public void sessionDeleted(SessionDeletedEvent event) {
log.info("SESSION:DELETE:ID="+event.getSessionId());
}
#EventListener
public void sessionDestroyed(SessionDestroyedEvent event) {
log.info("SESSION:DESTROY:ID="+event.getId());
}
#EventListener
public void sessionExpired(SessionExpiredEvent event) {
log.info("SESSION:EXPIRE:ID="+event.getSessionId());
}
}
Partial answer (I don't know exactly why it works):
If you add a session map listener to HazelcastInstance (in the creating bean) you suddenly start receiving SessionExpiredEvents.
So replace lines:
config.getMapConfig(Hazelcast4IndexedSessionRepository.DEFAULT_SESSION_MAP_NAME)
.addAttributeConfig(attributeConfig).addIndexConfig(
new IndexConfig(IndexType.HASH, Hazelcast4IndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE));
with (set maximum idle seconds to the session map configuration):
config.getMapConfig(Hazelcast4IndexedSessionRepository.DEFAULT_SESSION_MAP_NAME)
.addAttributeConfig(attributeConfig).addIndexConfig(
new IndexConfig(IndexType.HASH, Hazelcast4IndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE))
.setMaxIdleSeconds(tout);
and
return Hazelcast.newHazelcastInstance(config);
with (add session entry listener to the session map)
HazelcastInstance instance = Hazelcast.newHazelcastInstance(config);
IMap<Object, Object> map = instance.getMap( Hazelcast4IndexedSessionRepository.DEFAULT_SESSION_MAP_NAME );
map.addEntryListener( new HazelcastSessionEntryListener(), true );
return instance;
where HazelcastSessionEntryListener can be defined like this:
#Component
public class HazelcastSessionEntryListener implements EntryListener<Object, Object>
{
public HazelcastSessionEntryListener(){}
#Override
public void entryAdded(EntryEvent<Object, Object> event){}
#Override
public void entryUpdated(EntryEvent<Object, Object> event){}
#Override
public void entryRemoved(EntryEvent<Object, Object> event){}
#Override
public void entryEvicted(EntryEvent<Object, Object> event){}
#Override
public void entryExpired(EntryEvent<Object, Object> event){}
#Override
public void mapCleared(MapEvent event){}
#Override
public void mapEvicted(MapEvent event){}
}
Funny thing is that HazelcastSessionEntryListener is just an empty implementation in my case (it doesn't do anything). Seems like buggy behaviour (but I'm not a Spring expert).

spring-session Session implementation not getting CGLIB Enhanced Proxy Bean

I'm attempting to drop spring-session into an existing web application that uses session scoped beans. This application is running on Wildfly 10.1.0.Final
When I run the application uses wildfly's internal session management, everything works fine. Attempting to put in a custom implementation of Spring-Session is losing the session bean information. When I debug the application, there is a call for scopedTarget.userSessionData, however it is of the underlying class UserSessionData, and not the UserSessionData$$EnhancedBySpringCGLIB$$1234 that has the actual data.
configuration-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="userSessionData" scope="session" class="UserSessionData">
<aop:scoped-proxy/>
</bean>
<context:annotation-config />
<bean class="my.MyHttpSessionConfiguration" />
</beans>
This configuration properly creates and registers a springSessionRepositoryFilter using MyHttpSessionConfiguration. However the problem is when MySession object has a session scoped bean of UserSessionData, I get the raw proxied class instance and not the decorated one which has the session values. I was able to verify this by debugging the below SesssionImpl on the #setAttribute method
class UserSessionData {
private String _myValue;
public void setMyValue(String value) {
_myValue = value;
}
public String getMyValue() {
return _myValue;
}
}
final class MySession implements ExpiringSession {
private final MapSession _delegate;
private final Map<String, Object> _delta = new HashMap<>();
private boolean _isNew;
MySession() {
this(new MapSession());
_isNew = true;
flushImmediateIfNecessary();
}
MySession(MapSession session) {
if (session == null) {
throw new IllegalArgumentException("MapSession cannot be null");
}
_isNew = false;
_delegate = session;
}
public boolean isNew() {
return _isNew;
}
#Override
public String getId() {
return _delegate.getId();
}
#Override
public long getCreationTime() {
return _delegate.getCreationTime();
}
#Override
public long getLastAccessedTime() {
return _delegate.getLastAccessedTime();
}
#Override
public void setLastAccessedTime(long lastAccessedTime) {
_delegate.setLastAccessedTime(lastAccessedTime);
flushImmediateIfNecessary();
}
#Override
public void setMaxInactiveIntervalInSeconds(int interval) {
_delegate.setMaxInactiveIntervalInSeconds(interval);
flushImmediateIfNecessary();
}
#Override
public int getMaxInactiveIntervalInSeconds() {
return _delegate.getMaxInactiveIntervalInSeconds();
}
#Override
public boolean isExpired() {
return _delegate.isExpired();
}
#SuppressWarnings("unchecked")
#Override
public <T> T getAttribute(String attributeName) {
return (T) _delegate.getAttribute(attributeName);
}
#Override
public Set<String> getAttributeNames() {
return _delegate.getAttributeNames();
}
#Override
public void setAttribute(String attributeName, Object attributeValue) {
_delegate.setAttribute(attributeName, attributeValue);
putAndFlush(attributeName, attributeValue);
}
#Override
public void removeAttribute(String attributeName) {
_delegate.removeAttribute(attributeName);
putAndFlush(attributeName, null);
}
Map<String, Object> getDelta() {
return _delta;
}
private void putAndFlush(String attr, Object value) {
_delta.put(attr, value);
flushImmediateIfNecessary();
}
private void flushImmediateIfNecessary() {
if (MySessionRepository.this._flushMode == MyFlushMode.IMMEDIATE) {
MySessionRepository.this.save(this);
}
}
}
I feel like I'm missing either a configuration or something because the native WildFly SessionImpl has the correct Enhanced object for serialization when I remove the springSessionRepositoryFilter and let the container handle the session object. Additionally the Spring Controllers have the correct enhanced object. It's just MySession that does not appear to be getting it. Any thoughts?
EDIT:
Looks like the core issue is that my spring-security is using a different version of the proxy class when setting members, so I am not getting them in the MySession object. Attached are the configurations for Spring-Security
public class WebSessionInitializer extends AbstractHttpSessionInitializer {
}
#Configuration
#EnableSpringHttpSession
public class MySessionConfiguration {
#Bean
public SessionRepository sessionRepository() {
return new MySessionRepository();
}
}
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="imb"
version="2.5">
<distributable />
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/configuration-spring.xml
</param-value>
</context-param>
The SpringSession filter is correctly added with my implementation. It appears the root problem is when I get into spring-security, it is setting values on a different instance of UserSessionData than the one in the MySession object. When I use #Autowired in MVC Controllers, I get the correct one.
Library Versions:
Spring: 3.2.14
Spring Session: 1.3.2
WildFly 10.1.0.Final

Spring Autowiring fails

I have added one Listener in web.xml
<listener>
<listener-class>*.LoadCacheServlet</listener-class>
</listener>
This is servlet class
#Component
public class LoadCacheServlet implements ServletContextListener {
#Autowired
private ConnectorRepository connectorRepository;
#Override
public void contextDestroyed(ServletContextEvent arg0) {
LOGGER.info("ServletContextListener destroyed----------------------------");
}
// Run this before web application is started
#Override
public void contextInitialized(ServletContextEvent arg0) {
LOGGER.info("ServletContextListener startedddd---------------------------------");
connectorRepository.callMe("xys");
}
}
On tomcat startup. contextInitialized method gets exceuted but connectorRepository is not initailized. ConnectorRepository is an interface and in other parts of application its gets autowired by SpringJPA module. How to initialize it in servlet.

Session-timeout in web.xml not working

I am new to JSF and working on session-timeout and based on it redirecting to a logout page. But it is not working properly and showing unusual behavior like, some times it's getting redirected and some times it's not...
Code for web.xml:
<context-param>
<param-name>web.TIME_OUT_PAGE</param-name>
<param-value>/sessionTimeOut.html</param-value>
</context-param>
<listener>
<display-name>SessionTimeoutNotifier</display-name>
<listener-class>test.web.SessionTimeoutNotifier</listener-class>
</listener>
<listener>
<display-name>ViewExpirationListener</display-name>
<listener-class>test.web.ViewExpirationListener</listener-class>
</listener>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
and code for SessionTimeoutNotifier.java:
package test.web;
import java.io.Serializable;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import org.apache.commons.lang.exception.ExceptionUtils;
import test.User;
public class SessionTimeoutNotifier implements HttpSessionBindingListener,Serializable
{
private static final long serialVersionUID = 8420390506024648336L;
public SessionTimeoutNotifier(){
}
public void valueBound(HttpSessionBindingEvent event)
{
User user = (User) event.getSession().getAttribute(User.USER);
System.out.println("Session Max Inactive Interval Value:"+ event.getSession().getMaxInactiveInterval());
if (user != null) {
System.out.println("Session ID:"+ event.getSession().getId() + " created for user ID: " + user.getId());
} else {
System.out.println("Session ID:"+ event.getSession().getId() + " created for user ID: UNKNOWN");
}
}
public void valueUnbound(HttpSessionBindingEvent event)
{
User user = (User) event.getSession().getAttribute(User.USER);
System.out.println("Session expired : [" + (user == null ? "Unknown" : user.getId()) + "]");
}
}
and ViewExpirationListener.java Class:
package test.web;
import java.io.IOException;
import java.util.List;
import javax.faces.FacesException;
import javax.faces.application.FacesMessage;
import javax.faces.application.ViewExpiredException;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
public class ViewExpirationListener implements PhaseListener {
private static final String TIME_OUT_PAGE_PARAM = "web.TIME_OUT_PAGE";
#Override
public PhaseId getPhaseId() {
return PhaseId.APPLY_REQUEST_VALUES;
}
#Override
public void afterPhase(PhaseEvent event) {
// Do nothing.
}
#Override
public void beforePhase(PhaseEvent event) {
FacesContext facesContext = event.getFacesContext();
List<FacesMessage> iter = facesContext
.getMessageList(ViewExpiredException.class.getName());
List<FacesMessage> msgs = facesContext.getMessageList();
int count = 1;
if (iter.size() > 0) {
handleTimeOut(facesContext);
}
}
private void handleTimeOut(FacesContext facesContext) {
ExternalContext extContext = facesContext.getExternalContext();
String timeOutPage = extContext.getRequestContextPath()
+ extContext.getInitParameter(TIME_OUT_PAGE_PARAM);
try {
extContext.redirect(timeOutPage);
} catch (IOException e) {
throw new FacesException(e);
}
facesContext.responseComplete();
}
}
and still it is not redirecting every-time after inactivity of 30 min. Some times timeout works even at 31st minute and some times session is still active even after 5 hours....
I am not able to understand where I am wrong and what can be done to resolve it..
Thanks in advance for help...

Getting notification when bounded/unbounded to a HTTP session

How can i get notified when my Object gets bounded/unbounded to a session object of HTTP.
Let the object's class implement HttpSessionBindingListener.
public class YourObject implements HttpSessionBindingListener {
#Override
public void valueBound(HttpSessionBindingEvent event) {
// The current instance has been bound to the HttpSession.
}
#Override
public void valueUnbound(HttpSessionBindingEvent event) {
// The current instance has been unbound from the HttpSession.
}
}
If you have no control over the object's class code and thus you can't change its code, then an alternative is to implement HttpSessionAttributeListener.
#WebListener
public class YourObjectSessionAttributeListener implements HttpSessionAttributeListener {
#Override
public void attributeAdded(HttpSessionBindingEvent event) {
if (event.getValue() instanceof YourObject) {
// An instance of YourObject has been bound to the session.
}
}
#Override
public void attributeRemoved(HttpSessionBindingEvent event) {
if (event.getValue() instanceof YourObject) {
// An instance of YourObject has been unbound from the session.
}
}
#Override
public void attributeReplaced(HttpSessionBindingEvent event) {
if (event.getValue() instanceof YourObject) {
// An instance of YourObject has been replaced in the session.
}
}
}
Note: when you're still on Servlet 2.5 or older, replace #WebListener by a <listener> configuration entry in web.xml.

Resources